在您阅读此文档时,我们假定您已经具备了相应Android应用开发经验,使用Android Studio开发过Android原生。也应该对HTML,JavaScript,CSS等有一定的了解, 并且熟悉在JavaScript和JAVA环境下的JSON格式数据操作等。
为了插件开发者更方便快捷的开发uni原生插件!2.9.8版本起修改了uni插件开发API及规范。当然还会继续兼容老的插件运行及开发。推荐插件开发者按新版规范实现开发插件。方便日后更高效的更新迭代uni原生插件!
HX3.8.7+版本开始 androidx适配库升级为1.1.0 还在使用support/v4老版本适配的插件同学请尽快升级适配androidx 避免打包机环境不支持导致的编译失败或闪退
Activity
变更为FragmentActivity
.解决部分插件开发者需要加载Fragment的需求。但也带来了一些代码与之前不同的修改。需要注意以下问题:
Activity
没有限制requestCode的值域。FragmentActivity
的权限申请限制requestCode的值域不能为负值,也不能大于16位bit值65536。否则报异常或崩溃Can only use lower 16 bits for requestCode
,强烈推荐使用0~60500之间取值。可以根据自己的业务进行定制自己的功能。 主要分为两类扩展:
Module 扩展 非 UI 的特定功能.
Component 扩展 实现特别功能的 Native 控件.
Tips
下面以TestModule为例,源码请查看 UniPlugin-Hello-AS(2.9.8+)工程中的uniplugin_module
模块;
uniplugin_module
示例:
//导入aar需要的配置
repositories {
flatDir {
dirs 'libs'
}
}
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')
}
Tips:
uniapp-v8-release.aar是扩展module主要依赖库,必须导入此依赖库!
示例:
public class TestModule extends UniModule
示例:
//run ui thread
@UniJSMethod(uiThread = true)
public void testAsyncFunc(JSONObject options, UniJSCallback callback) {
Log.e(TAG, "testAsyncFunc--"+options);
if(callback != null) {
JSONObject data = new JSONObject();
data.put("code", "success");
callback.invoke(data);
}
}
//run JS thread
@UniJSMethod (uiThread = false)
public JSONObject testSyncFunc(){
JSONObject data = new JSONObject();
data.put("code", "success");
return data;
}
-keep public class * extends io.dcloud.feature.uniapp.common.UniModule{*;}
下面以TestComponent
为例,源码请查看 UniPlugin-Hello-AS(2.9.8+)工程中的uniplugin_component
模块;
请参考 扩展 Module
示例:
public class TestText extends UniComponent<TextView>
示例:
@Override
protected TextView initComponentHostView(@NonNull Context context) {
TextView textView = new TextView(context);
textView.setTextSize(20);
textView.setTextColor(Color.BLACK);
return textView;
}
示例:
@UniComponentProp(name = "tel")
public void setTel(String telNumber) {
getHostView().setText("tel: " + telNumber);
}
-keep public class * extends io.dcloud.feature.uniapp.ui.component.UniComponent{*;}
示例:
@UniJSMethod
public void clearTel() {
getHostView().setText("");
}
<template>
<div>
<myText ref="telText" tel="12305" style="width:200;height:100" @onTel="onTel" @click="myTextClick"></myText>
</div>
</template>
<script>
export default {
methods: {
myTextClick(e) {
this.$refs.telText.clearTel();
}
}
}
</script>
向JS环境发送一些事件,比如click事件
void fireEvent(elementRef,type)
void fireEvent(elementRef,type, data)
void fireEvent(elementRef,type,data,domChanges)
elementRef
(String):产生事件的组件idtype
(String): 事件名称,UniApp默认事件名称格式为"onXXX",比如OnPullDown
data
(Map<String, Object>): 需要发送的一些额外数据,比如click
时,view大小,点击坐标等等。domChanges
(Map<String, Object>): 目标组件的属性和样式发生的修改内容示例:
以myText标签为例, 通过 @事件名="方法名" 添加事件,如下添加onTel
事件,源码请查看 UniPlugin-Hello-AS工程中的uniplugin_component
模块
//原生触发fireEvent 自定义事件onTel
Map<String, Object> params = new HashMap<>();
Map<String, Object> number = new HashMap<>();
number.put("tel", telNumber);
//目前uni限制 参数需要放入到"detail"中 否则会被清理
params.put("detail", number);
fireEvent("onTel", params);
//标签注册接收onTel事件
<myText tel="12305" style="width:200;height:100" @onTel="onTel"></myText>
//事件回调
methods: {
onTel: (e)=> {
console.log("onTel="+e.detail.tel);
}
}
JS调用时,有的场景需要返回一些数据,比如以下例子,返回x、y坐标
void invoke(Object data);
void invokeAndKeepAlive(Object data);
invoke
调用javascript回调方法,此方法将在调用后被销毁。invokeAndKeepAlive
调用javascript回调方法并保持回调活动以备以后使用。示例:
@UniJSMethod(uiThread = true)
public void testAsyncFunc(JSONObject options, UniJSCallback callback) {
Log.e(TAG, "testAsyncFunc--"+options);
if(callback != null) {
JSONObject data = new JSONObject();
data.put("code", "success");
callback.invoke(data);
}
}
注意
执行自定义事件fireEvent时params的数据资源都要放入到"detail"中。如果没有将你得返回的数据放入"detail"中将可能丢失。请注意!!!
用于页面监听持久性事件,例如定位信息,陀螺仪等的变化。
示例:
页面监听event事件
var globalEvent = uni.requireNativePlugin('globalEvent');
globalEvent.addEventListener('myEvent', function(e) {
console.log('myEvent'+JSON.stringify(e));
});
插件 原生代码发出myEvent
事件
Map<String,Object> params=new HashMap<>();
params.put("key","value");
mUniSDKInstance.fireGlobalEventCallback("myEvent", params);
注意 globalEvent事件只能通过页面的UniSDKInstance实例给当前页面发送globalEvent事件。其他页面无法接受。
使用PermissionControler实现权限申请
PermissionControler.requestPermissions(Activity, permissions, REQUEST_CODE);
UniModule、UniComponent都有onRequestPermissionsResult周期函数 实现并处理数据即可监听权限申请结果
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
简单示例:
import io.dcloud.common.core.permission.PermissionControler;
import android.Manifest;
...
...
public class TestModule extends UniModule {
final int REQUEST_CODE = 11111;
private static String[] permissions = new String[]{
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
};
@UniJSMethod(uiThread = true)
public void requestPermissions() {
if(mUniSDKInstance.getContext() != null) {
Activity activity = (Activity) mUniSDKInstance.getContext();
PermissionControler.requestPermissions(activity, permissions, REQUEST_CODE);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if(requestCode == REQUEST_CODE) {
for(int i= 0; i< permissions.length; i++) {
String preName = permissions[i];
int granted = grantResults[i];
if(Manifest.permission.ACCESS_FINE_LOCATION.equals(preName) && granted == PackageManager.PERMISSION_GRANTED) {
//获取权限结果
}
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
封装了一个 RichAlertModule, 富文本alert弹窗Module
public class RichAlertModule extends UniDestroyableModule {
...
@UniJSMethod(uiThread = true)
public void show(JSONObject options, UniJSCallback jsCallback) {
if (mUniSDKInstance.getContext() instanceof Activity) {
...
RichAlert richAlert = new RichAlert(mUniSDKInstance.getContext());
...
richAlert.show();
...
}
}
...
...
@UniJSMethod(uiThread = true)
public void dismiss() {
destroy();
}
@Override
public void destroy() {
if (alert != null && alert.isShowing()) {
UniLogUtils.w("Dismiss the active dialog");
alert.dismiss();
}
}
}
// require插件名称
const dcRichAlert = uni.requireNativePlugin('DCloud-RichAlert');
// 使用插件
dcRichAlert.show({
position: 'bottom',
title: "提示信息",
titleColor: '#FF0000',
content: "<a href='https://uniapp.dcloud.io/' value='Hello uni-app'>uni-app</a> 是一个使用 Vue.js 开发跨平台应用的前端框架!\n免费的\n免费的\n免费的\n重要的事情说三遍",
contentAlign: 'left',
checkBox: {
title: '不再提示',
isSelected: true
},
buttons: [{
title: '取消'
},
{
title: '否'
},
{
title: '确认',
titleColor: '#3F51B5'
}
]
}, result => {
switch (result.type) {
case 'button':
console.log("callback---button--" + result.index);
break;
case 'checkBox':
console.log("callback---checkBox--" + result.isSelected);
break;
case 'a':
console.log("callback---a--" + JSON.stringify(result));
break;
case 'backCancel':
console.log("callback---backCancel--");
break;
}
});
以上两种方式选一即可
public class RichAlert_AppProxy implements UniAppHookProxy {
@Override
public void onCreate(Application application) {
//当前uni应用进程回调 仅触发一次 多进程不会触发
//可通过UniSDKEngine注册UniModule或者UniComponent
}
@Override
public void onSubProcessCreate(Application application) {
//其他子进程初始化回调 可用于初始化需要子进程初始化需要的逻辑
}
}
nativePlugins
: 插件跟节点 可存放多个插件hooksClass
: 生命周期代理(实现AppHookProxy接口类)格式(完整包名加类名)plugins
: 插件数组name
: 注册名称class
: module 或 component 实体类完整名称type
: module 或 component类型。{
"nativePlugins": [
{
"hooksClass": "uni.dcloud.io.uniplugin_richalert.apphooks",
"plugins": [
{
"type": "module",
"name": "DCloud-RichAlert",
"class": "uni.dcloud.io.uniplugin_richalert.RichAlertModule"
}
]
}
]
}
通过使用uni.requireNativePlugin获取插件对象。
const pluginImpl = uni.requireNativePlugin('插件 name')
安装最新HbuilderX 大于等于1.4.0+
创建uni-app工程或在已有的uni-app工程编写相关的.nvue 和.vue文件。使用uni-app插件中的module 或 component。
xxx.vue 示例代码(源码请参考UniPlugin-Hello-AS项目中uniapp示例工程源码
文件夹的unipluginDemo
工程,导入示例工程时需要重新获取appid)
把APP资源文件放入到UniPlugin-Hello-AS工程下 app
Module根目录assets/apps/测试工程appid/www对应目录下,再修改assets/data/dcloud_control.xml!修改其中appid=“测试工程appid”!,测试工程UniPlugin-Hello-AS 已有相关配置可参考。具体可查看App离线打包。
appid注意 一定要统一否则会导致应用无法正常运行!
// 添加uni-app插件
implementation project(':uniplugin_richalert')
示例:
|-- DCloud-RichAlert --->插件id命名的文件夹
|-- android --->安卓插件目录
|--libs
- xxx.jar --->依赖的jar
- libxxx.so --->依赖的so文件
- vendor.aar --->依赖的aar
- unipluginRichAlert.aar --->插件module的aar
生成插件的aar并放入到android目录下
注意:新版本Android studio将assembleRelease放入other中了
创建package.json文件并填写必要的信息。放入到android目录下
创建libs文件夹。并放入到android目录下
将插件依赖的aar文件放入到插件android目录下
注意
目前发现部分同学将开发插件的uniapp-v8-release.aar、uniapp-release.aar放到了插件包libs中。导致冲突。切记不要放进libs里去!!! 插件所依赖的aar要放到android目录下。
源代码的package中一定要作者标识防止与其他插件冲突导致插件审核失败,无法上传。
如示例中插件类的“package uni.dcloud.io.uniplugin_richalert;” “dcloud”就是作者标识!
Module的注册命名首先必须要使用id为前缀。与id完全相同也可以!如果你的插件中可能存在多个Module就需要注意id为前缀的重要性。
Component的注册命名还没有严格要求。但开发者尽量使用id前缀。减少与其他插件的命名冲突导致插件无法正常运行。
Tips:
id
?请阅读package.json
示例:
{
"name": "插件名称",
"id": "DCloud-RichAlert", // 插件标识
"version": "插件版本号",
"description": "插件描述信息",
"_dp_type":"nativeplugin",
"_dp_nativeplugin":{
"android": {
"plugins": [
{
"type": "module",
"name": "DCloud-RichAlert_TestModule", //id为前缀
"class": "uni.dcloud.io.uniplugin_richalert.TestModule"
},
{
"type": "component",
"name": "DCloud-RichAlert_TestComponent",
"class": "uni.dcloud.io.uniplugin_richalert.TestComponent"
}
]
}
}
...
...
压缩插件id命名的文件夹为zip即可。具体目录机构如下:
注意:.os文件需要注意 armeabi-v7a、x86 、arm64-v8a以上三种类型的.so必须要有,如果没有无法正常使用!!
** 登录注册**DCloud插件市场 按提示步骤提交插件(需要编写对应插件的说明文档,md(markdown) 格式)
Tips onActivityResume事件存在缺陷。应用第一次启动无法正常收到onActivityResume事件,后台切换到前台是可以收到的。
示例:
@Override
public void onActivityPause() {
UniLogUtils.e(TAG, "onActivityPause");
}
@Override
public void onActivityResume() {
UniLogUtils.e(TAG, "onActivityResume");
}
请将以下混淆配置信息添加到相应的原生插件proguard配置文件中即可。
-keep public class * extends io.dcloud.weex.AppHookProxy{*;}
-keep public class * extends io.dcloud.feature.uniapp.UniAppHookProxy{*;}
-keep public class * extends io.dcloud.feature.uniapp.common.UniModule{*;}
-keep public class * extends io.dcloud.feature.uniapp.ui.component.UniComponent{*;}
Q:云打包 运行插件提示"XXX"插件不存在?
A:按以下步骤检测自己项目:
1、请使用自定义基座测试自己插件。默认基座并不包含你的插件。
2、确认打包时是否勾选了"XXX"插件。
3、查看插件配置package.json
这也是多数同学容易犯错的地方。主要检查class
配置的信息与插件中的aar是否匹配。
4、插件中依赖库没有添加到配置或者aar中。多数为离线工程没问题,云打包有问题!认真检查依赖库是否都包含在插件中。
5、以上都不能解决你的问题,请@客服
Q:插件中怎么跳转原生Activity页面 A:获取UniSDKInstance对象。该对象中可以获取到上下文.通过startActivity跳转
示例
@UniJSMethod (uiThread = true)
public void gotoNativePage(){
if(mUniSDKInstance != null) {
Intent intent = new Intent(mUniSDKInstance.getContext(), NativePageActivity.class);
mUniSDKInstance.getContext().startActivity(intent);
}
}
Q:插件跳转Activity页面后。Activity页面关闭后有数据需要返回。怎么能实现? A:可以按以下步骤操作实现:
示例
public static int REQUEST_CODE = 1000; //数据返回标识code
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == REQUEST_CODE && data.hasExtra("respond")) {
Log.e("TestModule", "原生页面返回----"+data.getStringExtra("respond"));
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
示例
@UniJSMethod (uiThread = true)
public void gotoNativePage(){
if(mUniSDKInstance != null && mUniSDKInstance.getContext() instanceof Activity) {
Intent intent = new Intent(mUniSDKInstance.getContext(), NativePageActivity.class);
((Activity)mUniSDKInstance.getContext()).startActivityForResult(intent, REQUEST_CODE);
}
}
示例
Intent intent = new Intent();
intent.putExtra("respond", "我是原生页面");
setResult(TestModule.REQUEST_CODE, intent);
finish();
Q:插件开发支持Androidx吗? A:设置useAndroidX = true 目前已知讯飞语音无法支持androidx配置不能兼容需要注意并提醒插件使用者。
Q:component、module的生命周回调 不支持OnActivityCreate(),某些注册服务需要该事件注册怎么办。 A:component可以在的构造函数中调用相关注册初始化服务等操作, module的构造无法获取到上下文。可能需要换一个思路。通过js调用相关初始化的函数。
Q:打包发现集成的三方库与主APP集成的三方库有冲突。导致无法打包成功?
A:
Q:HX3.0.0+版本云打包编译之前插件无法编译通过。HX2.9.8版本云打包是可以的。
A:
build.gradle
文件。检测是否使用api files(xxx.aar)
引入某些依赖库aar文件。如果有这样的配置请改为使用compileOnly 修饰。否则无法在3.0.0+编译通过。主要原因就是资源冲突。 api files(xxx.aar)
这种玩法是错误的。classes.jar
文件。使用压缩工具查看一下classes.jar
里的文件是否存在资源文件。如AndroidManifest.xml
、res
等资源文件需要删除。否则无法在3.0.0+编译通过。Q:插件SDK部分需要在Application初始化。目前在AppHookProxy中onCreate初始化无效
A: 请使用UniAppHookProxy接口(此处注意!不是AppHookProxy是UniAppHookProxy) 有onSubProcessCreate子进程初始化回调。将SDK需要在Application初始化的代码onCreate和onSubProcessCreate都放一份就可以了。
Q:插件可以重写Application吗?
A:不支持。重写Application存在很多安全隐患。
Q:插件生产文件路径需要注意哪些?
A:
file://
防止路径被转换导致无法正确获取文件/storage/emulated/0/Android/data/$应用的包名$/apps/$uniapp的appid$/doc/
目录下Q: 如何查看如何查看uniapp console日志
A:修改项目中assets/data/dcloud_control.xml 内部信息。将syncDebug改为true,开启调试模式。 注意正式版需要改为false!!! 查看log.TAG为console
Q: HX3.8.7插件编译失败或运行插件闪退
A: 查看是否使用了老版本的v4依赖库。3.8.7开始已升级androidx1.1.0版本,对老版本v4已不再兼容了 请升级androidx依赖库。