UNI APP---Android native plug-in development practice (2)

1 Introduction

A recent project requires that our products must go through a network tunnel and provide a corresponding SDK. Obviously, this process can only be realized through native development. I have written an article about data requests through proxy methods, but this time The method on the Android side is to use the NBA method to request data, and the development of the native plug-in will start below.

2. List of tools and materials

Tools/Materials version/version name
HBuilder X 3.1.4
Android Studio 4.1.3
UNI-SDK [email protected]_20210305
Android Gradle Plugin Version 4.1.1
Gradle Version 6.5

First check whether your gradle meets the requirements according to the official gradel version precautions

As Android Gradle Plugin Versionand Gradle Versionin AS File-Project Structurecan view

3. SDK integration document

3.1 Introduction

3.1.1 Environment integration:

  1. libsecure_portal.soThe library is only placed libs\armeabiunder the folder, armeabi-v7a,armeabi64-v8please do not place the so in other directories.

  2. libsecure_portal.jarThe file is placed in the libs directory.

  3. Configuration build.gradlefile, add the following configuration

defaultConfig {
    ndk {
            abiFilters 'armeabi'
    }
}

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

复制代码
  1. AndroidMainfest.xmlAdd permissions in
<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>
复制代码

After the above 4 steps are completed, the environment configuration is complete.

3.1.2 Code Integration

1. Configure NBA parameters and connect to 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. Start the NC network card of the Android system after the NBA connection is successful. Note: When this function is called for the first time, a system authorization box will pop up, asking the user whether to allow the establishment of a virtual network card. You must click "Allow" here. If you click "decline" for the first time. You need to uninstall the app, restart the phone and then reinstall the app, and then the authorization box will pop up again for the user to choose! ! !

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. RewriteonActivityResult

@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. Development

4.1 Native project running

In order to develop native plug-ins, it is an indispensable condition to establish a native project project. For the convenience of development, the project UNI-SDKin the folder is directly used here UniPlugin-Hello-AS, and it is directly dragged into Android Studio(hereinafter referred to as AS) and clicked on File-New- Import Project,

After selecting UniPlugin-Hello-AS, click OK, and the entire directory structure will come out.

Now hit the run button to start the example project.

4.2 Plug-in development

First follow the Android native plug-in development tutorial , step by step. According to the official layout, after the new creation is completed, it is necessary to configure the newly created Moduleinformation build.gradle. Note that it is Modulenot appthe installation of the official steps, and create a new one. ModuleBefore that, we first convert the project structure to Projectthe structure of the type, and then click File-New-New Module

select library

Configure the package name and Modulename, click Finish ( Finish)

According to the official layout, after the new creation is completed, you need to configure the newly created Moduleinformation build.gradle, pay attention to yes Moduleand appnot

The following error message may appear after the new creation is completed

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
复制代码

The specific solution can go to Baidu, but I found that this seems to be just a warning, anyway, it did not affect my compilation, operation and use in the end.

4.2.1 SDK configuration

Follow the configuration instructions of the third-party SDK

  1. libsecure_portal.soThe library is only placed libs\armeabiunder the folder, armeabi-v7a,armeabi64-v8please do not place the so in other directories.

  2. libsecure_portal.jarThe file is placed in the libs directory.

  1. Configuration build.gradlefile, add the following configuration
defaultConfig {
	ndk {
		abiFilters 'armeabi'
	}
}

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

复制代码

According to the above configuration description, modify NBATunnel modulethebuild.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'
}
复制代码

after modification

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')
}
复制代码

For the import of local files in NBATunnelthe module , I have summarized the following situationsGradle.buildearr

//可以正常的运行,但是打包会出错
//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.xmlAdd permissions in
<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>
复制代码

The environment configuration is complete, and the business code is developed below

4.2.2 Native code business realization

According to the official steps, this class needs to be inherited UniModule. According to the writing method in DEMO, the details are as follows

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 code writing

Create a new UNI project and write the code to call the native plug-in

<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>
复制代码

After the writing is complete, right-click the UNI project: release-native APP local packaging-generate local packaging APP resources

Delete the entire file app/src/main/assets/appsin the directory of the original project , and then paste the file you have packaged and renamed to the directory that has just been deleted. Here is an example.__UNI__BCEC007APPIDapps__UNI__BAC0197

Then go to app-src-main-assets-data-dcloud_control.xmlmodify it appidto the one you just copiedappid

4.2.4 Plug-in testing in native APP

After writing, the tunnel initialization test needs to be performed. To implement this invocation test in the native project Module, the following steps are required:

  • dcloud_uniplugins.jsonDeclare and Moduleintroduce the native plug-in through
  • Create a new custom UNIproject and write the corresponding calling method

So our first step is to declare the plug-in in the native project, as described in the official document: file UniPlugin-Hello-ASunder the project . Add what you want to register under the app-src-main-assets/dcloud_uniplugins.jsonnode or moudlesModuleComponent

Then go to appthe module build.gradleto add the new Moudle plug-in

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')
}
复制代码

Click after the configuration is complete run, and then click the icon on the home page of the app to call the native method

As can be seen above, the call can be made normally. Plugin test successful

5 plugin packaging

The first step of plug-in packaging is still very simple, click Gradlethe icon on the right side of the IDE, find uniPlugin-Hello-AS/testModule/Tasks/NBATunnel/other/assembleRelease, double-clickassembleRelease

In NBATunnel/build/outputs/arrthe folder, find our uni-app plug-in generated NBATunnel-release.arr according to the plug-in naming conventionpackage.json

specific code

{
    "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": {
                
            }
        }
    }
}
复制代码

Before packaging, you must remember to manifest.jsonselect the local native plug-in, and you will find that the plug-in name is the field package.jsonin the previous name.

When packaging, choose to run - run to the mobile phone or emulator - make a custom debugging base, wait for the packaging to complete and click to run, if there is no problem, you can open the official package

This article is quoted from the unsupported tadpole of Rare Earth Nuggets.

Guess you like

Origin blog.csdn.net/std7879/article/details/127700402
Recommended