Uniapp 自制 Android 原生插件(详细流程,包含打包 aar、本地使用、上传云端)
2025-03-08 20:12:18
一、简介
-
当
Uniapp
实现有些特殊需求时,可能需要调用原生的一些API
,或者一些原生第三方库等,但是这些库或API
不支持Uniapp
能够直接调用到,这个时候可以通过封装一个Uniapp
可以使用的原生插件,将一些第三方原生库或API
封装到插件内,并以Uniapp
的方式暴露出来,使在Uniapp
开发中能正常调用或使用到这些库或API
。 -
本文会以一个简单的插件需求,整体的走一遍封装
Uniapp
能够使用的原生插件。 -
原生插件包内支持内部引入
Kotlin
第三方包。 -
附:Android 原生插件官方文档 。
-
插件注意事项
-
Activity
的获取方式。通过mUniSDKInstance.getContext()
强转Activity
。建议先instanceof Activity
判断一下再强转。 -
.vue
文件内暂时只能使用module
形式的插件。component
还不支持在.vue
下使用,需要放到.nvue
文件中使用。 -
component、module
的生命周回调,暂时只支持onActivityDestroy
、onActivityPause
、onActivityResult
,其他暂时不支持。 -
插件代码中用到的
JSONObject
、JSONArray
要使用com.alibaba.fastjson.JSONArray
、com.alibaba.fastjson.JSONObject
; 不要使用org.json.JSONObject
、org.json.JSONArray
否则造成参数无法正常传递使用等问题。 -
更多的自行看文档吧,列几个常见问题。
-
二、准备工作
-
必须走通 Uniapp Android 本地离线打包(详细流程),在这个基础上会更容易理解插件的本地调试与配置,开发工具与环境配置也都在这个文章内包含了。而且只是在这个流程的基础上稍微做下调整,然后再加上导入开发的插件引入与使用步骤。
-
重要
:目前无论是本地插件还是云端插件,都只支持云打包的基座
运行,离线打包基座也是无法运行出来效果的,只能走云打包
流程,运行后才能看到插件的效果。但有一种情况可以使用离线基座,那就是在开发原生插件的时候使用官方提供的
UniPlugin-Hello-AS
工程,它打包的离线基座可以运行出来插件的效果,但仅限于开发中的,也就是有源码在的这个插件工程里面的。如果这都不能运行,插件都没法开发了。而离线打包的工程HBuilder-Integrate-AS
则不行,只能走云打包基座。上面这种情况说的就是现在这种,咱们准备进行开发插件的工程就是
UniPlugin-Hello-AS
工程。 -
下载的
Android
原生插件工程目录结构介绍 -
将
UniPlugin-Hello-AS
拖入Android Studio
,遇到运行问题可以参考下面的文章:UniPlugin-Hello-AS
工程中:-
uniplugin_component
:是有界面插件案例。 -
uniplugin_module
:是无界面插件案例。 -
uniplugin_richalert
:是富文本弹窗插件案例,这个富文本插件之前有用来写过 Uniapp 原生插件的详细使用步骤(本地插件、云端插件),这里就是实现源码。
-
-
插件需要本地调试,流程跟离线打包流程差不多,都需要每次调整好
uniapp
代码后再编译出来导入到项目中进行调试。
三、开发中的插件如何调试
-
以
DCloud-RichAlert(uniplugin_richalert)
为例,它现在是个现成的插件,举例一下插件开发完成或开发中,如何进行本地调试。 -
找到
uniapp
测试项目,打开一个页面文件,添加上插件使用代码:js
代码解读
复制代码
{{ title }} -
然后按离线打包一样,生成本地打包资源
-
按照 Uniapp Android 离线生成自定义基座(详细流程) 配置一下
dcloud_control.xml
,并添加debug-server-release.aar
到libs
目录下,配置、添加了即可,不需要往后走流程。 -
然后按 Uniapp Android 本地离线打包(详细流程) 中的
四、离线工程的调整
进行配置好项目,其实到这里就是离线打包的本地工程配置了,流程一样的,配置好打包运行即可。唯独在配置
AppKey
的时候,发现顶部并没有包名
需要配置,这里可以不需要配置也没问题,只需要将下面的AppKey
配置好即可。 -
然后运行项目到模拟器即可,如果没有打开后没有生效,没有弹出窗口,可以将设备上安装的
app
删除重新运行,一般这样就解决了。
四、Java
开发一个简单的 module 扩展(无界面原生插件)
-
第一步:创建
Android Studio
的Module
模块Module name
::app:mylibrary
的意思就是在app
目录下新建一个mylibrary
模块,如果要跟官方结构保持一致在app
目录外面,直接保留mylibrary
即可,为了跟官方保持命名统一,可以改成uniplugin_mylibrary
。创建好
Module
后需要配置一下它的build.gradle
文件,最简单的就是拷贝一下其他插件的,推荐拷贝uniplugin_richalert
的,看起来它的依赖比较干净。注意备份新建插件build.gradle
的namespace
,拷贝覆盖后,在修改为它自己的。然后修改
namespace
为这个插件新建时候的包名 -
第二步:在
app
目录的build.gradle
中引入新建的插件然后编译代码是否报错,没有报错不用管,我看其他插件也没配置,如果有报错
Failed to calculate the value of property 'namespace'
,可以添加配置一下插件的命名空间:细节:每次修改配置后,如何快速生效,点一下工具顶部显示的
Sync Now
然后等待即可: -
第三步:创建插件入口文件
需要给插件创建一个入口文件,文件名随便起,也可以参考其他插件的
XXXXModule(例如:RichAlertModule)
回车即可创建:
然后继承
UniModule
:java
代码解读
复制代码
package com.example.uniplugin_mylibrary; import io.dcloud.feature.uniapp.common.UniModule; public class MylibraryModule extends UniModule { }
-
第四步:到
app
目录dcloud_uniplugins.json
中配置插件信息首先拷贝入口文件的
class
:找到
app
目录下的dcloud_uniplugins.json
进行配置插件:到这插件配置相关的就完成了,现在主要就是实现代码了。
-
第五步:添加一份简单的调试代码
java
代码解读
复制代码
package com.example.uniplugin_mylibrary; import android.util.Log; // JSONObject 需要使用 com.alibaba.fastjson 这个 import com.alibaba.fastjson.JSONObject; import io.dcloud.feature.uniapp.annotation.UniJSMethod; import io.dcloud.feature.uniapp.bridge.UniJSCallback; import io.dcloud.feature.uniapp.common.UniModule; public class MylibraryModule extends UniModule { // 日志标识,后续可以通过日志表示快速排查问题 private String Tag = "MylibraryModule"; // 私有方法 private int add(int a, int b) { return a + b; } // 对外暴露的方法需要添加 @UniJSMethod (uiThread = true/false),并且设置为public 。 // uiThread: 是否在 UI 线程执行。 // JSONObject options: 可以接收 UniApp 传过来的数据,也可以为空。 // callback: 回调的时候,可以用 JSONObject 存放数据,回调 json 数据,但是某些时候回调不到,可以切换成 Map。 @UniJSMethod(uiThread = false) public void testAdd (JSONObject options, UniJSCallback callback) { Log.e(Tag, "TestAddFunc:" + options); int sum = add(10, 8); if (callback != null) { JSONObject data = new JSONObject(); data.put("code", "success"); data.put("sum", sum); // 组装好的数据通过回调外抛给 uniapp callback.invoke(data); } } }
-
@UniJSMethod(uiThread = false)
使用分析:在
UniApp
的自定义封装组件中,@UniJSMethod
注解用于标记方法,使其可以被JavaScript
调用。注解中的uiThread
参数决定了方法运行的线程,具体作用如下:java
代码解读
复制代码
@UniJSMethod(uiThread = true) public void updateUI() { // 更新UI操作,需要运行在主线程 } @UniJSMethod(uiThread = false) public String performBackgroundTask() { // 耗时操作,比如读取文件或网络请求 return "Task Done"; }
-
uiThread = true
(默认)-
运行线程:在主线程(UI 线程)执行。
-
适用场景:如果方法需要直接操作与 UI 相关的内容(如更新视图或触发动画),需要在 UI 线程中运行。
-
优点:确保与 UI 的操作线程安全。
-
缺点:如果方法执行耗时操作,可能导致 UI 卡顿,影响用户体验。
-
-
uiThread = false
-
运行线程:在工作线程中执行。
-
适用场景:用于执行耗时的任务,例如网络请求或复杂计算,避免阻塞主线程。
-
优点:不会影响 UI 响应速度。
-
注意事项:如果方法需要操作 UI,必须切换到主线程,否则可能会出现线程冲突或崩溃。
-
-
总结:
-
uiThread = true
适合与UI
相关的任务。 -
uiThread = false
适合耗时任务,可以避免阻塞UI
。
-
-
-
没遇到可跳过,导入
Uni
相关插件报错找不到解决方案,细节问题:需要把代码改不会报错的情况下,点这个
Sync Now
按钮,要不然依然不会生效,也可以通过修改插件的build.gradle
中的namespace 'com.example.uniplugin_mylibrary'
然后保存,重新唤起配置按钮点击生效。 -
第六步:使用插件
js
代码解读
复制代码
{{ title }} 使用后,重新按离线打包一样,记得先通过工具栏的
Build -> 先 Clean Project -> 再 Rebuild Project -> 最后在打包或运行
,生成基座或者测试离线包都适用这套流程。生成本地打包资源,导入到
Android Studroid
重新运行到手机上,如果没生效,可以将手机上之前安装的app
删除再运行一次。如果遇到这种
Test
文件报错的,直接删掉,留着主文件夹就行了,如果需要用它可以自己修好,这两个测试文件夹本来就是可有可无的。 -
调试细节:
-
方式一:直接用
Android Studio
运行打包好的UniApp
离线代码。 -
方式二:按照自定义基座一样,将
Android Studio
编译好的apk
丢到UniApp
项目中运行基座到手机或模拟器,这样可以两边都能看到之前输出的调试日志,效果图:
-
五、Java
开发一个简单的 componet 扩展(有界面原生插件)
-
可以了解下 Uniapp .vue 与 .nvue 区别与书写区别;混搭使用与场景注意事项; ,
.nvue
对原生界面支持比较好,如果想要在.vue
文件中引入安卓原生界面组件的话,推荐放在.nvue
文件中引用,不建议放在.vue
文件内引用使用,可能会出别的兼容问题。 -
所以这里准备做一个
componet 扩展(有界面原生插件)
,那么则需要放到.nvue
文件中使用,因此需要修改一下当前页面的文件后缀。 -
第一步:创建
Android Studio
的Module
模块同第
四、Java 开发一个简单的 module 扩展(无界面原生插件)
一致。 -
第二步:在
app
目录的build.gradle
中引入新建的插件同第
四、Java 开发一个简单的 module 扩展(无界面原生插件)
一致。 -
第三步:创建插件入口文件
同第
四、Java 开发一个简单的 module 扩展(无界面原生插件)
一致,只是继承对象不同。需要给插件创建一个入口文件,文件名随便起,也可以参考其他插件的
XXXXComponent(例如:MylibraryComponent)
,然后继承UniComponent
:java
代码解读
复制代码
package com.example.uniplugin_mylibrary; import android.widget.TextView; import io.dcloud.feature.uniapp.UniSDKInstance; import io.dcloud.feature.uniapp.ui.action.AbsComponentData; import io.dcloud.feature.uniapp.ui.component.AbsVContainer; import io.dcloud.feature.uniapp.ui.component.UniComponent; public class MylibraryComponent extends UniComponent
{ // 构造函数 // 在 Android Studio 中,快捷添加 构造函数 (Constructor) 非常简单,快捷键: // Windows/Linux:Alt + Insert // macOS:Command + N public MylibraryComponent(UniSDKInstance uniSDKInstance, AbsVContainer absVContainer, AbsComponentData absComponentData) { super(uniSDKInstance, absVContainer, absComponentData); } } -
第四步:到
app
目录dcloud_uniplugins.json
中配置插件信息同第
四、Java 开发一个简单的 module 扩展(无界面原生插件)
一致。到这插件配置相关的就完成了,现在主要就是实现代码了。
-
第五步:添加一份简单的调试代码
java
代码解读
复制代码
package com.example.uniplugin_mylibrary; import android.content.Context; import android.graphics.Color; import android.widget.TextView; import androidx.annotation.NonNull; import io.dcloud.feature.uniapp.UniSDKInstance; import io.dcloud.feature.uniapp.annotation.UniJSMethod; import io.dcloud.feature.uniapp.ui.action.AbsComponentData; import io.dcloud.feature.uniapp.ui.component.AbsVContainer; import io.dcloud.feature.uniapp.ui.component.UniComponent; import io.dcloud.feature.uniapp.ui.component.UniComponentProp; public class MylibraryComponent extends UniComponent
{ // 构造函数 // 在 Android Studio 中,快捷添加 构造函数 (Constructor) 非常简单,快捷键: // Windows/Linux:Alt + Insert // macOS:Command + N public MylibraryComponent(UniSDKInstance uniSDKInstance, AbsVContainer absVContainer, AbsComponentData absComponentData) { super(uniSDKInstance, absVContainer, absComponentData); } // 创建UI组件,直接输入 init 会自动出来初始化函数 @Override protected TextView initComponentHostView(@NonNull Context context) { TextView textView = new TextView(context); textView.setTextSize(30); textView.setTextColor(Color.RED); return textView; } // 给UI组件添加可以设置的属性 tel @UniComponentProp(name = "tel") public void setTel(String telNumber) { getHostView().setText("tel:" + telNumber); } // 给UI组件添加可以调用的方法,默认 uiThread = true ,所以不写就是默认 @UniJSMethod public void clearTel () { getHostView().setText(""); } } -
第六步:使用插件
UI 插件
不需要引入,直接使用即可:js
代码解读
复制代码
编译细节同第
四、Java 开发一个简单的 module 扩展(无界面原生插件)
一致。 -
调试细节:
同第
四、Java 开发一个简单的 module 扩展(无界面原生插件)
一致,效果图:
六、Android Studio
删除插件
-
新建好的插件,需要移除,右键没找到
Delete
按钮,因为组件文件不能直接删除,需要先移除组件,变成普通文件夹才可以删除。 -
先移除配置,这样避免删除后报错:
-
然后移除组件,记得顶部切换到
Project
展示模式,不要使用Android
展示进行移除。 -
按上面操作之后,文件夹还在,但是变成了普通文件夹。看下面两个文件夹的图标不一样了,这就变成了普通文件夹,然后右键就有删除按钮了,删除即可。
如果没有生效,可以看下
工具上面顶部弹出的蓝色提示条上的按钮,点一点
。
七、插件混合开发
-
一个插件包下面,是可以存在多个
模块(Module)或组件(Component)的
,上面的案例是拆开单个讲解而已,其实放一起也没问题的,无非多配置一个。 -
配置好后重新打个包导入运行效果是一样的,而且两个插件模块都会生效。
html
代码解读
复制代码
八、打包插件(导入本地插件使用)
-
找到
Android Studio
右侧菜单栏上的Gradle
,打开需要打包的插件目录,找到打包按钮。在打开这个菜单后,一般是在
Gradle -> uniplugin_mylibrary -> Tasks -> other
里面有assembleDebug
与assembleRelease
按钮的,点一下可以直接构建测试与正式版本的aar
,结果跟下面是一样的。尝试了下没找到这两个按钮,那么也没事,其实这两个选项也就是执行了两个命令,找不到不会自己执行一下么。
-
一般在项目根目录有一个
gradlew
文件,通过它就可以执行assembleDebug
与assembleRelease
命令:然后打开
Android Studio
底部的Terminal
手动执行命令:这里会分两种情况:
1、
如果当前工程是一个模块工程
,那么可以直接执行命令sh
代码解读
复制代码
# Debug 构建 $ ./gradlew assembleDebug # Release 构建 $ ./gradlew assembleRelease # 其他命令(知道就行) # 清理项目 $ ./gradlew clean # 列出当前模块支持的 Gradle 任务,检查是否有 `assembleDebug` 或 `assembleRelease` $ ./gradlew tasks # 如果 Gradle 配置有问题,运行以下命令强制重新加载: $ ./gradlew --refresh-dependencies assembleRelease
2、
如果当前工程是一个正常项目工程,然后在项目工程内新建了模块
,就类似我上面截图的这种,则需要这么执行:sh
代码解读
复制代码
your-project/ ├── gradlew ├── settings.gradle ├── mylibrary/ │ ├── build.gradle ├── app/ │ ├── build.gradle
sh
代码解读
复制代码
# Debug 构建 $ ./gradlew :mylibrary:assembleDebug # Release 构建 $ ./gradlew :mylibrary:assembleRelease # :mylibrary 冒号后面的表示模块名称。 # 如果模块名称与你的实际模块不同,请检查模块 settings.gradle 中的模块声明。 # 其他命令(知道就行,使用同理)
-
执行命令后,发现报错没执行权限
zsh: permission denied: ./gradlew
,添加执行权限:sh
代码解读
复制代码
$ chmod +x ./gradlew
-
添加后再次执行,运行成功:
-
拿到
.aar
文件后,那么安卓的自定义插件也就开发完成了,然后到uniapp
项目中根目录找到nativeplugins
文件夹,没则自己手动创建。然后按照官方的文件夹结构创建创建一下,不清楚结构可以下载一个官方的插件 DCloud-RichAlert,下离线包即可,可以参考package.json
配置。调整好后,丢入自己的插件包:
拖入
uniapp
项目中nativeplugins
文件夹内,然后修改package.json
。附 UniApp 官方原生插件开发 package.json 书写规则与插件结构文档,只需添加需要的配置即可,不需要全部配置,可以参考官方上传的插件配置。 -
配置好后,到配置中添加一下本地插件,插件这里显示的名称是
package.json
中的name
字段,名字随意起。勾选添加一下插件,然后打个自定义基座运行即可,本地插件
或云插件
都是需要运行在自定义基座
上面的,标准基座
是会报找不到的插件的。插件(包含云插件、本地插件)
只能通过云打包的基座
运行。上面显示支持
Android / iOS
是刚才package.json
配置的,移除iOS
的配置就只有安卓了。 -
最后重新打一个包,运行看看效果,
注意
:必须使用云基座打包才能看到效果
,放到离线打包工程中运行时看不到效果的哦。推荐删除安装好的app
,在用基座运行,免得没生效。打包就用公共证书速度走,正式包了再填信息。
-
运行成功:
九、上传到云端插件
-
将刚才的插件文件夹,压缩成
.zip
就行。 -
登录注册 DCloud插件市场 按提示步骤提交插件(需要编写对应插件的说明文档,
md(markdown)
格式)。登录后,点击自己的昵称,就能进入
我的插件
页面,然后按着填信息,上传插件压缩包。 -
后面没啥东西了,看着来就行了。
转自:https://juejin.cn/post/7439935260241018930
发表评论: