ReactNative 源码分析5——ReactActivity之启动RN应用

39 阅读6分钟

ReactNative 源码分析1——HybridData 机制深度分析

ReactNative 源码分析2——Application初始化

ReactNative 源码分析3——ReactActivity之初始化RN应用

ReactNative 源码分析4——ReactActivity之加载JSBundle

ReactNative 源码分析5——ReactActivity之启动RN应用

ReactNative 源码分析6——Native Modules通信机制

ReactNative 源码分析7——Native Modules通信机制 之Js调用Java

ReactNative 源码分析8——Native Modules通信机制 之Java调用Js

ReactNative 源码分析9——Native View初始化

ReactNative 源码分析10——Native View创建流程createView

ReactNative 源码分析11——Native View创建流程setChildren和manageChildren

ReactNative 源码分析12——Native View创建流程onBatchComplete

接着上篇我们继续分析setupReactContext方法

private void runCreateReactContextOnNewThread(final ReactContextInitParams initParams) {
    setupReactContext(reactApplicationContext);
}

setupReactContext函数核心方法

  • catalystInstance.initialize:初始化
  • attachRootViewToInstance->reactRoot.runApplication:启动RN应用
  • ReactContext初始化完成监听回调,开发者可以监听这个事件做相关开发
private void setupReactContext(final ReactApplicationContext reactContext) {
  synchronized (mAttachedReactRoots) {
    synchronized (mReactContextLock) {
      mCurrentReactContext = Assertions.assertNotNull(reactContext);
    }

    CatalystInstance catalystInstance =
        Assertions.assertNotNull(reactContext.getCatalystInstance());

    catalystInstance.initialize();

    ...

    for (ReactRoot reactRoot : mAttachedReactRoots) {
      attachRootViewToInstance(reactRoot);
    }
    ReactMarker.logMarker(ATTACH_MEASURED_ROOT_VIEWS_END);
  }
  
  ...

  com.facebook.react.ReactInstanceEventListener[] listeners =
      new com.facebook.react.ReactInstanceEventListener[mReactInstanceEventListeners.size()];
  final com.facebook.react.ReactInstanceEventListener[] finalListeners =
      mReactInstanceEventListeners.toArray(listeners);

  UiThreadUtil.runOnUiThread(
      () -> {
        moveReactContextToCurrentLifecycleState();

        for (com.facebook.react.ReactInstanceEventListener listener : finalListeners) {
          // Sometimes this listener is null - probably due to race
          // condition between allocating listeners with a certain
          // size, and getting a `final` version of the array on
          // the following line.
          if (listener != null) {
            listener.onReactContextInitialized(reactContext);
          }
        }
      });
}

在NativeModules线程上调用每一个NativeModule的initialize方法

public void initialize() {
  mInitialized = true;
  mNativeModulesQueueThread.runOnQueue(
      () -> {
        mNativeModuleRegistry.notifyJSInstanceInitialized();
      });
}

private void doInitialize(NativeModule module) {
    ...
    if (shouldInitialize) {
      module.initialize();
      // Once finished, set flags accordingly, but we don't expect anyone to wait for this to
      // finish
      // So no need to notify other threads
      synchronized (this) {
        mIsInitializing = false;
      }
    }
    ...
}

attachRootViewToInstance方法中会启动RN应用

private void attachRootViewToInstance(final ReactRoot reactRoot) { 
  ...
  @Nullable Bundle initialProperties = reactRoot.getAppProperties();

  final int rootTag;
  if (reactRoot.getUIManagerType() == FABRIC) {
    rootTag =
        uiManager.startSurface(
            reactRoot.getRootViewGroup(),
            reactRoot.getJSModuleName(),
            initialProperties == null
                ? new WritableNativeMap()
                : Arguments.fromBundle(initialProperties),
            reactRoot.getWidthMeasureSpec(),
            reactRoot.getHeightMeasureSpec());
    reactRoot.setShouldLogContentAppeared(true);
  } else {
    rootTag =
        uiManager.addRootView(
            reactRoot.getRootViewGroup(),
            initialProperties == null
                ? new WritableNativeMap()
                : Arguments.fromBundle(initialProperties));
    reactRoot.setRootViewTag(rootTag);
    reactRoot.runApplication();
  }
  ...
}

我们这里看不是FABRIC,它是新架构的 渲染器和组件,因此我们看reactRoot.runApplication

  • 启动时可以传入启动参数
  • 调用getJSModule
  • mJSModuleRegistry:JavaScriptModuleRegistry对象
public void runApplication() {
  Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "ReactRootView.runApplication" );
  try {
    ...
    WritableNativeMap appParams = new WritableNativeMap();
    appParams.putDouble( "rootTag" , getRootViewTag());
    @Nullable Bundle appProperties = getAppProperties();
    if (appProperties != null) {
      appParams.putMap( "initialProps" , Arguments.fromBundle(appProperties));
    }

    mShouldLogContentAppeared = true;

    catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);
  } finally {
    Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
  }
}

