Demo太简单。现给它扩展为 多 Package + 原生 / RN 互调 + EventEmitter 事件通知。
1、创建Toast原生模块,测试RN调用原生应用。代码如下:
Kotlin
package com.example.androidapp.module
import android.widget.Toast
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
// RN调用原生Toast的模块
class ToastModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
// 必须重写:RN中调用该模块的名称(比如RN中用NativeModules.ToastModule)
override fun getName(): String {
return "ToastModule"
}
// ReactMethod注解:暴露给RN调用的方法(必须是void返回,参数支持基本类型/字符串)
@ReactMethod
fun showToast(message: String, duration: Int) {
// 在主线程执行UI操作
reactApplicationContext.runOnUiQueueThread {
Toast.makeText(reactApplicationContext, message, duration).show()
}
}
}
2、创建EventEmitter事件模块(原生->RN通知):
Kotlin
package com.example.androidapp.module
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.modules.core.DeviceEventManagerModule
// 原生给RN发送事件通知的模块
class EventEmitterModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
override fun getName(): String {
return "EventEmitterModule"
}
// 暴露给RN的方法:启动原生定时发送事件
@ReactMethod
fun startSendEvent() {
// 模拟原生每隔2秒给RN发一次消息
Thread {
for (i in 1..5) {
Thread.sleep(2000)
// 发送事件:事件名「NativeEvent」,携带参数「message」
sendEvent("NativeEvent", mapOf("message" to "原生发送的第${i}条消息"))
}
}.start()
}
// 核心方法:发送事件到RN
private fun sendEvent(eventName: String, params: Map<String, String>) {
reactApplicationContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
.emit(eventName, params)
}
}
3、新建自定义package(注册上述俩个模块):
Kotlin
package com.example.androidapp.rnpkg
import com.example.androidapp.module.EventEmitterModule
import com.example.androidapp.module.ToastModule
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager
import java.util.ArrayList
// 自定义Package:注册所有自定义原生模块
class CustomReactPackage : ReactPackage {
// 注册原生模块(方法/事件类)
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
val modules = ArrayList<NativeModule>()
modules.add(ToastModule(reactContext)) // 注册Toast模块
modules.add(EventEmitterModule(reactContext)) // 注册EventEmitter模块
return modules
}
// 注册自定义原生UI组件(本次demo用不到,返回空列表)
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
return emptyList()
}
}
4、修改MainApplication(注册多个package)
Kotlin
package com.example.androidapp // 替换为你的实际包名
import android.app.Application
import com.example.androidapp.rnpkg.CustomReactPackage
import com.facebook.react.ReactApplication
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactPackage
import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.react.shell.MainReactPackage
import com.facebook.soloader.SoLoader
import java.util.ArrayList
class MainApplication : Application(), ReactApplication {
override val reactNativeHost: ReactNativeHost = object : DefaultReactNativeHost(this) {
override fun getPackages(): List<ReactPackage> {
val packages = ArrayList<ReactPackage>()
packages.add(MainReactPackage()) // 核心Package(必传)
packages.add(CustomReactPackage()) // 自定义Package(新增)
return packages
}
override fun getJSMainModuleName(): String {
return "index"
}
override fun getUseDeveloperSupport(): Boolean {
return true
}
override val isNewArchEnabled: Boolean
get() = false
override val isHermesEnabled: Boolean
get() = true
override fun getBundleAssetName(): String {
return "index.android.bundle"
}
}
override fun onCreate() {
super.onCreate()
SoLoader.init(this, false)
}
}
5、修改RN代码(App.tsx),实现双向通信
javascript
import React, { useEffect, useState } from 'react';
import { View, Text, TouchableOpacity, NativeModules, DeviceEventEmitter, StyleSheet } from 'react-native';
// 1. 获取原生模块(和原生模块的getName()对应)
const { ToastModule, EventEmitterModule } = NativeModules;
const App = () => {
const [eventMessages, setEventMessages] = useState<string[]>([]); // 存储原生发送的事件
// 2. RN→原生:调用原生Toast方法
const callNativeToast = () => {
// 调用原生ToastModule的showToast方法(参数对应原生方法)
ToastModule.showToast("RN调用原生Toast成功! \n 吕布: \"我被酒色所伤,竟如此憔悴,从今天开始戒酒!\"", 1); // 1=Toast.LENGTH_LONG
};
// 3. 监听原生发送的事件(EventEmitter)
useEffect(() => {
// 启动原生定时发送事件
EventEmitterModule.startSendEvent();
// 监听原生事件:事件名「NativeEvent」(和原生sendEvent的eventName一致)
const eventListener = DeviceEventEmitter.addListener('NativeEvent', (params) => {
console.log("收到原生事件:", params);
setEventMessages(prev => [...prev, params.message]);
});
// 组件卸载时移除监听
return () => {
eventListener.remove();
};
}, []);
return (
<View style={styles.container}>
<Text style={styles.title}>RN-原生互调 Demo</Text>
{/* RN→原生按钮 */}
<TouchableOpacity style={styles.button} onPress={callNativeToast}>
<Text style={styles.buttonText}>点击调用原生Toast</Text>
</TouchableOpacity>
{/* 显示原生发送的事件 */}
<Text style={styles.subTitle}>原生发送的事件:</Text>
{eventMessages.map((msg, index) => (
<Text key={index} style={styles.message}>{msg}</Text>
))}
</View>
);
};
// 样式
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f5f5f5',
padding: 20,
},
title: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 30,
},
button: {
backgroundColor: '#2196F3',
padding: 10,
borderRadius: 5,
marginBottom: 30,
},
buttonText: {
color: 'white',
fontSize: 16,
},
subTitle: {
fontSize: 16,
fontWeight: 'bold',
alignSelf: 'flex-start',
marginBottom: 10,
},
message: {
fontSize: 14,
color: '#666',
alignSelf: 'flex-start',
marginBottom: 5,
},
});
export default App;
说明,const [eventMessages, setEventMessages] = useState<string[]>([]); 这句代码,eventMessages是一个字符串数组,setEventMessages是设置该字符串数组的函数。当通过该函数修改字符串数组,会自动触发页面刷新。userState函数如下:

