目录

RN 和 fbjni

前言

最近帮 RN 业务方查内存泄漏问题,发现了 fbjni 这个库挺有意思的

正好借助 RN 源码例子,讲述一下 fbjni 是如何控制 java 和 c++ 层对象的生命周期的

源码阅读

下面会贴一些 RN 中的源码 和 fbjni 的源码,不用关心 RN 源码中的对象是做什么的,我们这里只关注对象如何被创建和如何被释放的

// react/jni/CatalystInstanceImpl.h
class CatalystInstanceImpl : public jni::HybridClass<CatalystInstanceImpl> {
 public:
  static constexpr auto kJavaDescriptor =
      "Lcom/facebook/react/bridge/CatalystInstanceImpl;";
 
  static jni::local_ref<jhybriddata> initHybrid(
      jni::alias_ref<jclass>,
      bool enableRuntimeScheduler,
      bool enableRuntimeSchedulerInTurboModule);
// 略
}
// react/jni/CatalystInstanceImpl.cpp
jni::local_ref<CatalystInstanceImpl::jhybriddata>
CatalystInstanceImpl::initHybrid
    jni::alias_ref<jclass>,
    bool enableRuntimeScheduler,
    bool enableRuntimeSchedulerInTurboModule) {
    return makeCxxInstance(
      enableRuntimeScheduler, enableRuntimeSchedulerInTurboModule);
}
 
void CatalystInstanceImpl::initializeBridge(
    jni::alias_ref<ReactCallback::javaobject> callback,
    // This executor is actually a factory holder.
    JavaScriptExecutorHolder *jseh,
    jni::alias_ref<JavaMessageQueueThread::javaobject> jsQueue,
    jni::alias_ref<JavaMessageQueueThread::javaobject> nativeModulesQueue,
    jni::alias_ref<jni::JCollection<JavaModuleWrapper::javaobject>::javaobject>
        javaModules,
    jni::alias_ref<jni::JCollection<ModuleHolder::javaobject>::javaobject>
        cxxModules) {
  set_react_native_logfunc(&log);
 
  moduleRegistry_ = std::make_shared<ModuleRegistry>(buildNativeModuleList(
      std::weak_ptr<Instance>(instance_),
      javaModules,
      cxxModules,
      moduleMessageQueue_));
 
  instance_->initializeBridge(
      std::make_unique<JInstanceCallback>(callback, moduleMessageQueue_),
      jseh->getExecutorFactory(),
      std::make_unique<JMessageQueueThread>(jsQueue),
      moduleRegistry_);
}
 
CatalystInstanceImpl::CatalystInstanceImpl(
    bool enableRuntimeScheduler,
    bool enableRuntimeSchedulerInTurboModule)
    : instance_(std::make_unique<Instance>()),
      enableRuntimeScheduler_(enableRuntimeScheduler),
      enableRuntimeSchedulerInTurboModule_(
          enableRuntimeScheduler && enableRuntimeSchedulerInTurboModule) {}
