Cordova App集成第三方SDK

第一步,先测试SDK包打包安装后demo能否能正常在ios和android设备上。

IOS 

获取证书标识的方法

两个同名的证书,重签名时会有提示

“./AppResignTool [证书名称或标识] [主应用的 BundleID] [插件的描述文件名称]

 

为什么要重签名?

因为IOS系统的限制,有的第三方插件SDK, 需要重签名才能使用 

 比如这个 NetAccessProvider.appex

 

 

 

 

重签名成功 

 

 

钥匙串删除Apple developer 证书后,自动替换为iPhone Developer证书

 

第二步,

思路1 把对方的sdk封装成一个插件

 学习最好的方法就是借鉴, 1.可以借鉴公司以前的项目封装 2. IOS没人封装过, 那么就可以查看第三方的插件是如何封装和调用Object-C 方法、如何在里面传参的 比如 cordova-plugin-badge 插件, 可以在Android和IOS上使用, 里面就有他的调用方法

思路2 IOS: 把我们的cordova工程嵌入他们的Object-C 原生项目中

 

首先,先学习自创插件 

 

Android

1.创建cordova项目

cordova create demo xxx.xxx.xxx

demo --> 工程名 —— xxx.xxx.xxx --> 包名(config.xml文件内的id)

 

2.添加Android平台

 3.安装plugman插件

yarn global add plugman                   

创建一个插件: 

plugman create --name hnbhyoa-vpn --plugin_id cordova.hnbhyoa.vpn --plugin_version 1.0.0

创建插件的话 建议你不要创建在cordova项目中
把他放入专门放插件的目录

4.编写生成插件中的安卓代码-->当然也可以使用命令进行生产操作:(此命令在插件根目录下执行)  

plugman platform add --platform_name android/ios

5.修改plugin.xml

<?xml version='1.0' encoding='utf-8'?>
<plugin id="cordova.hnbhyoa.vpn" version="1.0.0" xmlns="http://apache.org/cordova/ns/plugins/1.0"
        xmlns:android="http://schemas.android.com/apk/res/android">
    <name>hnbhyoa-vpn</name>
    <!--  cordova-hnbhyoa-vpn.js 文件将被安装到plugins/cordova-hnbhyoa-vpn/www/文件夹下  -->
    <js-module name="cordova-hnbhyoa-vpn" src="www/hnbhyoa-vpn.js">
        <!--  clobbers 元素是 js-module元素内标记,用于指定 module.exports 被插入在 window 对象的命名空间  -->
        <clobbers target="cordova.plugins.vpn"/>
    </js-module>
    <!-- Android平台 -->
    <platform name="android">
    <!--  target 指定文件被复制到相对于 cordova 项目根路径的路径这里复制到了 platforms/android/app/src/main/res/xml  -->
        <config-file parent="/*" target="res/xml/config.xml">
    <!--  feature代表此插件提供的一个功能模块,name是此功能模块的命名  -->
            <feature name="HnbhyoaVpn">
                <!--  android-package是此功能模块对应的android实现  value的值是对应的插件中HnbhyoaVpn.java存放的路径
                这里安装到了app/java/cordova.hnbhyoa.vpn文件夹下 ,如果这里的value改为HnbhyVpn,那么android下的.java文件也要改名为HnbhyVpn.java     -->
                <param name="android-package" value="cordova.hnbhyoa.vpn.HnbhyoaVpn"/>
            </feature>
        </config-file>
        <!--  target 指定文件被复制到相对于 cordova 项目根路径的路径。如果指定的文件不存在,就会忽略配置变化,并继续安装  -->
        <config-file parent="/*" target="AndroidManifest.xml"></config-file>
        <!-- src 相对于 plugin.xml 文件的位置, target-dir:复制文件到该指定的相对于 cordova 项目根目录的目录中,要和上面的value路径对应 -->
        <!--  这里安装到了 platforms/android/app/src/main/java/cordova/hnbhyoa/vpn/HnbhyoaVpn.java-->
        <source-file src="src/android/HnbhyoaVpn.java" target-dir="src/cordova/hnbhyoa/vpn"/>
    </platform>
  <platform name="ios">
   <config-file parent="/*" target="config.xml">
   <feature name="HnbhyoaVpnIOS">
   <param name="ios-package" value="cordova.hnbhyoa.vpn.HnbhyoaVpnIOS"/>
  </feature>
   </config-file>
  <header-file src="src/ios/hnbhyoa-vpn.h"/>
  <source-file src="src/ios/hnbhyoa-vpn.m"/>
  </platform>