6、重新打包运行
cd /d E:\android\projects\RNDemo4\RNApp\RNApp
npx react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res/

再把 RN 工程android/app/src/main/assets/index.android.bundle复制到安卓工程app/src/main/assets/。
重新运行原生安卓工程。 崩溃了,日志:

报错位置:

原因是参数类型错误,事件参数用了mapOf("message" to "xxx")(Java 的SingletonMap),RN 桥接无法直接识别,须用 RN 提供的WritableMap。
修改这个类的代码:
Kotlin
package com.example.androidapp.module
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.WritableMap
import com.facebook.react.bridge.WritableNativeMap
import com.facebook.react.modules.core.DeviceEventManagerModule
// 原生给RN发送事件通知的模块
class EventEmitterModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
override fun getName(): String {
return "EventEmitterModule"
}
// 暴露给RN的方法:启动原生定时发送事件
@ReactMethod
fun startSendEvent() {
// 模拟原生每隔2秒给RN发一次消息
Thread {
for (i in 1..5) {
Thread.sleep(2000)
// 发送事件:事件名「NativeEvent」,携带参数「message」
val params = WritableNativeMap()
params.putString("message", "原生发送的第${i}条消息")
sendEvent("NativeEvent", params)
}
}.start()
}
// 核心方法:发送事件到RN
private fun sendEvent(eventName: String, params: WritableMap) { // 参数改为WritableMap
reactApplicationContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
.emit(eventName, params)
}
}
再次运行,按钮下面依次显示了5条消息:

ok. 点击按钮,显示了Toast消息:

ok. 虽然运行没问题,但是有个地方需要修复下,在子线程给RN发通知时,需要通过RN的桥接线程发送事件,而不是直接在子线程调用,再贴下EventEmitterModule代码:
Kotlin
package com.example.androidapp.module
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.WritableMap
import com.facebook.react.bridge.WritableNativeMap
import com.facebook.react.modules.core.DeviceEventManagerModule
// 原生给RN发送事件通知的模块
class EventEmitterModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
override fun getName(): String {
return "EventEmitterModule"
}
// 暴露给RN的方法:启动原生定时发送事件
@ReactMethod
fun startSendEvent() {
// 模拟原生每隔2秒给RN发一次消息
Thread {
for (i in 1..5) {
Thread.sleep(2000)
reactApplicationContext.runOnNativeModulesQueueThread { // 通过RN的桥接线程发送事件(而非直接在子线程调用)
// 发送事件:事件名「NativeEvent」,携带参数「message」
val params = WritableNativeMap() // 使用RN的WritableMap构建参数(而非普通Map)
params.putString("message", "原生发送的第${i}条消息")
sendEvent("NativeEvent", params)
}
}
}.start()
}
// 核心方法:发送事件到RN
private fun sendEvent(eventName: String, params: WritableMap) { // 参数改为WritableMap
reactApplicationContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
.emit(eventName, params)
}
}
ok.