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的,是你自己写的UniModuleUniDestroyableModule


一、先理解几个核心概念

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是哪里来的?

mUniSDKInstanceUniModule父类提供的当前页面实例。

只有你的类继承了:

UniModule

或:

UniDestroyableModule

才可以直接使用:

mUniSDKInstance

例如:

publicclassNativeBridgeModuleextendsUniModule{@UniJSMethod(uiThread=true)publicvoidsendEvent(){mUniSDKInstance.fireGlobalEventCallback("native_event",params);}}

如果代码写在下面这些地方,是没有mUniSDKInstance的:

ActivityApplicationServiceBroadcastReceiver普通工具类SDK回调类

这也是很多人找不到mUniSDKInstance的原因。


3.UniModuleUniDestroyableModule区别

作用适合场景
UniModule普通原生 Module一次性调用、获取数据、打开页面
UniDestroyableModule带销毁生命周期的 ModuleEventBus 注册、监听器、定位、蓝牙、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)

三、完整代码示例

下面示例实现两个方向:

  1. UniApp 主动给 Android 发送消息
  2. 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 扩展开发文档