前言
大家好呀,作为一个前端工程师,对于使用uniapp来写app大家应该并不陌生,但是对于封装第三方提供的安卓原生sdk给到uniapp当中去使用,并没有做过,前段时间刚好有这么需求,做了一个uniapp链接蓝牙设备,并且调用安卓原生sdk的能力的这么一个需求,想着写一个笔记,都不敢叫做教程的一个文章,有什么问题希望大家可以指教。
前期准备
- java的sdk,由于是涉及到安卓的原生开发,所以这个是安卓的开发环境还是要搭建的,教程一搜一大把,我就不赘述了
- Android studio,安卓原生的IDE编码工具
- 下载uniapp官网中的示例包,后来用于调试原生安卓代码时需要用的到,nativesupport.dcloud.net.cn/NativePlugi...

官方示例
前期准备做好,我们使用android_studio打开UniPlugin-Hello-AS这个项目,等待依赖包安装完成。
安装依赖包会比较慢,建议使用科学上网,然后在Android-studio上设置一下代理,速度会快很多
设置代理

官方示例代码结构

蓝牙扫描插件
接下来,我们要做一个蓝牙扫描器的插件,提供给uniapp的项目使用。分析一下需求
- 功能包括,连接蓝牙扫描器、开始扫描、获取扫描到的数据、停止扫描。
- 综上所述的功能要求,我们不需要安卓原生这边写界面,只需要提供调用sdk的能力。所以我们参考官方的uniplugin_module这个插件
新建一个蓝牙扫描器的安卓原生模块
我们在官方示例的项目上,新建一个blue_scan的模块

选择Android Library,填写模块名称

新建之后,等待依赖包安装完成。 
在模块的build.gradle 依赖文件上加上插件编写必要的依赖包,我们可以选择直接从uniplugin_module 下的build.gradle 中的dependencies 依赖项拷贝过来,所以我们模块的build.gradle 下的dependencies就应该变成下面的样子。

javascript
dependencies {
compileOnly fileTree(dir: 'libs', include: ['*.jar'])
compileOnly fileTree(dir: '../app/libs', include: ['uniapp-v8-release.aar'])
compileOnly 'androidx.recyclerview:recyclerview:1.0.0'
compileOnly 'androidx.legacy:legacy-support-v4:1.0.0'
compileOnly 'androidx.appcompat:appcompat:1.0.0'
implementation 'com.alibaba:fastjson:1.2.83'
implementation 'com.facebook.fresco:fresco:1.13.0'
implementation 'androidx.core:core-ktx:1.17.0'
implementation 'androidx.appcompat:appcompat:1.7.1'
implementation 'com.google.android.material:material:1.13.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.3.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0'
}
然后同步一下依赖,就会自动安装。

插件代码编写
到这里,我们的插件项目已经新建好了,接下来我们可以着手写对应的代码了
新建一个BlueScan类
在我们项目根目录下的 /blue_scan/src/main/java/com.example.blue_scan 下新建一个名为BlueScan的java的class类

插件类必须继承UniModule,所有我们将代码修改成下面的样子。
java
package com.example.blue_scan;
import io.dcloud.feature.uniapp.common.UniModule;
public class BlueScan extends UniModule {
}
连接蓝牙扫描拍,与断开蓝牙扫描拍函数
有几个注意点:
- 函数必须是public
- 需要使用@UniJSMethod(uiThread = true)装饰器
- 安卓原生给uniapp传参需要通过UniJSCallback
java
package com.example.blue_scan;
import io.dcloud.feature.uniapp.annotation.UniJSMethod;
import io.dcloud.feature.uniapp.bridge.UniJSCallback;
import io.dcloud.feature.uniapp.common.UniModule;
public class BlueScan extends UniModule {
@UniJSMethod(uiThread = true)
public void connectBlueScan(UniJSCallback callback){
// 连接蓝牙扫描拍成功,这里的true可以在uniapp代码中拿到,所以通过这种方式传递数据
callback.invoke(true);
}
@UniJSMethod(uiThread = true)
public void disconnectBlueScan(UniJSCallback callback){
// 断开蓝牙扫描器
callback.invoke(true);
}
}
获取蓝牙扫描拍的数据
这里跟上面的函数会有一点不一样,因为蓝牙扫描拍的数据传递是需要多次传递的,也可以简单理解需要一个类似于'长链接的概念 ',这里有一个需要注意的点!!!,在uniapp的代码中需要通过对象嵌套函数的方式传这个回调函数。否则只有第一次会调用成功,这个应该跟安卓的垃圾回收机制有关。
java
@UniJSMethod(uiThread = true)
public void getBlueScanData(UniJSCallback callback){
// 传递蓝牙扫描拍的数据,需要多次传递的话,就使用invokeAndKeepAlive的方式
callback.invokeAndKeepAlive(1);
}
uniapp 调用时(这里是伪代码 ),看下面代码的方式才能保证invokeAndKeepAlive的方式正常生效。
javascript
const callBackObj = {
// 需要使用对象嵌套函数的方式传递,才能保证正常传递数据
fn:function(data){
}
}
blueScan.getBlueScanData(callBackObj.fn)
安卓项目中注册插件模块