</plugin>

修改www/文件夹下的 js文件

var exec = require('cordova/exec');

// hnbhyoa-vpn: 是plugin.xml文件中的feature标签 name属性
// show:是js中调用的方法名
// [arg0]: 表示一个参数,[arg0,arg1]:表示两个参数
// Android
exports.show = function (arg0, success, error) { exec(success, error, 'HnbhyoaVpn', 'show', [arg0]); };

// IOS
exports.doTest = function (args, success, error) {
  exex(success, error, 'HnbhyoaVpnIOS', 'doTest', [args]);
};

修改src/android/下的 java文件

package cordova.hnbhyoa.vpn;

import android.widget.Toast;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaArgs;

import org.json.JSONException;

/**
 * This class echoes a string called from JavaScript.
 */

public class HnbhyoaVpn extends CordovaPlugin {

    @Override
    public boolean execute(String action, CordovaArgs args, CallbackContext callbackContext) throws JSONException {
        if ("show".equals(action)){
            // 获取activity和context --> cordova.getActivity()和cordova.getContext()
            Toast.makeText(cordova.getContext(),args.getString(0),Toast.LENGTH_SHORT).show();
            return true;
        }
        return false;
    }
}

 修改src/ios/下的 .m和.h文件

HnbhyoaVpnIOS.m

#import "HnbhyoaVpnIOS.h"

@implementation HnbhyoaVpnIOS

- (void)doTest:(CDVInvokedUrlCommand *)command {

    NSLog(@"插件被调起了");


}
@end

HnbhyoaVpnIOS.h

#import <Cordova/CDVPlugin.h>

@interface HnbhyoaVpnIOS : CDVPlugin

- (void)doTest:(CDVInvokedUrlCommand *)command;

@end

 

 

 

 

6.完成plugin.xml修改后,我们进入插件目录,执行 npm init -y

 

 

 7.添加插件到cordova项目中,   cordova plugin add  [插件路径]               

  ` 移除是插件的包名`    cordova plugin remove [包名]                     

 

 

 

 

 

 

8.使用js调用show和doTest方法方法

document.getElementById('toast').onclick = function(){
    cordova.plugins.vpn.show('777')
}
document.getElementById('test').onclick = function(){
    cordova.plugins.vpn.doTest();
}
 

 

9.注意事项:

index.html中可能会出现button事件无法生效,把<head>中第一行替换成下面

<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *;script-src * 'unsafe-inline'" 

 

由于您无法修改cordovas .gradle文件,因此必须添加自己的文件并在plugin.xml中引用它,您可以像这样进行操作:

<framework src="src/android/*.gradle" custom="true" type="gradleReference" />

这将允许您执行诸如编译外部模块之类的事情.为了使它真正起作用,您将不得不在要集成的项目之外创建一个.aar库.

产生的gradle-extension看起来像这样:

repositories {    
    jcenter()
    flatDir {
        dirs 'libs'
    }
}

dependencies {
    compile(name:'KTplay', ext:'aar')
}

android {
    packagingOptions {
        exclude 'META-INF/NOTICE'
        exclude 'META-INF/LICENSE'
    }
}

这假定您已将.aar库放在名为libs的插件的子目录中.剩下要做的就是确保在构建过程中确实复制了库,这就是为什么我们必须将其作为资源文件添加到plugin.xml:

中的原因

<resource-file src="libs/KTplay.aar" target="libs/KTplay.aar" />

其他:

hook 自定义脚本 

<hook type="after_plugin_install" src="scripts/afterPluginInstall.js" />
<hook type="before_plugin_uninstall" src="scripts/beforePluginUninstall.js" />

安装后文件  afterPluginInstall.js

const path = require('path');
const fs = require('fs-extra');
const figlet = require('figlet');

const outputChar = (str) => {
    return new Promise((resolve) => {
        figlet(str, function (err, data) {
            if (err) {
                return resolve(str);
            }
            return resolve(data);
        });
    });
};

