1.前言
最近一个项目要求我们的产品必须走网络隧道,并且提供了对应的SDK,很明显只能通过原生开发的方式才能实现这个流程,之前已经写过一篇通过代理的方式进行数据请求,而这次Android端的方式是采用NBA的方式进行数据请求,下面开始进行原生插件的开发。
2.工具材料清单
工具/材料 | 版本/版本名 |
---|---|
HBuilder X | 3.1.4 |
Android Studio | 4.1.3 |
UNI-SDK | Android-SDK@3.1.4.80686_20210305 |
Android Gradle Plugin Version | 4.1.1 |
Gradle Version | 6.5 |
首先根据官方的gradel版本注意事项查看自己的gradle是否满足要求
至于Android Gradle Plugin Version
和Gradle Version
在AS的File-Project Structure
可以查看
3.SDK集成文档
3.1介绍
3.1.1环境集成:
-
libsecure_portal.so
库只放入libs\armeabi
文件夹下面,其他armeabi-v7a,armeabi64-v8
等目录请不要放置该so。 -
libsecure_portal.jar
文件放入libs目录下。 -
配置
build.gradle
文件,增加以下配置
defaultConfig {
ndk {
abiFilters 'armeabi'
}
}
sourceSets {
main {
jniLibs.srcDirs = ["libs"]
}
}
复制代码
- 在
AndroidMainfest.xml
中加入权限
<service
android:name="com.secure.sportal.sdk.NBA.SPNCService"
android:permission="android.permission.BIND_NBA_SERVICE">
<intent-filter>
<action android:name="android.net.NBAService" />
</intent-filter>
</service>
复制代码
以上4步完成后,环境配置完成。
3.1.2 代码集成
1.配置NBA参数,连接NBA
private static final int REQUEST_NC_CODE = 1234;
public void connectNBA() {
Properties params = new Properties();
// 配置NBA服务器地址,端口
params.setProperty(SPNBAClient.PARAM_NBA_HOST, "NBA公网地址 ");
params.setProperty(SPNBAClient.PARAM_NBA_PORT, "NBA公网端口");
// 认证服务器名称,这里建议填空,默认使用第一个认证服务器
params.setProperty(SPNBAClient.PARAM_AUTH_SERVER, "");
// SSLNBA 登录用户名
params.setProperty(SPNBAClient.PARAM_AUTH_USERNAME, "username");
// SSLNBA 登录密码
params.setProperty(SPNBAClient.PARAM_AUTH_PASSWORD, "password");
// 可直接在UI主线程调用login()方法,SDK内部自动在非UI线程完成工作,
// 并通过异步回调方式通知执行结果。login()会立即返回,不会阻塞。
SPNBAClient.login(this, params, new SPNBAClient.OnNBALoginCallback() {
@Override
public void onNBALoginMessage(int msgid, String msg) {
//SSLNBA的登录结果会回调该函数
if (msgid == SPNBAClient.MSGID_LOGIN_SUCC) {
// SSLNBA 登录成功
startTunnel(); // 启动nc网卡
} else if (msgid == SPNBAClient.MSGID_LOGIN_FAIL) {
// SSLNBA 登录失败,打印出失败信息
Log.e(TAG, "连接服务器失败 msgid is " + msgid + ", msg is " + msg);
}
}
});
}
复制代码
2.NBA连接成功后启动Android系统的NC网卡 注意:该函数第一次调用时会弹出系统授权框,询问用户是否允许建立虚拟网卡,这里必须要点"允许"才行。 如果第一次点了"拒绝"。则需要卸载APP,重启手机后再重装App,然后才会再次弹授权框给用户选择!!!
private void startTunnel() {
//判断网关配置是否有下发NC业务,如没有下发,则不需要建立nc网卡。
if (SPNBAClient.needsNBATunnel()) {
Intent intent = NBAService.prepare(getApplicationContext());
if (intent != null) {
// 用户从未同意过NC权限,启动对话框向用户确认权限
Log.e(TAG, "用户未授权NC权限,启动失败");
startActivityForResult(intent, REQUEST_NC_CODE);
} else {
// 用户已确认权限可直接启动NC
Log.d(TAG, "startNBATunnel...");
SPNBAClient.startNBATunnel(this, true, 0); //该函数最终会启动nc网卡
}
} else {
Log.e(TAG, "服务器未配置NC业务!");
}
}
复制代码
3.重写onActivityResult
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_NC_CODE) {
log.d("result is " + resultCode);
if (resultCode == RESULT_OK) {
SPNBAClient.startNBATunnel(this, true, 0); //该函数最终会启动nc网卡
Log.d(TAG, "登录成功,建立NC网卡成功");
}
}
}
复制代码
4. 开发
4.1原生项目运行
为了开发原生插件,那么建立原生的项目工程这是必不可少的条件,为了方便开发这里直接使用了UNI-SDK
文件夹中的UniPlugin-Hello-AS
这个工程,直接拖入到Android Studio
(以下简称AS
)点击文件-新建-Import Project
,
选中UniPlugin-Hello-AS
后点击确定,整个目录结构就出来了
现在点击运行按钮让示例项目跑起来。
4.2 插件开发
首先跟着Android原生插件开发教程,一步一步往下进行。 按照官方的布置,新建完成了要去配置刚创建的Module
的build.gradle
信息,注意是Module
的而不是app
的 安装官方的步骤,新建一个Module
,在此之前我们先把项目结构转换Project
类型的结构,然后点击 文件-新建-New Module
选择library
配置包名以及Module
名称,点击完成(Finish
)
按照官方的布置,新建完成了要去配置刚创建的Module
的build.gradle
信息,注意是Module
的而不是app
的
新建完成可能会出现如下的错误信息
Version 28 (intended for Android Pie and below) is the last version of the legacy support library, so we recommend that you migrate to AndroidX libraries when using Android Q and moving forward. The IDE can help with this: Refactor > Migrate to AndroidX... less... (Ctrl+F1)
Inspection info:There are some combinations of libraries, or tools and libraries, that are incompatible, or can lead to bugs. One such incompatibility is compiling with a version of the Android support libraries that is not the latest version (or in particular, a version lower than your targetSdkVersion). Issue id: GradleCompatible
复制代码
具体的解决办法可以去百度,但是我发现这貌似仅仅是个警告,反正最后没有影响我的编译、运行和使用。
4.2.1 SDK配置
按照第三方SDK的配置说明
-
libsecure_portal.so
库只放入libs\armeabi
文件夹下面,其他armeabi-v7a,armeabi64-v8
等目录请不要放置该so。 -
libsecure_portal.jar
文件放入libs目录下。
- 配置
build.gradle
文件,增加以下配置
defaultConfig { ndk { abiFilters 'armeabi' } } sourceSets { main { jniLibs.srcDirs = ["libs"] } } 复制代码
根据上面的配置描述,对
NBATunnel module
的build.gradle
进行修改//修改前 plugins { id 'com.android.library' } android { compileSdkVersion 30 buildToolsVersion "30.0.3" defaultConfig { minSdkVersion 16 targetSdkVersion 30 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" consumerProguardFiles "consumer-rules.pro" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } dependencies { implementation 'com.android.support:appcompat-v7:28.0.0' testImplementation 'junit:junit:4.+' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' } 复制代码
修改后
plugins { id 'com.android.library' } android { compileSdkVersion 30 defaultConfig { minSdkVersion 16 targetSdkVersion 30 versionCode 1 versionName "1.0" ndk { abiFilters "armeabi-v7a", "x86" //this 货 } testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" consumerProguardFiles "consumer-rules.pro" } sourceSets { main { //在 AS 中它会自动去加载 jniLibs 目录中的 *.so 文件(工程目录而不是在module目录)。如果你放在了其它任何目录,你必须要告诉它你将这些文件放在那里了(重定向)。 // 这里我写 `libs`,它就会去当前module的 `libs` 目录中找,你也可以放在其它任何目录中。 jniLibs.srcDirs = ["libs"] } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } dependencies { /**引入uniSDK必要的依赖开始**/ //以com.等开头的是第三方的远程依赖库 compileOnly 'com.android.support:recyclerview-v7:28.0.0' compileOnly 'com.android.support:support-v4:28.0.0' compileOnly 'com.android.support:appcompat-v7:28.0.0' compileOnly 'com.alibaba:fastjson:1.1.46.android' compileOnly fileTree(include: ['uniapp-v8-release.aar'], dir: '../app/libs') //这种引入方式 ../app/libs 指定了app目录下的模块的rarr文件 /**引入uniSDK必要的依赖结束**/ //结合上面的 jniLibs.srcDirs = ["libs"] implementation files('libs/libsecure_portal.jar') } 复制代码
对于
NBATunnel
模块Gradle.builde
中本地的arr
文件引入,我总结了以下几种情况//可以正常的运行,但是打包会出错 //implementation files('libs/libsecure_portal.jar') //无法编译Could not find method filetree() for arguments [{dir=libs, include=[*.jar]}] on object of type org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler. //implementation filetree(dir: 'libs', include: ['*.jar']) //Could not resolve all files for configuration ':app:debugRuntimeClasspath'. //implementation(name: 'libsecure_portal.jar', ext: 'jar') //可以通过编译,但是运行的时候会提示找不到类里面的方法 //compileOnly files('libs/libsecure_portal.jar') //可以通过编译和运行 //compile fileTree(dir: 'libs', include: ['*.jar']) 环境配置完毕厚,下面进行业务代码的开发 复制代码
- 在
AndroidMainfest.xml
中加入权限
<service
android:name="com.secure.sportal.sdk.NBA.SPNCService"
android:permission="android.permission.BIND_NBA_SERVICE">
<intent-filter>
<action android:name="android.net.NBAService" />
</intent-filter>
</service>
复制代码
环境配置完成,下面进行业务代码的开发
4.2.2 原生代码业务实现
按照官方的步骤这个类需要继承UniModule
,按照DEMO里面的写法,具体如下
package com.example.NBAmodule;
import android.app.Activity;
import android.content.Intent;
import android.net.NBAService;
import android.util.Log;
import com.alibaba.fastjson.JSONObject;
import com.secure.sportal.sdk.SPNBAClient;
import java.util.Properties;
import io.dcloud.feature.uniapp.annotation.UniJSMethod;
import io.dcloud.feature.uniapp.bridge.UniJSCallback;
import io.dcloud.feature.uniapp.common.UniModule;
import static android.app.Activity.RESULT_OK;
public class NBATunnel extends UniModule {
private static final int REQUEST_NC_CODE = 1234;
String TAG = "结果";
private UniJSCallback callback;
@UniJSMethod(uiThread = false)
public void connectNBA(JSONObject options, UniJSCallback callback1) {
callback = callback1;
//获取JS层传递过来的网关账号和密码
String username = options.getString("NBAUsername");
String password = options.getString("NBAPassword");
Log.i(TAG, username);
Log.i(TAG, password);
Properties params = new Properties();
params.setProperty(SPNBAClient.PARAM_NBA_HOST, "xxx.xxx.xxx.xx");
params.setProperty(SPNBAClient.PARAM_NBA_PORT, "xxx");
params.setProperty(SPNBAClient.PARAM_AUTH_SERVER, "");
params.setProperty(SPNBAClient.PARAM_AUTH_USERNAME, username);
params.setProperty(SPNBAClient.PARAM_AUTH_PASSWORD, password);
// 可直接在UI主线程调用login()方法,SDK内部自动在非UI线程完成工作,
// 并通过异步回调方式通知执行结果。login()会立即返回,不会阻塞。
SPNBAClient.login(mUniSDKInstance.getContext(), params, new SPNBAClient.OnNBALoginCallback() {
@Override
public void onNBALoginMessage(int msgid, String msg) {
//SSLNBA的登录结果会回调该函数
if ( msgid==SPNBAClient.MSGID_LOGIN_SUCC )
{
Log.i("msgid", String.valueOf(msgid));
Log.i("msgid", msg);
// SSLNBA 登录成功
startTunnel(); // 启动nc网卡
}
else if ( msgid==SPNBAClient.MSGID_LOGIN_FAIL )
{
// SSLNBA 登录失败,打印出失败信息
callback.invoke("网关登录失败:"+msg);
}
}
});
}
@UniJSMethod(uiThread = false)
private void startTunnel() {
//判断网关配置是否有下发NC业务,如没有下发,则不需要建立nc网卡。
if (SPNBAClient.needsNBATunnel())
{
Intent intent = NBAService.prepare(mUniSDKInstance.getContext());
if (intent != null)
{
// 用户从未同意过NC权限,启动对话框向用户确认权限
// Log.e(TAG, "用户未授权NC权限,启动失败");
callback.invoke("请求权限");
((Activity)mUniSDKInstance.getContext()).startActivityForResult(intent, REQUEST_NC_CODE);
}
else
{
// 用户已确认权限可直接启动NC
Log.d(TAG, "startNBATunnel...");
SPNBAClient.startNBATunnel(mUniSDKInstance.getContext(), true, 0);//该函数最终会启动nc网卡
callback.invoke("网关登录成功");
}
}
else
{
callback.invoke("服务器未配置NC业务");
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log.d("结果","result is " + resultCode+requestCode+data);
if (requestCode == REQUEST_NC_CODE)
{
callback.invoke("权限许可成功");
if (resultCode == RESULT_OK)
{
SPNBAClient.startNBATunnel(mUniSDKInstance.getContext(), true, 0);//该函数最终会启动nc网卡
callback.invoke("登录成功,建立NC网卡成功");
}
}
else
{
callback.invoke("权限许可失败");
}
}
}
复制代码
4.2.3 UNI代码编写
新建一个UNI项目,编写调用原生插件的代码
<template>
<view>
<!--状态栏 -->
<view class="status_bar"></view>
<view class="login">
<view class="content">
<!-- 头部logo -->
<view class="header">
<image src="@/static/images/logo.png"></image>
</view>
<text class="title"></text>
<!-- 主体表单 -->
<view class="main">
<wInput v-model="account" type="text" placeholder="账号" :focus="isFocus" :disabled="disabled">
</wInput>
<wInput v-model="password" type="password" placeholder="密码" :disabled="disabled"></wInput>
</view>
<wButton class="wbutton" text="登 录" :rotate="isRotate" @click="startLogin"></wButton>
</view>
<yomol-upgrade :type="upgradeType" :url="upgradeUrl" title="发现新版本" :content="upgradeContent"
ref="yomolUpgrade"></yomol-upgrade>
<!-- 网关modal -->
<tui-modal :show="modal" :custom="true" fadeIn >
<view class="tui-modal-custom">
<view class="tui-prompt-title">NBA账号</view>
<input class="tui-modal-input" v-model="NBAUsername" />
<view class="tui-prompt-title">NBA密码</view>
<input class="tui-modal-input password" v-model="NBAPassword" />
<tui-button height="72rpx" :size="28" shape="circle" @click="requestNBA">提交</tui-button>
</view>
</tui-modal>
</view>
</view>
</template>
<script>
let tunnel
if (uni.getSystemInfoSync().platform == "android")
tunnel = uni.requireNativePlugin('NBATunnel')
if (uni.getSystemInfoSync().platform == "ios")
tunnel = uni.requireNativePlugin("NBATunnel-NBATunnel")
import wInput from "@/components/watch-login/watch-input.vue";
import wButton from "@/components/watch-login/watch-button.vue";
import tuiModal from '@/components/tui-modal/tui-modal.vue';
import { DbHelper } from "@/js/db.js";
export default {
data() {
return {
account: "",
password: "",
isRotate: false, //是否加载旋转
isFocus: false, // 是否聚焦
disabled: false,
upgradeType: "pkg", //pkg 整包 wgt 升级包
upgradeContent: "", //更新内容
upgradeUrl: "", //更新地址
NBAUsername:'',
NBAPassword:'',
modal:false,
};
},
components: {
wInput,
wButton,
},
async mounted() {
await DbHelper.init();
this.isLogin();
},
methods: {
async isLogin() {
if (uni.getSystemInfoSync().platform == "android")
{
uni.showLoading({mask:true})
//无本地NBA数据
if(!uni.getStorageSync('NBAInfo'))
{
uni.hideLoading()
let [,res] = await uni.showModal({
content: '即将发起NBA权限请求,请点击确认,若在此过程中退出应用或者拒绝了权限,需要重装本应用才能重新发起NBA权限请求!',
showCancel:false,
});
this.modal = true
}
else//有本地NBA数据,说明之前已经建立了网卡
{
let NBAInfo = uni.getStorageSync('NBAInfo')
this.NBAUsername = NBAInfo.NBAUsername
this.NBAPassword = NBAInfo.NBAPassword
uni.hideLoading()
await this.requestNBA()
}
}
if (uni.getSystemInfoSync().platform == "ios")
{
uni.showLoading({mask:true})
//无本地NBA数据
if(!uni.getStorageSync('NBAInfo'))
{
uni.hideLoading()
let [,res] = await uni.showModal({
content: '请输入正确的NBA账号密码才能后续登录!',
showCancel:false,
});
this.modal = true
}
else//有本地NBA数据,说明之前已经建立了网卡
{
let NBAInfo = uni.getStorageSync('NBAInfo')
this.NBAUsername = NBAInfo.NBAUsername
this.NBAPassword = NBAInfo.NBAPassword
uni.hideLoading()
await this.requestNBA()
}
}
},
/**
* @description 连接NBA服务器
*/
async requestNBA(){
return new Promise((resolve,rejcet) => {
uni.showLoading({
title: 'NBA连接中...',
mask: true
});
if (!this.NBAUsername)
return uni.showToast({
title: "NBA账号不能为空!",
icon: "none"
}); // 显示提示框
if (!this.NBAPassword)
return uni.showToast({
title: "NBA密码不能为空!",
icon: "none"
});
if (uni.getSystemInfoSync().platform == "android")
{
tunnel.connectNBA({
NBAUsername:this.NBAUsername,
NBAPassword:this.NBAPassword
},async res=>{
this.modal = false
uni.hideLoading()
if(res == '网关登录成功' || res == '请求权限')
{
let NBAInfo = {
NBAUsername:this.NBAUsername,
NBAPassword:this.NBAPassword
}
uni.setStorageSync('NBAInfo',NBAInfo);
let { account,password } = uni.getStorageSync("userInfo"); // 从本地缓存中同步获取指定 key 对应的内容。
if (!account) return; // 本地没有用户信息 直接返回(停留在登录页面)
this.isFocus = false;
this.isRotate = true;
this.disabled = true;
this.account = account;
this.password = password;
setTimeout(()=>{this.getUpdate()},1000)
}
else
{
if(/02000405/.test(res))
{
await uni.showModal({
content:`NBA账号或者密码错误,请重新输入` ,
showCancel:false,
});
this.NBAUsername = ''
this.NBAPassword = ''
uni.removeStorageSync('NBAInfo');
this.modal = true
}
else
{
uni.showModal({
content:res,
showCancel:false
});
}
rejcet(res)
}
})
}
if (uni.getSystemInfoSync().platform == "ios")
{
let NBAInfo = {
NBAUsername:this.NBAUsername,
NBAPassword:this.NBAPassword
}
tunnel.connectNBA(NBAInfo,async res=>{
console.log(res);
this.modal = false
uni.hideLoading()
if(res == '网关登录成功' || res == '请求权限')
{
uni.setStorageSync('NBAInfo',NBAInfo);
let { account,password } = uni.getStorageSync("userInfo"); // 从本地缓存中同步获取指定 key 对应的内容。
if (!account) return; // 本地没有用户信息 直接返回(停留在登录页面)
this.isFocus = false;
this.isRotate = true;
this.disabled = true;
this.account = account;
this.password = password;
setTimeout(()=>{this.getUpdate()},1000)
}
else
{
if(/用户名或密码错误/.test(res))
{
await uni.showModal({
content:`NBA账号或者密码错误,请重新输入` ,
showCancel:false,
});
this.NBAUsername = ''
this.NBAPassword = ''
uni.removeStorageSync('NBAInfo');
this.modal = true
}
else
{
uni.showModal({
title:"NBA登录失败",
content:res,
showCancel:false
});
}
rejcet(res)
}
})
}
})
},
// 检查网络状态,并进一步检查APP更新情况(有网条件)
async getUpdate() {
let [, netWork] = await uni.getNetworkType()
if (netWork.networkType == "2g" || netWork.networkType == "none")
{
if (uni.getStorageSync("userInfo"))
uni.reLaunch({url: "/pages/home/home"});
}
else
{
console.log(plus.runtime.appid);
plus.runtime.getProperty(plus.runtime.appid, async widgetInfo => {
let option = {
params:{
appid: 'com.sklgp.warningSystem.ddh',
version: plus.runtime.version,
imei: plus.device.imei,
}
}
if (uni.getSystemInfoSync().platform == "android")
var {data: res} = await this.$http.get('/api/basedata/GetAppUpdateMsg',option)
if (uni.getSystemInfoSync().platform == "ios")
var {data: res} = await this.$http.getProxy('/api/basedata/GetAppUpdateMsg',option)
if (res.data)
{
if (uni.getSystemInfoSync().platform == "android")
{
this.upgradeUrl = res.data.DownLoadURL;
this.upgradeContent = res.data.Describe || "1.性能优化\n2.修复部分错误"
this.$refs.yomolUpgrade.show();
}
if (uni.getSystemInfoSync().platform == "ios")
{
await uni.showModal({
content: '有新的版本发布,请前往应用商店更新!',
showCancel:false
});
}
} else
uni.reLaunch({url: "/pages/home/home"})
});
}
},
async startLogin(e) {
if (this.isRotate) return;
if (!this.account)
return uni.showToast({
title: "账号不能为空!",
icon: "none"
}); // 显示提示框
if (!this.password)
return uni.showToast({
title: "密码不能为空!",
icon: "none"
});
this.isRotate = true;
this.disabled = true;
let res;
if (uni.getSystemInfoSync().platform == "android")
{
try {
let data = await this.$http.post("/api/security/token", {
username: this.account,
password: this.password,
});
res = data.data;
} catch (e) {
this.isRotate = false;
this.disabled = false;
return;
}
let {data: res2} = await this.$http.get("/api/account/GetUserInfo",{
custom: { auth: false },
header: { token: res.token }
});
let userInfo = {
account: this.account,
password: this.password,
token: res.token
};
for (let key in res2.data) {
userInfo[key] = res2.data[key];
}
uni.setStorageSync("userInfo", userInfo);
await this.getUpdate()
this.isRotate = false;
}
if (uni.getSystemInfoSync().platform == "ios")
{
tunnel.post({
url:`${this.$http.config.baseURL}/api/security/token?username=${this.account}&password=${this.password}`,
},callBack=>{
callBack = JSON.parse(callBack)
console.log(callBack);
//存储token
if(callBack.status != 0)
{
uni.showToast({
title: callBack.msg,
icon: 'none'
});
this.isRotate = false;
this.disabled = false;
return
}
tunnel.get({
url:`${this.$http.config.baseURL}/api/account/GetUserInfo`,
token:callBack.token
},callBack2=>{
callBack2 = JSON.parse(callBack2)
console.log(callBack2);
let userInfo = {
account: this.account,
password: this.password,
token: callBack.token
};
for (let key in callBack2.data)
{
userInfo[key] = callBack2.data[key];
}
console.log(userInfo);
uni.setStorageSync("userInfo", userInfo);
this.getUpdate()
})
})
}
},
},
};
</script>
复制代码
编写完成后,右键UNI项目: 发行-原生APP本地打包-生成本地打包APP资源
把原生工程中app/src/main/assets/apps
目录下的__UNI__BCEC007
这整个文件删除,然后把你打包完成以新的APPID
命名的文件粘贴到刚刚删除干净的apps
目录下这里以__UNI__BAC0197
为例子。
然后去app-src-main-assets-data-dcloud_control.xml
中修改appid
为你刚刚复制过来的那个appid
4.2.4 在原生APP里进行插件测试
写完之后需要进行隧道初始化的测试 要在原生工程中实现这个Module
的调用测试,需要进行下步骤:
- 将原生插件在通过
dcloud_uniplugins.json
进行声明和Module
引入 - 新建一个自定义的
UNI
项目,并编写对应的调用方法
所以我们第一步是先去原生工程中进行插件的声明,按照官方文档描述: 在UniPlugin-Hello-AS
工程下app-src-main-assets/dcloud_uniplugins.json
文件。 在moudles
节点下 添加你要注册的Module
或 Component
然后还要去app
模块的build.gradle
去添加新增的Moudle插件
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion '28.0.3'
defaultConfig {
applicationId "com.HBuilder.UniPlugin"
minSdkVersion 21
targetSdkVersion 21 //建议此属性值设为21 io.dcloud.PandoraEntry 作为apk入口时 必须设置 targetSDKVersion>=21 沉浸式才生效
versionCode 1
versionName "1.0"
multiDexEnabled true
ndk {
abiFilters "armeabi-v7a", "x86" //this 货
}
}
buildTypes {
release {
zipAlignEnabled true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
zipAlignEnabled true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
//使用uniapp时,需复制下面代码
/*代码开始*/
aaptOptions {
additionalParameters '--auto-add-overlay'
//noCompress 'foo', 'bar'
ignoreAssetsPattern "!.svn:!.git:.*:!CVS:!thumbs.db:!picasa.ini:!*.scc:*~"
}
/*代码结束*/
}
repositories {
flatDir {
dirs 'libs'
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation fileTree(dir: 'libs', include: ['*.aar'])
implementation "com.android.support:support-v4:28.0.0"
implementation "com.android.support:appcompat-v7:28.0.0"
/*uniapp所需库-----------------------开始*/
implementation 'com.android.support:recyclerview-v7:28.0.0'
implementation 'com.facebook.fresco:fresco:1.13.0'
implementation "com.facebook.fresco:animated-gif:1.13.0"
/*uniapp所需库-----------------------结束*/
// 基座需要,必须添加
implementation 'com.github.bumptech.glide:glide:4.9.0'
implementation 'com.alibaba:fastjson:1.1.46.android'
// 添加uni-app插件
// implementation project(':uniplugin_component')
// implementation project(':uniplugin_module')
// implementation project(':uniplugin_richalert')
implementation project(':NBAModule')
}
复制代码
配置完成后点击run
,然后点击app首页的图标调用原生的方法
以上可以看到能够正常的进行调用。插件测试成功
5 插件打包
插件打包第一步还是很简单的,点击IDE右侧的Gradle
图标,找到uniPlugin-Hello-AS/testModule/Tasks/NBATunnel/other/assembleRelease
,双击assembleRelease
在NBATunnel/build/outputs/arr
文件夹找到我们的NBATunnel-release.arr
按照插件编写命名规范生成uni-app插件的package.json
具体代码
{
"name": "原生插件",
"id": "NBATunnel",
"version": "1.0",
"description": "原生插件",
"_dp_type":"nativeplugin",
"_dp_nativeplugin":{
"android": {
"plugins": [
{
"type": "module",
"name": "NBATunnel",
"class": "com.example.NBAmodule.NBATunnel"
}
],
"hooksClass": "",
"integrateType": "aar",
"dependencies": [
],
"compileOptions": {
"sourceCompatibility": "1.8",
"targetCompatibility": "1.8"
},
"abis": [
"armeabi-v7a",
"x86"
],
"minSdkVersion": "16",
"useAndroidX": false,
"permissions": [
],
"parameters": {
}
}
}
}
复制代码
打包之前一定要记得去manifest.json
选择本地的原生插件,你会发现插件名就是之前package.json
中的name
字段。
打包的时候选择 运行-运行到手机或模拟器-制作自定义调试基座,等待打包完成点击运行即可,如果没有问题就可以打正式包了
本文引自 稀土掘金网的扶不起的蝌蚪。