###0.前言
开发环境:windows10 + vscode + react-native 0.57 + Android模拟器
本文主要记录原生项目与react-native的交互方式和注意项。(Android方向)
交互方式总共2种:
- react-native主动调用native
- native主动调用react-native
###1.react-native主动调用native
react-native调用native主要是通过native端代码
向app的ReactNativeHost实例
中注入特定格式的原生模块,从而使ReactNativeHost具备该模块实例。即react-native端可以通过NativeModules模块去获取注入的原生模块。
####1.1 具体实现流程
- Android端注入原生模块
- 自定义
NativeModule
(继承ReactContextBaseJavaModule
) - 自定义
ReactPackage
(实现ReactPackage
接口) NativeModule
添加入ReactPackage
(添加入createNativeModules
的List<NativeModule>中
)ReactPackage
注入ReactNativeHost
- 自定义
- react-native调用原生模块
调用方式:
NativeModules.[原生模块名称].[模块方法或者参数名];- 原生模块名称:对应原生端
自定义NativeModule
的getName()
- 模块方法:对应原生端
自定义NativeModule
带@ReactMethod
注解的方法 - 参数名:对应原生端
自定义NativeModule
的getConstants()
- 原生模块名称:对应原生端
注意事项
- 原生模块的方法是异步,只能通过回调返回(private/public均可以调起)
参数类型限制(没有java中的Long类型)
Boolean -> Bool Integer -> Number Double -> Number Float -> Number String -> String Callback -> function ReadableMap -> Object ReadableArray -> Array
###2.native主动调用react-native
Native核心代码:
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params);
- 通过reactContext对象获取RCTDeviceEventEmitter模块,调用emit(eventName, params)方法传递数据到react-native端。
ReactNative核心代码:
componentWillMount() {
DeviceEventEmitter.addListener(eventName, (params) => {
...//执行事件
});
}
- 通过DeviceEventEmitter组件添加[eventName]的监听,接收参数[params]
###2.1注意事项
native端的reactContext实例的获取
reactContext
是通过runCreateReactContextOnNewThread(final ReactContextInitParams initParams)
方法实例化的。//ReactInstanceManager类 private void runCreateReactContextOnNewThread(final ReactContextInitParams initParams) { //省略部分代码... mCreateReactContextThread = new Thread( new Runnable() { @Override public void run() { //省略部分代码... try { //省略部分代码... mCreateReactContextThread = null; ReactMarker.logMarker(PRE_SETUP_REACT_CONTEXT_START); final Runnable maybeRecreateReactContextRunnable = new Runnable() { @Override public void run() { if (mPendingReactContextInitParams != null) {//重新创建 runCreateReactContextOnNewThread(mPendingReactContextInitParams); mPendingReactContextInitParams = null; } } }; Runnable setupReactContextRunnable = new Runnable() { @Override public void run() { try { setupReactContext(reactApplicationContext);//设置ReactContext } catch (Exception e) { mDevSupportManager.handleException(e); } } }; reactApplicationContext.runOnNativeModulesQueueThread(setupReactContextRunnable); UiThreadUtil.runOnUiThread(maybeRecreateReactContextRunnable); } catch (Exception e) { mDevSupportManager.handleException(e); } } }); ReactMarker.logMarker(REACT_CONTEXT_THREAD_START); mCreateReactContextThread.start(); }
//ReactQueueConfigurationSpec类
public static ReactQueueConfigurationSpec createDefault() {
MessageQueueThreadSpec spec = Build.VERSION.SDK_INT < 21 ?
MessageQueueThreadSpec.newBackgroundThreadSpec("native_modules", LEGACY_STACK_SIZE_BYTES) :
MessageQueueThreadSpec.newBackgroundThreadSpec("native_modules");
return builder()
.setJSQueueThreadSpec(MessageQueueThreadSpec.newBackgroundThreadSpec("js"))
.setNativeModulesQueueThreadSpec(spec)
.build();
}
从源码可以知道reactContext的实例化是通过native_modules
这个后台消息队列的子线程执行的。即reactContext的获取为异步操作。
异步导致的问题:
- ReactActivity中获取getReactInstanceManager().getCurrentReactContext()为空
解决方法:
getWindow().getDecorView().post(new Runnable() {
@Override
public void run() {
if(getReactInstanceManager().getCurrentReactContext() == null ){
if(!isFinishing())
getWindow().getDecorView().postDelayed(this,1000);
}else{
WindowModule.sendWindowInfo(getReactInstanceManager().getCurrentReactContext());
}
}
});
通过消息队列进行轮询,判断reactContext非空后调用
###3.示例代码
END
–Nowy
–2018.11.12