React Native 代码阅读(七):CatalystInstanceImpl 的创建

2019-02-19

前言

CatalystInstanceImpl 是 React Native 中很重要的一个类。因为它横跨 Java、C++ 两侧(这在 React Native 中称为 Hybrid),所以它的创建过程有点复杂。在本文中我们一探它的创建过程。

Java 层实例创建

我们先从 Java 层下手。还记得 CatalystInstanceImpl 是通过 Builder 模式构造的吗?

在整个 React Native 中只有一个地方会创建这个 Builder,即在 ReactInstanceManager 的 createReactContext 方法中。具体创建流程在之前的系列中已有讲解这里不再赘述。

在这里我们再向前进一步,ReactInstanceManager 是在哪里创建的呢?还记得在 React Native 官方文档中,如何在 Android 项目中集成 React Native 吗?

示例代码如下:

public class MainActivity extends Activity implements DefaultHardwareBackBtnHandler {

    private ReactInstanceManager mReactInstanceManager;
    private ReactRootView mReactRootView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mReactRootView = new ReactRootView(this);

        mReactInstanceManager = ReactInstanceManager.builder()
                .setApplication(getApplication())
                .setBundleAssetName("index.android.bundle")
                .setJSMainModuleName("index.android")
                .addPackage(new MainReactPackage())
                .setUseDeveloperSupport(BuildConfig.DEBUG)
                .setInitialLifecycleState(LifecycleState.RESUMED)
                .build();

        mReactRootView.startReactApplication(mReactInstanceManager, "Basic", null);

        setContentView(mReactRootView);
    }

我们可以看出,一个包含 ReactRootView 的 Activity 中,只会有一个 ReactInstanceManager 实例,进而只会有一个 CatalystInstanceImpl 实例。

C++ 层实例的创建

卖个关子

在这里我先卖个关子。CatalystInstanceImpl 在 C++ 侧对应的类是 ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.h。

我们可以在 React Native 的 C++ 代码中搜索,看看哪里有创建这个实例的地方?

我们会发现,就算把代码都找遍了,也找不到。

奇怪了吧?这里就要说到 React Native 底层的一个 Native 依赖库,叫 fbjni,它包含一个框架,通过这个框架,我们能在 C++ 侧建立一个与 Java 侧为映射关系的类。相当于提供了一种机制,对 JVM 中的类建立一个 C++ 侧的映像。

在本文中,我们不展开讲 fbjni 的实现原理(涉及篇幅太长,等我梳理好后会放在后面的文章中),而是把它当做一个黑盒,主要精力放在这个问题:

  • CatalystInstanceImpl 的 Java 对象的创建我们已经明白了
  • C++ 侧对象是在哪创建的?怎么创建的?

CatalystInstanceImpl 类

我们先来看 CatalystInstanceImpl 类的继承关系:

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>);
  ~CatalystInstanceImpl() override;

其中:

HybridClass 位于 ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Hybrid.h。这是 fbjni 中的类。

在这里我们把 fbjni 当做黑盒,先速成一下 fbjni 的使用。

HybridClass 速成

假设我们在 Java 侧有一个类叫 Foo(它的完整类名是 com.maxiee.Foo),我们想要创建一个它在 C++ 侧的映射类。

因此我们声明这样一个 C++ 类:

class Foo : public jni::HybridClass<Foo> {
public:
    static constexpr auto kJavaDescriptor = "Lcom/maxiee/Foo";

    static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jclass>);

    static void registerNatives();
}

其中:

  • kJavaDescriptor 是 fbjni 中的命名约定,用于指明对应的 Java 类
  • initHybrid 是一个静态方法,它是供 Java 侧调用的,C++ 实例就是在这个方法里创建出来的
  • 说到 Java 侧调用 initHybrid,就不得不提到(别开花!)JNI,因此 registerNatives 方法是注册 JNI 用的

下面先看 initHybrid 实现:

jni::local_ref<Foo::jhybriddata> Foo::initHybrid(jni::alias_ref<jclass>) {
    return makeCxxInstance();
}

其中:

  • 这个方法看起来很吓人,打眼一看像是这样 ::::::<><>::::::><><:::::><>><>< 跟象形文字一样
  • 其实它很简单,简单说就是通过 makeCxxInstance 方法,创建一个与 Java 侧对象关联的对象

这个对象是返回到 Java 层的,我们看下 Java 层的实现:

public class Foo {
    static {
        SoLoader.loadLibrary("my-lib-contains-foo");
    }

    // C++ parts
    private final HybridData mHybridData;
    private native static HybridData initHybrid();

    public Foo() {
        mHybridData = initHybrid();
    }

    public destroy() {
        mHybridData.resetNative();
    }
}

其中:

  • 首先我们要先装载 So 库
  • mHybridData 用于存放 C++ 侧实例
  • 在构造函数中我们创建 C++ 实例
  • 我们提供了一个 destroy 方法,用于销毁 C++ 实例

最后再看 registerNatives 方法,它用于在 Native 库 OnLoad 时注册 JNI 方法:

registerHybrid({
    makeNativeMethod("initHybrid", Foo::initHybrid)
});

其中:

  • registerHybrid 和 makeNativeMethod 也是 React Native 中提供的注册方法

小结

至此我们对 CatalystInstanceImpl 的 Java 侧和 C++ 侧两头怎么创建的都明了了。

我们也开始接触到 fbjni 这个库,这个库很有意思,也不容易掌握。希望我未来能够写一篇详解它的文章。