Uniapp——安卓与Uniapp数据通信交互
原生向Uniapp通信
- UniApp 安卓自定义基座:Android 原生与 UniApp 双向通信完整方案
- 一、先理解几个核心概念
- 1. `globalEvent` 是什么?
- 2. `mUniSDKInstance` 是哪里来的?
- 3. `UniModule` 和 `UniDestroyableModule` 区别
- 二、整体通信架构
- 三、完整代码示例
- 1. 全局保存 UniSDKInstance:`UniInstanceHolder.java`
- 2. 自定义原生插件:`NativeBridgeModule.java`
- 3. Android Activity 主动发送消息:`NativeActivity.java`
- 4. 注册原生插件:`dcloud_uniplugins.json`
- 5. UniApp 页面使用
- 四、为什么 `uni.requireNativePlugin('globalEvent')` 不会创建 Module 实例?
- 五、Activity 里能不能直接用 `mUniSDKInstance`?
- 六、能不能用 EventBus?
- 七、重要注意事项
- 1. 必须先监听,再发送
- 2. 必须先绑定 `mUniSDKInstance`
- 3. `globalEvent` 只适合当前页面
- 4. 不要强引用 `UniSDKInstance`
- 5. 关键业务数据建议缓存后主动读取
- 八、推荐使用场景
- 九、最终结论
- 参考文档
UniApp 安卓自定义基座:Android 原生与 UniApp 双向通信完整方案
在 UniApp 安卓自定义基座开发中,经常会遇到一个需求:
Android 原生层拿到数据之后,如何传给 UniApp 页面?同时 UniApp 页面又如何给 Android 原生发送消息?
比如这些场景:
- 原生登录 SDK 回调登录结果
- 原生支付页面返回支付状态
- Android Activity 处理完业务后通知 UniApp
- UniApp 页面主动调用 Android 原生能力
- 原生 SDK 异步回调后需要通知前端页面刷新状态
本文整理一套比较完整的双向通信方案:
Android -> UniApp:globalEvent + fireGlobalEventCallback UniApp -> Android:uni.requireNativePlugin + 自定义 UniModule 方法需要注意的是,globalEvent不是你自己写的原生插件 Module,它只是 UniApp JS 侧的内置事件监听对象。真正能拿到mUniSDKInstance的,是你自己写的UniModule或UniDestroyableModule。
一、先理解几个核心概念
1.globalEvent是什么?
globalEvent是 UniApp App 端内置的全局事件监听对象。
UniApp 页面可以这样监听 Android 原生发来的事件:
constglobalEvent=uni.requireNativePlugin('globalEvent')globalEvent.addEventListener('native_event',e=>{console.log('收到 Android 消息:',JSON.stringify(e))})但是它只负责 JS 侧监听,不会创建你的 Android Module 实例。
也就是说:
uni.requireNativePlugin('globalEvent')拿到的是 DCloud 内置事件监听对象,不是你自己的NativeBridgeModule。
2.mUniSDKInstance是哪里来的?
mUniSDKInstance是UniModule父类提供的当前页面实例。
只有你的类继承了:
UniModule或:
UniDestroyableModule才可以直接使用:
mUniSDKInstance例如:
publicclassNativeBridgeModuleextendsUniModule{@UniJSMethod(uiThread=true)publicvoidsendEvent(){mUniSDKInstance.fireGlobalEventCallback("native_event",params);}}如果代码写在下面这些地方,是没有mUniSDKInstance的:
ActivityApplicationServiceBroadcastReceiver普通工具类SDK回调类这也是很多人找不到mUniSDKInstance的原因。
3.UniModule和UniDestroyableModule区别
| 类 | 作用 | 适合场景 |
|---|---|---|
UniModule | 普通原生 Module | 一次性调用、获取数据、打开页面 |
UniDestroyableModule | 带销毁生命周期的 Module | EventBus 注册、监听器、定位、蓝牙、SDK 回调等需要释放的场景 |
如果只是简单调用:
getToken(callback)用UniModule就可以。
如果你有:
EventBus.register(this)BroadcastReceiver注册Handler延迟任务SDK监听回调Dialog/Popup持有Activity建议使用:
UniDestroyableModule因为它可以在destroy()中释放资源。
二、整体通信架构
推荐结构如下:
UniApp 页面 | | uni.requireNativePlugin('NativeBridge') v NativeBridgeModule | | 保存 mUniSDKInstance v UniInstanceHolder | | Activity / SDK 回调调用 fireEvent v mUniSDKInstance.fireGlobalEventCallback(...) | v UniApp globalEvent.addEventListener(...)双向通信关系:
UniApp -> Android: nativeBridge.sendMessageToAndroid(params, callback) Android -> UniApp: mUniSDKInstance.fireGlobalEventCallback("native_event", data) Android -> UniApp 页面监听: globalEvent.addEventListener("native_event", callback)三、完整代码示例
下面示例实现两个方向:
- UniApp 主动给 Android 发送消息
- Android Activity 主动给 UniApp 页面发送消息
1. 全局保存 UniSDKInstance:UniInstanceHolder.java
不要直接用:
publicstaticUniSDKInstanceinstance;因为UniSDKInstance绑定的是当前页面实例,直接静态强引用容易导致页面泄漏、旧页面收到事件、事件发错页面。
建议用WeakReference:
packagecom.xxx.uniplugin;importandroid.os.Handler;importandroid.os.Looper;importjava.lang.ref.WeakReference;importjava.util.Map;importio.dcloud.feature.uniapp.UniSDKInstance;publicclassUniInstanceHolder{privatestaticWeakReference<UniSDKInstance>instanceRef;publicstaticvoidbind(UniSDKInstanceinstance){instanceRef=newWeakReference<>(instance);}publicstaticvoidclear(UniSDKInstanceinstance){if(instanceRef!=null&&instanceRef.get()==instance){instanceRef.clear();instanceRef=null;}}publicstaticvoidfireEvent(StringeventName,Map<String,Object>data){if(instanceRef==null)return;UniSDKInstanceinstance=instanceRef.get();if(instance==null)return;newHandler(Looper.getMainLooper()).post(()->{instance.fireGlobalEventCallback(eventName,data);});}}2. 自定义原生插件:NativeBridgeModule.java
这个 Module 负责:
- 绑定当前页面的
mUniSDKInstance - 接收 UniApp 发来的消息
- 回调结果给 UniApp
- 测试 Android 主动发送事件给 UniApp
- 页面销毁时清理实例
packagecom.xxx.uniplugin;importcom.alibaba.fastjson.JSONObject;importjava.util.HashMap;importjava.util.Map;importio.dcloud.feature.uniapp.annotation.UniJSMethod;importio.dcloud.feature.uniapp.bridge.UniJSCallback;importio.dcloud.feature.uniapp.common.UniDestroyableModule;publicclassNativeBridgeModuleextendsUniDestroyableModule{@UniJSMethod(uiThread=true)publicvoidbindUniInstance(){UniInstanceHolder.bind(mUniSDKInstance);}@UniJSMethod(uiThread=true)publicvoidunbindUniInstance(){UniInstanceHolder.clear(mUniSDKInstance);}/** * UniApp 主动给 Android 发送消息 */@UniJSMethod(uiThread=true)publicvoidsendMessageToAndroid(JSONObjectparams,UniJSCallbackcallback){Stringtype=params.getString("type");Stringcontent=params.getString("content");android.util.Log.e("NativeBridge","type = "+type);android.util.Log.e("NativeBridge","content = "+content);if(callback!=null){JSONObjectresult=newJSONObject();result.put("code",0);result.put("msg","Android 已收到 UniApp 消息");result.put("receiveType",type);result.put("receiveContent",content);callback.invoke(result);}}/** * Android 主动发送事件给 UniApp */@UniJSMethod(uiThread=true)publicvoidtestSendEventToUniApp(){Map<String,Object>data=newHashMap<>();data.put("type","android_event");data.put("msg","这是 Android 主动发送给 UniApp 的消息");UniInstanceHolder.fireEvent("native_event",data);}@Overridepublicvoiddestroy(){UniInstanceHolder.clear(mUniSDKInstance);super.destroy();}}3. Android Activity 主动发送消息:NativeActivity.java
如果你是在 Activity 中拿到了数据,比如登录结果、支付结果、页面选择结果,可以这样通知 UniApp:
packagecom.xxx.uniplugin;importandroid.app.Activity;importandroid.os.Bundle;importandroid.view.View;importandroid.widget.Button;importjava.util.HashMap;importjava.util.Map;publicclassNativeActivityextendsActivity{@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);Buttonbutton=newButton(this);button.setText("发送数据给 UniApp");button.setOnClickListener(newView.OnClickListener(){@OverridepublicvoidonClick(Viewview){sendDataToUniApp();}});setContentView(button);}privatevoidsendDataToUniApp(){Map<String,Object>data=newHashMap<>();data.put("type","activity_result");data.put("token","abc123");data.put("userId","10001");UniInstanceHolder.fireEvent("native_event",data);}}4. 注册原生插件:dcloud_uniplugins.json
注意:name要和前端的uni.requireNativePlugin('NativeBridge')保持一致。
{"nativePlugins":[{"plugins":[{"type":"module","name":"NativeBridge","class":"com.xxx.uniplugin.NativeBridgeModule"}]}]}如果是自定义基座,需要重新打包自定义基座。
5. UniApp 页面使用
<template> <view class="page"> <button @click="sendToAndroid">UniApp 发消息给 Android</button> <button @click="androidSendToUniApp">测试 Android 发消息给 UniApp</button> <view class="title">结果:</view> <text>{{ result }}</text> </view> </template> <script> const globalEvent = uni.requireNativePlugin('globalEvent') const nativeBridge = uni.requireNativePlugin('NativeBridge') export default { data() { return { result: '' } }, onLoad() { // Android -> UniApp globalEvent.addEventListener('native_event', e => { console.log('收到 Android 主动消息:', JSON.stringify(e)) this.result = JSON.stringify(e) }) // 绑定当前页面对应的 mUniSDKInstance nativeBridge.bindUniInstance() }, onUnload() { nativeBridge.unbindUniInstance() }, methods: { // UniApp -> Android sendToAndroid() { nativeBridge.sendMessageToAndroid( { type: 'user_click', content: '用户点击了按钮' }, res => { console.log('Android 返回:', JSON.stringify(res)) this.result = JSON.stringify(res) } ) }, // 测试 Android -> UniApp androidSendToUniApp() { nativeBridge.testSendEventToUniApp() } } } </script>四、为什么uni.requireNativePlugin('globalEvent')不会创建 Module 实例?
因为globalEvent是 DCloud 内置事件监听模块,不是你自己注册的插件。
下面这句:
constglobalEvent=uni.requireNativePlugin('globalEvent')只是在 JS 侧拿到事件监听对象,用来:
globalEvent.addEventListener('native_event',callback)它不会创建你的:
NativeBridgeModule真正创建你自定义 Module 的是:
constnativeBridge=uni.requireNativePlugin('NativeBridge')前提是你已经在dcloud_uniplugins.json中注册:
{"type":"module","name":"NativeBridge","class":"com.xxx.uniplugin.NativeBridgeModule"}五、Activity 里能不能直接用mUniSDKInstance?
不能直接用。
因为 Activity 并不继承UniModule,所以它没有mUniSDKInstance。
错误理解:
publicclassNativeActivityextendsActivity{// 这里没有 mUniSDKInstance}正确做法:
UniModule 拿到 mUniSDKInstance ↓ 保存到 UniInstanceHolder ↓ Activity 通过 UniInstanceHolder.fireEvent(...) ↓ UniApp 通过 globalEvent 收到六、能不能用 EventBus?
可以。
但 EventBus 只能解决 Android 原生层内部通信,不能直接把数据发到 UniApp JS。
正确结构应该是:
Activity / SDK 回调 ↓ EventBus.post(...) ↓ NativeBridgeModule 接收事件 ↓ mUniSDKInstance.fireGlobalEventCallback(...) ↓ UniApp globalEvent 收到也就是说:
- EventBus 是 Android 内部消息通道
globalEvent才是 Android 通知 UniApp 的通道mUniSDKInstance仍然是最终发送事件的关键
如果业务是登录结果、支付结果这种“不允许丢”的数据,也可以用postSticky,或者更稳一点:原生先缓存,UniApp 页面加载后主动读取。
七、重要注意事项
1. 必须先监听,再发送
UniApp 页面必须先执行:
globalEvent.addEventListener('native_event',callback)然后 Android 再发送:
fireGlobalEventCallback("native_event",data)如果 Android 发送太早,事件会丢。
2. 必须先绑定mUniSDKInstance
页面加载后要调用:
nativeBridge.bindUniInstance()否则 Activity 通过UniInstanceHolder.fireEvent(...)时,没有可用的页面实例。
3.globalEvent只适合当前页面
globalEvent是通过当前页面的UniSDKInstance发送事件,所以更适合当前页面接收。
如果你的 App 有多个页面,或者页面切换频繁,需要特别注意:
- 当前保存的是不是最新页面实例
- 旧页面销毁时有没有清理
- 事件是否可能发给错误页面
4. 不要强引用UniSDKInstance
不推荐:
publicstaticUniSDKInstanceinstance;推荐:
privatestaticWeakReference<UniSDKInstance>instanceRef;并在destroy()中清理:
UniInstanceHolder.clear(mUniSDKInstance);5. 关键业务数据建议缓存后主动读取
如果是下面这些数据:
- 登录结果
- 支付结果
- SDK 初始化结果
- 广告归因结果
- 启动参数
不建议完全依赖globalEvent。
更稳的方式是:
Android 先缓存数据 ↓ UniApp 页面 onLoad / onShow ↓ 主动调用 nativeBridge.getPendingData(...) ↓ Android 通过 callback 返回数据因为globalEvent有监听时机问题,一旦页面没有准备好,事件就可能丢。
八、推荐使用场景
| 场景 | 推荐方案 |
|---|---|
| UniApp 主动调用原生能力 | NativeBridgeModule + UniJSCallback |
| Android 主动通知当前页面 | globalEvent + fireGlobalEventCallback |
| Activity 给 UniApp 发实时事件 | UniInstanceHolder + globalEvent |
| Service / SDK 回调通知页面 | EventBus + NativeBridgeModule + globalEvent |
| 登录、支付等不能丢的数据 | 原生缓存 + UniApp 主动读取 |
| 长时间监听定位、蓝牙、传感器 | UniDestroyableModule管理生命周期 |
九、最终结论
UniApp 安卓自定义基座里,原生与 UniApp 双向通信可以这样理解:
UniApp -> Android: 调用自定义 NativeBridgeModule 方法 Android -> UniApp: 通过 mUniSDKInstance.fireGlobalEventCallback 发送 globalEvent Activity 里没有 mUniSDKInstance: 需要通过 UniModule 绑定后间接使用最推荐的整体方案是:
UniApp 页面 addEventListener ↓ UniApp 调 bindUniInstance ↓ UniModule 保存当前 mUniSDKInstance ↓ Activity / SDK 回调通过 Holder 或 EventBus 通知 ↓ UniModule / Holder 调 fireGlobalEventCallback ↓ UniApp 收到事件如果只是实时通知,这套方案可以使用。
如果是关键业务结果,比如登录、支付、归因,建议增加一层缓存,让 UniApp 主动读取,避免事件丢失。
参考文档
- uni.requireNativePlugin 官方文档
- DCloud Android 原生插件开发文档
- DCloud UniMP Android 扩展开发文档