前言
封装原生Uniapp插件,需要你打包自己的自定义基座,且插件的开发也是在自定义基座的安卓项目里进行的,如果你还没有自己的自定义基座,请先移步此处: UniApp使用最新版Android Studio本地离线打包全流程
为什么要开发原生插件
因为当HBuilderX中提供的能力无法满足App功能需求,需要通过使用Andorid/iOS原生开发实现时,可使用App离线SDK开发原生插件来扩展原生能力。
很显然HBuilder并没有提供RFID扫描的功能,需要我们自己利用它的安卓SDK进行二次封装,暴露出接口让我们在前端使用
为什么要离线打包自定义基座
因为封装原生插件需要频繁调试,总不至于改动一次就云打包一次,云打包一次十多分钟,且有次数限制,不方便。
里面有关于RFID的详细教程吗?
没有,我不懂这个,我是反编译apk慢慢捋出来的
源码
里面的证书我已经删了没啥用了,记得换成自己的,但是建议自己跑一遍
正文开始
创建一个Module模块
打开我们封装自定义基座的安卓项目,点击file->new->new module
添加第三方依赖
在新创建的Module模块下,创建libs目录,将官方提供的SDK放到该目录和自定义基座的libs目录下
都得放,自定义基座不放就会在开发完成运行时出现经典的报错:"XXX"插件不存在
gradle配置
打开module模块的build.gradle,在dependencies中添加以下依赖
compileOnly 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
compileOnly 'androidx.core:core:1.1.0'
compileOnly 'androidx.fragment:fragment:1.1.0'
compileOnly 'androidx.appcompat:appcompat:1.1.0'
compileOnly 'androidx.recyclerview:recyclerview:1.1.0'
compileOnly 'com.alibaba:fastjson:1.2.83'
compileOnly fileTree(include: ['uniapp-v8-release.aar'], dir: '../app/libs')
compileOnly(name: 'DeviceAPI_ver20230301_release', ext: 'aar') // 第三方SDK
然后打开自定义基座的build.gradle,在dependencies中添加以下依赖
implementation project(":RFIDDemo") // 这里是你的模块名,我的就叫这个,记得改
最后打开根目录的setting.gradle文件,在dependencyResolutionManagement的repositories中添加如下配置
flatDir {
dirs 'libs','./app/libs','./RFIDDemo/libs'
}
别忘了点Sync Now
代码预编写
这一部分终于可以直接看官网了 uni原生插件开发教程
这里只提一嘴,帮助大家理解@UniJSMethod (uiThread = false or true) 注解
什么情况下,代码应该运行在ui线程当中
- 当需要操作UI或Android主线程组件时,例如调用 Toast、更新 View、访问 Activity 等。
如果你在子线程中执行这些操作,Android 会抛出异常或导致程序崩溃。
就比如这种代码,就需要运行在UI线程中
@UniJSMethod(uiThread = true)
public void showToast(String message) {
Toast.makeText(mUniSDKInstance.getContext(), message, Toast.LENGTH_SHORT).show();
}
什么情况下,代码应该运行在非ui线程(或者称之为工作线程或者子线程)当中
那和上面对照着看,显而易见,当执行耗时操作(如文件操作、网络请求、数据库查询等),不应该阻塞UI时,就该运行在非UI线程。
如果你设置为 false,就不能直接访问 UI 元素,否则会出错。
| uiThread 值 | 线程类型 | 适用场景 |
|---|---|---|
true | 主线程 | 操作 UI、调用 Activity 等 |
false | 子线程 | 耗时任务、网络/IO 操作 |
前端的角度如何理解
-
uiThread = true类比于:
直接在主线程中操作 DOM 或调用 alert、console.log 等同步 UI 方法。
// 类似主线程中的 UI 操作
document.getElementById('btn').innerText = '更新了';
alert('弹窗提示');
-
uiThread = false类比于:
在异步任务(如 setTimeout、fetch)中处理数据,不直接操作 UI。
setTimeout(() => {
const data = heavyComputation(); // 耗时任务
// 更新 UI 前要回到主线程
requestAnimationFrame(() => {
document.getElementById('output').innerText = data;
});
}, 0);
这个流程中,耗时逻辑
heavyComputation在子线程中执行,而 UI 更新则“切换回主线程”执行。和 Java中 的uiThread=true概念对应。
总结
| Android 插件(Java) | 前端 JS 类比 | 说明 |
|---|---|---|
uiThread = true | 直接操作 DOM / alert / console.log | 必须在主线程中执行的 UI 操作 |
uiThread = false | 异步任务(如 fetch、setTimeout) | 在子线程执行,不可操作 UI |
代码编写 (偷懒版)
文档是相当抽象的,就一个javaDoc生成的。
我就是个前端,我哪会Java
我就是个前端,我哪懂什么RFID
有参考源码的情况下
开源码,直接抄,适当封装把值返回出来
没有源码的情况下
巧了,我就是这么个情况,但是RFID扫描的机器里有写好的App Demo,我们开始如下操作
把包从机器里提取出来
插机器连电脑,打开终端,使用adb命令
# windows下
adb shell dumpsys activity | findstr “mResume”
# mac下
adb shell dumpsys window | grep mCurrent
圈起来的就是包名了
然后获取apk路径
adb shell pm path com.rscja.ht
最后导出apk
adb pull /product/app/AppCenter/AppCenter.apk ./AppCenter.apk
然后apk就导出来了
使用jadx反编译,获取源码
window直接往进拖,mac则需要安装,然后终端输入jadx-gui,即可唤出页面,然后也是一样,直接把apk往进拖。
剩下的就是根据页面分析源码了,这里面的代码清清楚楚
这条命令可以帮你快速定位到相关页面
adb shell dumpsys activity activities
插件打包
这边默认大家根据各种手段开发好了自己的插件,现在进入打包阶段。
创建app目录下的assets/dcloud_uniplugins.json文件。在moudles节点下添加你要注册的Module
点击右边的大象,你会发现你的模块也在里面:
展开app和你的模块,点击assemble,打包基座和插件
基座老地方拿,插件在这里
打开Hbuilder把最新的基座,修改好名称后放到Uniapp项目里,然后在项目根目录下创建nativeplugins目录,
根据官方要求完整创建插件
官网上都有,package.json的解释十分详细,这里不贴了
插件的使用
插件的使用按照官网来就行,没什么大坑,这里就不贴了。