@Override
public <T extends JavaScriptModule> T getJSModule(Class<T> jsInterface) {
  return mJSModuleRegistry.getJavaScriptModule(this, jsInterface);
}

JavaScriptModuleRegistry核心能力:Java层调用JS中指定的module中方法,这里的意图是希望调用JS中的AppRegistry.runApplication(appParams),但是Java层是无法直接调用JS,所以这里会有一个黑科技

public synchronized <T extends JavaScriptModule> T getJavaScriptModule(
    CatalystInstance instance, Class<T> moduleInterface) {
  JavaScriptModule module = mModuleInstances.get(moduleInterface);
  if (module != null) {
    return (T) module;
  }

  JavaScriptModule interfaceProxy =
      (JavaScriptModule)
          Proxy.newProxyInstance(
              moduleInterface.getClassLoader(),
              new Class[] {moduleInterface},
              new JavaScriptModuleInvocationHandler(instance, moduleInterface));
  mModuleInstances.put(moduleInterface, interfaceProxy);
  return (T) interfaceProxy;
}

getJSModule返回的是JavaScriptModule的动态代理,当执行runApplication调用时,会触发执行JavaScriptModuleInvocationHandler.invoke

  • getJSModuleName:返回字符串“AppRegistry”
  • method.getName:返回字符串"runApplication"
  • jsArgs:调用runApplication方法所需参数
@Override
public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
    throws Throwable {
  NativeArray jsArgs = args != null ? Arguments.fromJavaArgs(args) : new WritableNativeArray();
  mCatalystInstance.callFunction(getJSModuleName(), method.getName(), jsArgs);
  return null;
}

所以JavaScriptModuleRegistry的作用就是将

catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);

转化为

  mCatalystInstance.callFunction("AppRegistry", "runApplication", jsArgs);

mCatalystInstance.callFunction最终会调用到C++层CatalystInstanceImpl对应的jniCallJSFunction方法,由C++实现JS模块的调用

private native void jniCallJSFunction(String module, String method, NativeArray arguments);

//jniCallJSFunction进一步调用
instance_->callJSFunction(std::move(module), std::move(method), arguments->consume());

//callJSFunction调用
nativeToJsBridge_->callFunction(std::move(module), std::move(method), std::move(params));

executor是JSIExecutor对象,会调用到JSIExecutor.callFunction

void NativeToJsBridge::callFunction(
    std::string&& module,
    std::string&& method,
    folly::dynamic&& arguments) {
     ...
     executor->callFunction(module, method, arguments);
  });
}

callFunction主要完成 3 件事:

  • bindBridge:绑定JS中的全局函数,这样C++就可以调用JS了
  • callFunctionReturnFlushedQueue_->call:调用JS中指定模块和方法
  • callNativeModules:JS执行会返回Native 调用队列,然后调用callNativeModules执行Native调用
void JSIExecutor::callFunction(
    const std::string& moduleId,
    const std::string& methodId,
    const folly::dynamic& arguments) {
  SystraceSection s(
      "JSIExecutor::callFunction" , "moduleId" , moduleId, "methodId" , methodId);
  if (!callFunctionReturnFlushedQueue_) {
    bindBridge();
  }

  auto errorProducer = [=] {
    std::stringstream ss;
    ss << "moduleID: " << moduleId << " methodID: " << methodId;
    return ss.str();
  };

  Value ret = Value::undefined();
  try {
    scopedTimeoutInvoker_(
        [&] {
          ret = callFunctionReturnFlushedQueue_->call(
              *runtime_,
              moduleId,
              methodId,
              valueFromDynamic(*runtime_, arguments));
        },
        std::move(errorProducer));
  } catch (...) {
    std::throw_with_nested(
        std::runtime_error( "Error calling " + moduleId + "." + methodId));
  }

  callNativeModules(ret, true);
}

如果是第一次调用会调用bindBridge

  • 通过runtime_查找到JS中的全局变量 __fbBatchedBridge
  • 然后获取到JS中如下三个方法,有了这3 个方法就能实现C++调用JS了
void JSIExecutor::bindBridge() {
  std::call_once(bindFlag_, [this] {
    SystraceSection s( "JSIExecutor::bindBridge (once)" );
    Value batchedBridgeValue =
        runtime_->global().getProperty(*runtime_, "__fbBatchedBridge" );
    if (batchedBridgeValue.isUndefined() || !batchedBridgeValue.isObject()) {
      throw JSINativeException(
          "Could not get BatchedBridge, make sure your bundle is packaged correctly" );
    }

    Object batchedBridge = batchedBridgeValue.asObject(*runtime_);
    callFunctionReturnFlushedQueue_ = batchedBridge.getPropertyAsFunction(
        *runtime_, "callFunctionReturnFlushedQueue" );
    invokeCallbackAndReturnFlushedQueue_ = batchedBridge.getPropertyAsFunction(
        *runtime_, "invokeCallbackAndReturnFlushedQueue" );
    flushedQueue_ =
        batchedBridge.getPropertyAsFunction(*runtime_, "flushedQueue" );
  });
}