module.exports = async function (ctx) {
    const sourceLibs = path.join(ctx.opts.plugin.dir, './src/libs');
    const targetLibs = path.join(ctx.opts.projectRoot, './platforms/android/app/libs');
    await fs.copy(sourceLibs, targetLibs);
    // await fs.copy(sourceJNI, targetJNI);
    const chars = await outputChar('ADD SUCCESS');
    console.log(chars);
    console.log('^_^ vpn plugin add success!');
};

卸载前文件  beforePluginUninstall.js

const path = require('path');
const fs = require('fs-extra');
const figlet = require('figlet');

const outputChar = (str) => {
    return new Promise((resolve) => {
        figlet(str, function(err, data) {
            if (err) {
                return resolve(str);
            }
            return resolve(data);
        });
    });
};

module.exports = async function(ctx) {
    const targetLibs = path.join(ctx.opts.projectRoot, './platforms/android/app/libs');
    await fs.remove(targetLibs);
    const chars = await outputChar('REMOVE SUCCESS');
    console.log(chars);
    console.log('^_^ remove londen plugin SDK');
};


 

现在,开始正题

 这是第三方给的SDK文件夹,可以直接用android studio启动

 

第一步、framework引用aar包

1.写入plugin.xml文件中

<framework src="src/vpn.gradle" custom="true" type="gradleReference" />

2.包vpn.gradle文件放到src文件下 

repositories {
    jcenter()
    flatDir {
        dirs 'libs'
    }
}
dependencies {
    // implementation files('libs/TopSecVPN.aar')
    compile(name:'TopSecVPN', ext:'aar')
}
android {    
    packagingOptions {
        exclude 'META-INF/NOTICE'
        exclude 'META-INF/LICENSE'
    }
}

3.把第三方提供的aar文件放入文件夹中

第二步、把第三方的SDK demo.java文件放入自定义插件文件夹中

 

 

 放到这里来(记得要修改包名为插件id cordova.hnbhyoa.vpn)

 

 

 

 第三步、找到主页面(MainActivity.java)文件

 分析文件内容

 

 

第四步、构建新文件

可以参考show方法的java文件

public class HnbhyoaVpn extends CordovaPlugin {

    @Override
    public boolean execute(String action, CordovaArgs args, CallbackContext callbackContext) throws JSONException {
        if ("show".equals(action)){
            // 获取activity和context --> cordova.getActivity()和cordova.getContext()
            Toast.makeText(cordova.getContext(),args.getString(0),Toast.LENGTH_SHORT).show();
            return true;
        }
        return false;
    }
}

需要了解的知识:

1.cordova 调用原生上下文

原生: getApplicationContext()

cordova: cordova.getActivity().getApplicationContext()

2. e.printStackTrace() 方法  在命令行打印异常信息在程序中出错的位置及原因。

3. 报错提示 callbackContext.error(e.toString());

 

 

4.@Override 重写方法 类似注释代码

 

开始修改

添加常用API

import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CordovaArgs;
import org.json.JSONArray;
import org.json.JSONException;

修改他们的Class为Cordova的类

public class MainActivity extends Activity implements OnAcceptSysLogListener,OnAcceptResultListener,OnClickListener {
}

改为

public class HnbhyVpn extends CordovaPlugin implements OnAcceptSysLogListener,OnAcceptResultListener{ // 这里移除了OnClickListener抽象方法,因为没有用到

}

先添加Cordova的方法

 public boolean execute(String action, CordovaArgs args, CallbackContext callbackContext) throws JSONException {
        switch (action) {
            case "init":
                this.init(callbackContext);
                return true;
        }
        return false;
 }

对应js文件调用

var exec = require('cordova/exec');
exports.init = function (success, error) {
    exec(success, error, 'HnbhyVpn', 'init');
};

找到他们的初始化方法