插件调试
安卓原生的代码调试,最方便的方式就是官方示例中的方式,通过打包uniapp的本地资源放到安卓项目中进行调试,因为uniapp是无法看到安卓原生插件打印的一些输出的,并且uniapp是需要打包自定义基座才能使用安卓原生插件,每次改动插件都需要重新打包自定义基座,效率非常慢。所以我们下面使用官方示例中的方式
uniapp项目代码中,引用插件,并且调用。
uniapp中写如下代码。
javascript
<template>
<view class="content">
<button @tap="connectBlueScan">连接蓝牙</button>
<button @tap="disconnectBlueScan">断开蓝牙</button>
<button @tap="getBlueScan">获取蓝牙数据</button>
</view>
</template>
<script>
// 蓝牙原生插件,这里的名称就是在安卓项目中dcloud_uniplugins.json中写的name
const blueScanNativePlugin = uni.requireNativePlugin('BlueScan');
export default {
data() {
return {
title: 'Hello'
}
},
onLoad() {
},
methods: {
connectBlueScan(){
blueScanNativePlugin.connectBlueScan((data)=>{
console.log('connectBlueScan---',data)
})
},
disconnectBlueScan(){
blueScanNativePlugin.disconnectBlueScan((data)=>{
console.log('disconnectBlueScan-----',data)
})
},
getBlueScan(){
const callBack = {
fn:function(data){
console.log('getBlueScan----',data)
}
}
blueScanNativePlugin.getBlueScanData(callBack.fn)
},
}
}
</script>
<style>
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.logo {
height: 200rpx;
width: 200rpx;
margin-top: 200rpx;
margin-left: auto;
margin-right: auto;
margin-bottom: 50rpx;
}
.text-area {
display: flex;
justify-content: center;
}
.title {
font-size: 36rpx;
color: #8f8f94;
}
</style>
到uniapp开发者后台创建android的离线key

这个时候会需要用到安卓的签名信息,我们生成一个安卓的签名,我们通过keytool命令行的形式来生成,下面只是示例,具体的信息可以根据自己的要求来填写。
vbnet
keytool -genkey -v -keystore my-release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias my-alias
查看签名中的信息,到后台中进入填入。
perl
# 查看签名信息
keytool -list -v -keystore my-release-key.jks

保存好之后,就可以生成的离线key,填写到安卓项目中

把安卓的签名文件放到安卓项目中的app文件夹的根目录上,并填写对应的签名配置

在uniapp项目中新建一个nativeplugins的文件夹,这个名称是固定的,存放安卓插件内容。在这个目录下新建一个目录插件的名称,我们就叫BlueScan,在下面新建一个package.json。
json
{
"name": "BlueScan",
"id": "BlueScan", // ID必须唯一
"version": "1.0.0",
"_dp_type": "nativeplugin",
"_dp_nativeplugin": {
"android": {
"hooksClass": "",
"integrateType": "aar",
"plugins": [{
"type": "module",
"name": "BlueScan", // 在uniapp这边调用的时候插件的名称
"class": "com.example.blue_scan.BlueScan" // 安卓原生项目上的包名
}]
}
}
}
Z
填写好内容之后,我们在uniapp项目中的mainfest.json配置上勾选这个安卓原生插件。

勾选完之后,我们就可以将uniapp代码打包本地资源。

打包出来的资源放到安卓项目下app/src/main/assets/apps

修改dcloud_control.xml文件中的appid,为uniapp资源包的文件名

如果需要显示出uniapp这边的打印日志到安卓这边的控制台上的话,就修改一下dcloud_control.xml文件,加上下面的代码,需要注意加上了这个代码的话,最好每次都删除App重新安装,否则会出现代码缓存的问题,并且在生产发布的时候需要去掉这段代码

运行到真机
前面所有的工作都算做完了,没有问题的话,我们连接上手机,然后直接运行到真机,当当当~能正常看到打印的日志

打包插件到uniapp项目中使用
安卓打包插件

拷贝打包出的插件

放到uniapp项目的nativeplugins目录下对应的插件文件夹下,新建一个android的文件夹,把拷贝的插件包放进去

然后运行自定义基座 或者直接打包就可以直接使用了,uniapp标准基座是不支持自定义插件的