__fbBatchedBridge位于BatchedBridge.js文件中,可以看到它其实就是MessageQueue对象

const MessageQueue = require( './MessageQueue' );

const BatchedBridge: MessageQueue = new MessageQueue();
Object.defineProperty(global, '__fbBatchedBridge' , {
  configurable: true,
  value: BatchedBridge,
});

module.exports = BatchedBridge;

MessageQueue对象中这三个方法的核心模式一致:被C++层调用,在 JS 侧执行某些操作后,把执行期间产生的 Native 调用队列批量返回给 C++ 侧处理,形成了 Native → JS(其中可能会调用Native API) → Native 的完整调用闭环

方法JS 签名触发场景JS 侧执行返回值C++ 对返回值的处理
callFunctionReturnFlushedQueue(module, method, args)Native 调用 JS 模块方法__callFunction(module, method, args)积压的 Native 调用队列callNativeModules(ret, true)
invokeCallbackAndReturnFlushedQueue(cbID, args)Native 触发 JS 回调(如 Promise resolve/reject)__invokeCallback(cbID, args)积压的 Native 调用队列callNativeModules(ret, true)
flushedQueue()无需执行 JS 函数,仅刷新积压队列__callReactNativeMicrotasks()积压的 Native 调用队列 或 nullcallNativeModules(ret, true)

继续回到,这里我们执行的moduleId="AppRegistry",methodId="runApplication"

ret = callFunctionReturnFlushedQueue_->call(
              *runtime_,
              moduleId,
              methodId,
              valueFromDynamic(*runtime_, arguments));
        }

callFunctionReturnFlushedQueue会调用到__callFunction(module, method, args)

__callFunction(module: string, method: string, args: mixed[]): void {
  try {
    //查找模块
    const moduleMethods = this.getCallableModule(module);
    ...
    //调用模块对应方法
    moduleMethods[method].apply(moduleMethods, args);
  } finally {
    Systrace.endEvent();
  }
}

AppRegistry模块位于AppRegistry.js,它里面有runApplication的实现,看到这里我们就不继续分析,核心流程基本分析完成。

这里有个有意思的问题callFunctionReturnFlushedQueue_为什么返回Native调用队列?然后又为什么调用callNativeModules?这是因为旧架构的原因:在旧架构(Bridge 模式)下,JS 通过队列调 Native 都是异步的,所有调用是被放到_queue队列中

JS 调用 NativeModule.method(args)
  → enqueueNativeCall() 存入 _queue
  → 等下次 Native 来取(或 nativeFlushQueueImmediate 主动触发)
  → Native 执行完通过 invokeCallback 回调 JS
  ❌ JS 无法同步拿到返回值

队列方式下,JS 无法主动通知 Native 来取队列,只能等 Native 下次调用 JS 时,把积压的队列"捎带"回去。

t1: Native 调用 JS → callFunctionReturnFlushedQueue("AppRegistry", "runApplication", args)
    │
    ├── JS 执行 runApplication
    │   ├── 内部调了 UIManager.createView(...)    → 存入 _queue
    │   ├── 内部调了 UIManager.setChildren(...)   → 存入 _queue
    │   └── 内部调了 AsyncStorage.setItem(...)    → 存入 _queue
    │
    └── JS 执行完毕,调用 flushedQueue()
        ├── 取出 _queue
        ├── 清空 _queue
        └── return queue  ←── 捎带给 Native

t2: C++ 拿到返回的 queue → callNativeModules(queue) → 逐一执行 Native 调用

返回队列不是"还有任务没做完",而是队列方式下 JS → Native 的唯一传回通道 — JS 把调用信息打包,借着 Native 调 JS 的机会"顺路"带回去。这也是为什么三个方法都以 ReturnFlushedQueueflushedQueue 结尾 — 每一次 Native → JS 的调用都是一次捎带的机会。

void JSIExecutor::callNativeModules(const Value& queue, bool isEndOfBatch) {
  ...
  delegate_->callNativeModules(
      *this, dynamicFromValue(*runtime_, queue), isEndOfBatch);
}
  • delegate_是JsToNativeBridge对象
  • nativeModules_:JSINativeModules对象,里面包含ModuleRegistry对象
  • moduleRegistry_:ModuleRegistry对象
void callNativeModules(
    [[maybe_unused]] JSExecutor& executor,
    folly::dynamic&& calls,
    bool isEndOfBatch) override {
  ...
  for (auto& call : methodCalls) {
    m_registry->callNativeMethod(
        call.moduleId, call.methodId, std::move(call.arguments), call.callId);
  }
  ...
}

callNativeModules中最终会调用到Java层对应的NativeMethod,这里我们不分析了,后面我们会详细分析Java与JS的通信。