private void Initialize() {
        try{
            /*
             * 在Android6.0+请在初始化SDK前申请好电话、存储等权限,确保SDK能够正常初始化
             */
            TopVPNSdkDemo.m_ihVPNService=VPNService.getVPNInstance(this.getApplicationContext());
        }
        catch(Exception ex)
        {
            Log.i("TopVPNSdkDemo", ex.toString());
        }
        if(null==TopVPNSdkDemo.m_ihVPNService){
            if (Build.VERSION.SDK_INT>22){
                Toast.makeText(this,"VPN实例初始化失败!可能是权限问题导致。如果是,请先到'设置-》应用管理'页面找到本程序,并在其‘应用权限’中授权电话、存储和网络等权限然后再试(本示例程序未加入动态权限申请的功能);如果不是,请看logcat日志输出。",Toast.LENGTH_LONG).show();
            }
            else{
                Toast.makeText(getApplicationContext(), "VPN实例初始化失败!具体请看logcat日志输出。", Toast.LENGTH_LONG).show();
            }
            return ;
        }

        TopVPNSdkDemo.m_ihVPNService.setOnAcceptResultListener(this);
        TopVPNSdkDemo.m_ihVPNService.setOnAcceptSysLogListener(this);
        ((EditText)findViewById(R.id.edtVPNAddr)).setOnFocusChangeListener(new OnFocusChangeListener(){

            @SuppressLint("ShowToast")
            @Override
            public void onFocusChange(View arg0, boolean arg1)
            {
                if(!arg1)
                {
                    String strAddr = ((EditText)arg0).getText().toString().trim();
                    DoConfigurationVPN(strAddr);
                }
            }
        });
    }

改为

private void init(CallbackContext callbackContext) {
    try {
        /*
         * 在Android6.0+请在初始化SDK前申请好电话、存储等权限,确保SDK能够正常初始化
         */
        TopVPNSdkDemo.m_ihVPNService = VPNService.getVPNInstance(cordova.getActivity().getApplicationContext());
    } catch (Exception e) {
        e.printStackTrace();
    }
    if (null == TopVPNSdkDemo.m_ihVPNService) {
        if (Build.VERSION.SDK_INT > 22) {
            callbackContext.error("VPN实例初始化失败2");
            Toast.makeText(cordova.getActivity().getApplicationContext(), "VPN实例初始化失败2222。", Toast.LENGTH_LONG).show();
        } else {
            Toast.makeText(cordova.getActivity().getApplicationContext(), "VPN实例初始化失败!具体请看logcat日志输出。", Toast.LENGTH_LONG).show();
        }
        return;
    }

    TopVPNSdkDemo.m_ihVPNService.setOnAcceptResultListener(this);
    TopVPNSdkDemo.m_ihVPNService.setOnAcceptSysLogListener(this);
}

尝试启动

 

这里直接使用会出现报错 说明需要重写方法

根据错误提示,找到抽象类新增的方法,在报错的类中进行重写,满足语法要求即可;

如果当前类未用到,返回值可以为nulll。

因为用的是第三方SDK, 所以我们直接把对方的抽象方法拿过来修改即可

 

 

注释掉一些报错的地方(看命名是用不到的地方)

 

最终java文件

 

 

第五步、修改www/下的js文件(完整代码  示例只用了init方法)

var exec = require('cordova/exec');

exports.init = function (success, error) {
    exec(success, error, 'HnbhyoaVpn', 'init');
};
exports.login = function (args, success, error) {
    exec(success, error, 'HnbhyoaVpn', 'login', [args]);
};
exports.logout = function (success, error) {
    exec(success, error, 'HnbhyoaVpn', 'logout');
};
exports.getResource = function (success, error) {
    exec(success, error, 'HnbhyoaVpn', 'getResource');
};
exports.startService = function (success, error) {
    exec(success, error, 'HnbhyoaVpn', 'startService');
};
exports.closeService = function (success, error) {
    exec(success, error, 'HnbhyoaVpn', 'closeService');
};
exports.doConfigurationVPN = function (args, success, error) {
    exec(success, error, 'HnbhyoaVpn', 'doConfigurationVPN', [args]);
};

 

最后、js调用

document.getElementById('toast').onclick = function(){
        cordova.plugins.vpn.init(function (data) {
            alert(data)
        }, function (message) {
            alert(message);
        });
} 

 集成成功

 

遇到的坑:

千万不要嵌套Promise, 会导致获取不到失败回调, 获取的全是成功的, 应该要拆开来写

 2. 监听用户选择的监听事件没有生效

.

 

 

 

 



posted @ 2021-03-31 15:54  一路向北√  阅读(1279)  评论(0编辑  收藏  举报

web应用开发&研究 -

业精于勤而荒于嬉。

工作,使我快乐。


Font Awesome | Respond.js | Bootstrap中文网