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

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

arduino 复制代码
private void runCreateReactContextOnNewThread(final ReactContextInitParams initParams) {
    setupReactContext(reactApplicationContext);
}

setupReactContext函数核心方法

  • catalystInstance.initialize:初始化
  • attachRootViewToInstance->reactRoot.runApplication:启动RN应用
  • ReactContext初始化完成监听回调,开发者可以监听这个事件做相关开发
scss 复制代码
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方法

typescript 复制代码
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应用

scss 复制代码
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对象
java 复制代码
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,所以这里会有一个黑科技

java 复制代码
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方法所需参数
less 复制代码
@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的作用就是将

scss 复制代码
catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);

转化为

arduino 复制代码
  mCatalystInstance.callFunction("AppRegistry", "runApplication", jsArgs);

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

arduino 复制代码
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

typescript 复制代码
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调用
c 复制代码
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了
ini 复制代码
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对象

ini 复制代码
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 调用队列 或 null callNativeModules(ret, true)

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

ini 复制代码
ret = callFunctionReturnFlushedQueue_->call(
              *runtime_,
              moduleId,
              methodId,
              valueFromDynamic(*runtime_, arguments));
        }

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

php 复制代码
__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队列中

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

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

css 复制代码
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 的调用都是一次捎带的机会。

arduino 复制代码
void JSIExecutor::callNativeModules(const Value& queue, bool isEndOfBatch) {
  ...
  delegate_->callNativeModules(
      *this, dynamicFromValue(*runtime_, queue), isEndOfBatch);
}
  • delegate_是JsToNativeBridge对象
  • nativeModules_:JSINativeModules对象,里面包含ModuleRegistry对象
  • moduleRegistry_:ModuleRegistry对象
arduino 复制代码
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的通信。

相关推荐
leory4 小时前
synchronized和ReentrantLock的区别是什么?各自的使用场景?
android·面试
__water5 小时前
【关于unity打包Android失败问题】
android·unity
重生之小比特6 小时前
【MySQL 数据库】基本查询
android·数据库·mysql
薛定猫AI6 小时前
【技术干货】用 AI + Expo 打通 iOS / Android / Web 跨端应用开发:从架构到代码生成实战
android·人工智能·ios
陌路206 小时前
第一行代码--初步学习--Android四大组件-activity1
android·学习
帅次8 小时前
状态 StateFlow、ViewModel 与 UI 收集
android·kotlin·gradle·android studio·android jetpack
匆忙拥挤repeat8 小时前
Android Compose 使用 CompositionLocal 将数据的作用域限定在局部
android
YF02118 小时前
Android 权限系统的演变与深度治理
android·app
左小左8 小时前
🔥🔥🔥 我用AI基于 Tauri + Vue 3 写了个 ADB 桌面工具,把命令行的脏活全干了
android·vue.js·rust