UNI APP---Android端原生插件开发实战(二)

1.前言

最近一个项目要求我们的产品必须走网络隧道,并且提供了对应的SDK,很明显只能通过原生开发的方式才能实现这个流程,之前已经写过一篇通过代理的方式进行数据请求,而这次Android端的方式是采用NBA的方式进行数据请求,下面开始进行原生插件的开发。

2.工具材料清单

工具/材料版本/版本名
HBuilder X3.1.4
Android Studio4.1.3
UNI-SDKAndroid-SDK@3.1.4.80686_20210305
Android Gradle Plugin Version4.1.1
Gradle Version6.5

首先根据官方的gradel版本注意事项查看自己的gradle是否满足要求

至于Android Gradle Plugin VersionGradle Version在AS的File-Project Structure可以查看

3.SDK集成文档

3.1介绍

3.1.1环境集成:

  1. libsecure_portal.so库只放入libs\armeabi文件夹下面,其他armeabi-v7a,armeabi64-v8等目录请不要放置该so。

  2. libsecure_portal.jar文件放入libs目录下。

  3. 配置build.gradle文件,增加以下配置

defaultConfig {
    ndk {
            abiFilters 'armeabi'
    }
}

sourceSets {
    main {
            jniLibs.srcDirs = ["libs"]
    }
}

复制代码
  1. 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原生插件开发教程,一步一步往下进行。 按照官方的布置,新建完成了要去配置刚创建的Modulebuild.gradle信息,注意是Module的而不是app的 安装官方的步骤,新建一个Module,在此之前我们先把项目结构转换Project类型的结构,然后点击 文件-新建-New Module

选择library

配置包名以及Module名称,点击完成(Finish)

按照官方的布置,新建完成了要去配置刚创建的Modulebuild.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的配置说明

  1. libsecure_portal.so库只放入libs\armeabi文件夹下面,其他armeabi-v7a,armeabi64-v8等目录请不要放置该so。

  2. libsecure_portal.jar文件放入libs目录下。

  1. 配置build.gradle文件,增加以下配置
defaultConfig {
	ndk {
		abiFilters 'armeabi'
	}
}

sourceSets {
	main {
		jniLibs.srcDirs = ["libs"]
	}
}

复制代码

根据上面的配置描述,对NBATunnel modulebuild.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'])
环境配置完毕厚,下面进行业务代码的开发
复制代码
  1. 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字段。

打包的时候选择 运行-运行到手机或模拟器-制作自定义调试基座,等待打包完成点击运行即可,如果没有问题就可以打正式包了

本文引自 稀土掘金网的扶不起的蝌蚪。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

std7879

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值