// com.facebook.react.bridge.CatalystInstanceImpl
private CatalystInstanceImpl(
      final ReactQueueConfigurationSpec reactQueueConfigurationSpec,
      final JavaScriptExecutor jsExecutor,
      final NativeModuleRegistry nativeModuleRegistry,
      final JSBundleLoader jsBundleLoader,
      NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
    FLog.d(ReactConstants.TAG, "Initializing React Xplat Bridge.");
    Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstanceImpl");
 
    if (ReactFeatureFlags.enableRuntimeSchedulerInTurboModule
        && !ReactFeatureFlags.enableRuntimeScheduler) {
      Assertions.assertUnreachable();
    }
    // jni 调用
    mHybridData =
        initHybrid(
            ReactFeatureFlags.enableRuntimeScheduler,
            ReactFeatureFlags.enableRuntimeSchedulerInTurboModule);
 
    mReactQueueConfiguration =
        ReactQueueConfigurationImpl.create(
            reactQueueConfigurationSpec, new NativeExceptionHandler());
    mBridgeIdleListeners = new CopyOnWriteArrayList<>();
    mNativeModuleRegistry = nativeModuleRegistry;
    mJSModuleRegistry = new JavaScriptModuleRegistry();
    mJSBundleLoader = jsBundleLoader;
    mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;
    mNativeModulesQueueThread = mReactQueueConfiguration.getNativeModulesQueueThread();
    mTraceListener = new JSProfilerTraceListener(this);
    Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
 
    FLog.d(ReactConstants.TAG, "Initializing React Xplat Bridge before initializeBridge");
    Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "initializeCxxBridge");
 
    if (ReactFeatureFlags.warnOnLegacyNativeModuleSystemUse) {
      warnOnLegacyNativeModuleSystemUse();
    }
    // jni 调用
    initializeBridge(
        new BridgeCallback(this),
        jsExecutor,
        mReactQueueConfiguration.getJSQueueThread(),
        mNativeModulesQueueThread,
        mNativeModuleRegistry.getJavaModules(this),
        mNativeModuleRegistry.getCxxModules());
    FLog.d(ReactConstants.TAG, "Initializing React Xplat Bridge after initializeBridge");
    Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
 
    mJavaScriptContextHolder = new JavaScriptContextHolder(getJavaScriptContext());
  }

我们会发现 java 和 cpp 层都有对象叫做 CatalystInstance,且 cpp 层依靠 java 层通知实现释放,那么具体是怎么实现的呢?

首先有几个点:

  1. c++ 层 initializeBridge 调用的时候,native 层的 instance_ 已经被使用了,说明已经创建好了
  2. 基于1,说明是 initHybrid 被调用的时候,创建了 c++ 对象
  3. 基于2,搜索源码你会发现 cpp 层 没有地方显示的调用 了 new CatalystanceImpl() 这种构造函数
  4. c++ 层,initHybrid 方法返回了很多不熟悉的东西,比如 local_ref
  5. c++ 的 CatalystInstance 继承了 HybridClass

我们可以先看 java 代码,此处为 CatalystInstanceImpl 构造函数,内部进行了两次 jni 调用。

那么此时,cpp 层的 CatalystInstance 没有意外的话,肯定就是这两次 jni 调用的过程中创建的。

但是当我们搜索 cpp CatalystInstanceImpl 的构造函数调用时,发现其并没有调用。

我们细看 cpp 层的 initHybrid 方法,它只是简单调用了一下 makeCxxInstance 结束了。

一切的魔法就在这个 makeCxxInstance 方法中,流程如下:

initHybrid 触发 makeCxxInstance 进而

  1. Hybrid.h new T=CatalystInstance 创建 c++ CatalystInstance 对象,绑定 instance_

  2. HybridData::create()
    此方法创建 java 层的 HybridData 对象,并完成绑定
    2.1 第一行调用 CoreClasses-inl.h newInstance
    fbjni 的封装,通过 newObject 创建对应 java 层的 HybridData 对象
    2.2 setNativePointer
    将 native 层指针传给 java 层的 HybridData 对象

至此,CatalystInstance java 对象 和 CatalystInstance c++ 对象生命周期绑定,java 对象通过 HybridData 来控制 c++ 对象(因为 c++ 对象指针已经传递到 java 层,只要 delete 就完成了释放)

所以,当 java CatalystInstance destroy 的时候,也会触发 java hybridData resetNative 方法,进而调用了 jni 方法,进而调用到了 c++ 的 deleteNative 方法,最终调用到 c++ 的 delete 方法,完成 c++ 对象释放

总结

fbjni 依靠 HybridClass 可以实现 java 和 cpp 对象生命周期的绑定

当然,fbjni 还有好多方便的工具,封装好了各种方法,比如可以更简单的调用 java 方法,而不必写一堆模版函数等等。