react-native实践记录(03)

###0.前言

开发环境:windows10 + vscode + react-native 0.57 + Android模拟器

本文主要记录原生项目与react-native的交互方式和注意项。(Android方向)

交互方式总共2种:

  1. react-native主动调用native
  2. native主动调用react-native

###1.react-native主动调用native
react-native调用native主要是通过native端代码向app的ReactNativeHost实例中注入特定格式的原生模块,从而使ReactNativeHost具备该模块实例。即react-native端可以通过NativeModules模块去获取注入的原生模块。
react-native调用native层方法图

####1.1 具体实现流程

  1. Android端注入原生模块
    1. 自定义NativeModule(继承ReactContextBaseJavaModule)
    2. 自定义ReactPackage(实现ReactPackage接口)
    3. NativeModule添加入ReactPackage(添加入createNativeModulesList<NativeModule>中)
    4. ReactPackage注入ReactNativeHost
  1. react-native调用原生模块

    调用方式:
    NativeModules.[原生模块名称].[模块方法或者参数名];
    • 原生模块名称:对应原生端自定义NativeModulegetName()
    • 模块方法:对应原生端自定义NativeModule@ReactMethod注解的方法
    • 参数名:对应原生端自定义NativeModulegetConstants()
  2. 注意事项

    1. 原生模块的方法是异步,只能通过回调返回(private/public均可以调起)
    2. 参数类型限制(没有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注意事项

  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的获取为异步操作

异步导致的问题:

  1. 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.示例代码

  1. React-Native中文网#原生模块
  2. 本文测试项目代码

END

–Nowy

–2018.11.12

分享到