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

uniapp原生插件 - android原生插件打包流程 ( 避坑指南一) 原因是因为module的androidManifest.xml文件没有注册activity。这一步 很重要,一定要注册。当时开发中安卓插件打包成功后,uniapp引用,用云打包 ,总是提示不包含插件。注意:尽量用demo里面的buildgradle插件版本号和gradle版本。注意:建议都下载最新版本,下载地址与详细请参考官方文档。本文采用vue3+ts编写uniapp部分。四、在Android studio中调试。一、导入uni插件项目(三、在uniapp中引用。二、新建module。 阅读详情

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 Version和Gradle 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原生插件开发教程,一步一步往下进行。 按照官方的布置,新建完成了要去配置刚创建的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的配置说明

  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 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'])
环境配置完毕厚,下面进行业务代码的开发
复制代码
  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字段。

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

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

uniapp调用原生android插件传递回调函数 uniapp原生方法,回调函数 阅读详情

相关推荐

uni-app之android原生插件开发

uni-app之android原生插件开发

u013083465的专栏 8882

Android原生插件开发——同步与异步

一、后端代码: 同步方式 @UniJSMethod(uiThread = false) public JSONObject scan() throws ConfigurationException { if (barcodeUtility != null) { Log.i(TAG, "ScanBarcode"); barcodeUtility.startScan(context, BarcodeUtility.ModuleType.B

qq_43026206的博客 2042

Android uni-app 封装原生插件

前言 据广大用户的需求,需要把我们anyRTC的SDK,封装到uni-app来使用,并且实现音视频通话。这边文章图文讲解一下怎么封装原生插件,并且在下一章uni-app实现音视频通话。 anyRTC开发者中心 1.uni-app是什么? 一个使用Vue.js开发所有前端应用框架,开发者编写一套代码,可发布到ios、Android、H5、以及各种小程序(微信/支付宝/百度/头条/QQ/钉钉)等多个平台。 如果你还没有了解uni-app是什么:点击这里:这篇文章举例说明。 2.uni-app的特点 1、跨

anyRTC的博客 6638

uniapp targetSdkVersion版本问题,部分市场要求不能低于30

targetSdkVersion用于指定应用的目标Android版本(API等级),默认值为28(即Android9.0)解决办法:时间来不及可以提供一个假的注销功能,点击注销提示:您的注销申请已提交。1.targetSdkVersion版本问题,部分市场要求不能低于30。应用中不允许出现暂未开放,待开发,以及点击无效等操作。4.必须要征得用户勾选并同意服务协议和隐私政策。错误示范:登录即代表您已同意服务协议与隐私政策。3.部分市场需要提供用户注销功能。解决办法:加个复选框。解决方法:把功能补全。

weixin_38943972的博客 4329

uniapp原生安卓插件开发之路

声明: 我的功能需求:全局悬浮框,也就是在其他应用上依旧能悬浮。查遍uni文档,无法实现,甚至native.js也无法实现,无奈只能走上原生插件开发这条不归路了。(吐槽:天天写前端js,突然写了下android简直要炸,public static String啥的,var不香嘛,不过别人严谨肯定还是有别人道理的,加油吧,争取能入个门!) 本人不懂安卓开发,完全安卓小白,所以安卓方面可能表达的不是很好,安卓代码可能写的不优美,但我功能最终还是实现了。但是为了需求,能力总能被“逼”出来。有描述错误的还望指正

Jone's Area 1万+

uni-app x,一个纯原生的Android App开发工具

下一代uni-app,一个神奇的产品。用vue语法、uni的组件、api,以及uts语言,编译出了kotlin的app。不再使用js引擎和webview。纯纯的kotlin原生app。uni-app x,让“跨平台开发性能不如原生”的这条曾广为流传的规则,扔进了历史的垃圾桶!

djemx2025的博客 6090

uni-app混合原生安卓开发

uni-app和原生App混合开发问题 首先务必确认uni-app和原生代码,谁是主谁是从的问题。 1、如果你的应用是uni-app开发的,需要扩展一些原生能力,那么首先去插件市场看看有没有现成的插件,如果没有,就自己开发,开发文档请参考原生插件开发教程https://nativesupport.dcloud.net.cn/NativePlugin/course/android。 2、如果你的App是原生开发的,部分功能栏目想通过uni-app实现,有2种方式: 在原生App里集成uni小程序sdk,参考

乌龟赛跑 1万+

uni-app原生打印插件开发

草稿

qq_36561118的博客 3778

uni-app与原生插件混合开发调试3-安卓原生插件开发调试和打包

上面已经介绍了怎么安装开发和调试环境,接下来就是安卓原生插件的具体开发和调试步骤:将uniapp前端项目的index.vue文件新增代码。重新打包,重新放到Android studio项目app/src/main/assps/目录下。重新启动run,项目启动成功。

rightkk的博客 1819

uni-app总结5-UTS插件开发

uni-app开发系统教程之UTS插件开发

Heyuan_Xie的博客 3270

uni-app与原生插件混合开发调试1-环境准备

简称uni,是dcloud旗下的产品,基于H5和vue,可用于开发各平台小程序,并且可以将开发的小程序跨平台打包,支持Android和iOS。,H是HTML的首字母,Builder是构造者,X是HBuilder的下一代版本。我们也简称HX。HX是轻如编辑器、强如IDE的合体版本。是dcloud提供的用于在本地打包的SDK,其本质就是一个Android工程,下载之后导入到Android studio,再将uni-app打包的本地资源包复制到离线SDK工程,依托离线SDK运行。

rightkk的博客 1720

Uni-APP 安卓原生插件开发及使用

HBuilderX学习笔记3 安卓插件开发@[TOC](HBuilderX学习笔记3 安卓插件开发)一、开发环境二、开发步骤1. 下载sdk2. 导入 UniPlugin-Hello-AS工程3. 编译其中的uniplugin_module,生成aar4. 在 HBuilder项目里添加插件package.json内容:5. 配置manifest.json自定义调试基座 一、开发环境 jdk 1.8 android studio 2.9.8+ 的 android平台SDK 二、开发步骤 1. 下载sdk

猿来这样-谢厂节的博客 6129

记一次uni-app Android原生插件开发

项目使用到了RFID射频手持设备(PDA),用于读取羊只耳标功能。原来有写过一个插件,新的设备与原来的不一样,所以需要重新写一遍。之前没有记录,这次重新记录下过程。使用的硬件设备Demo 为: uhfg_v2.2.10;...

郊眠寺 2280

uni-app使用原生安卓aar文件包,让uni-app可以调用安卓原生aar文件内的方法

桥接第三方aar文件,uni-app内使用第三方aar文件内的方法,uni-app引入原生安卓aar,uni-app引入安卓aarSDK文件,SDK引入,桥接开发

weixin_69477112的博客 4480

uni-app安卓原生插件的编写与调试

一、为什么会有这篇文章 这里是官方给出的开发文档链接。但是还是有很多诸如以下的情况出现: 官方可能也是疲于回复这些问题,这些情况把打算入门插件开发的小伙们挡在了门外,这里结合当初笔者学习的一些经验,从一个Android开发者的角度给大家做一些分享。 二、自定义基座 uni-app开发的原生插件,必须运行在自定义基座中,这个很好理解,你要调试原生的东西,肯定要有运行环境,自定义基座就是这...

qwe749082787的博客 1万+

安卓java打包uniapp原生插件 和 uniapp使用安卓android原生插件

安卓java打包uniapp原生插件 和 uniapp使用安卓android原生插件

cf 1946

【uniapp】 uni-app 之 Android 原生插件开发

在"android"文件夹下创建一个新的 Java 类,命名为"YourPluginNamePlugin",并继承自"UniPlugin"类。在上述配置中,我们添加了一个名为"your-plugin-name"的插件,平台为"android",描述为"你的插件描述",版本为"1.0.0",提供者为"your-plugin-provider"。填写项目名称和保存路径,点击"创建"按钮。在 HBuilderX 中,点击右上角的"运行"按钮,选择"运行到手机或模拟器",然后选择你的设备进行编译和运行。

菜狗小测试 1419

前沿数据解析 | 麻省理工-斯坦福-丰田研究中心电池数据集:AI驱动下的电池寿命早期预测

本文解析了麻省理工、斯坦福与丰田研究中心联合发布的电池数据集,展示了AI驱动下的电池寿命早期预测技术。通过分析124个商用锂离子电池的完整生命周期数据,研究团队仅用前100次循环数据即可准确预测电池报废时间,为电动汽车电池管理提供革命性解决方案。数据集包含LFP/石墨体系电池的详细测试记录,结合差分容量分析和梯度提升树模型,预测精度显著提升。

weixin_26950847的博客 529
上一篇: UTS 在安卓系统上的开发应用
下一篇: JEDEC发布用于高性能计算应用的DDR5 SDRAM标准更新
std7879
博客等级 码龄5年 273粉丝 283原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

打赏作者

std7879

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

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

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

打赏作者

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

抵扣说明:

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

余额充值