2019-02-18
在创建 NativeModule 或 ViewManager 时,我们都得将它们添加到某个 ReactPackage 下。这是怎样的一种设计思想呢?
我们先来看 ReactPackage 的注释:
ReactPackage 自身是一个接口,它的作用是向 Catalyst 框架提供额外的功能特性,主要包括:
在 ReactPackage 中实现了功能 1 和 3.
从本节开始,我们由浅入深地分析 React Native 是如何使用 ReactPackage 的。
首先我们从第一篇中讲到的,在 Android 中继承 React Native,MainActivity 的写法:
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); }
其中:
ReactInstanceManager 通过 Builder 模式构建,我们对它已经很熟悉了,因此跳过 ReactInstanceManagerBuilder 直接来看 ReactInstanceManager。
在 ReactInstanceManager 中,ReactPackage 存放在成员变量中:
List<ReactPackage> mPackages;
在 ReactInstanceManager 的构造函数中,ReactInstanceManager 也会自己向 mPackages 中添加几个包:
加上我们外面添加的 MainReactPackage 包,这样至少就有两个包会被一定添加的。
前面都是向 mPackages 添加 Package,添加的包什么时候被使用呢?
答案是在 ReactInstanceManager 的 createReactContext 方法中:
NativeModuleRegistry nativeModuleRegistry = processPackages(reactContext, mPackages, false);
其中:
因此接下来我们来看 processPackages 方法。
首先方法签名:
private NativeModuleRegistry processPackages( ReactApplicationContext reactContext, List<ReactPackage> packages, // 我们的 mPackages boolean checkAndUpdatePackageMembership) // false
NativeModuleRegistry 也是通过 Builder 方法构建的(NativeModuleRegistryBuilder):
NativeModuleRegistryBuilder nativeModuleRegistryBuilder = new NativeModuleRegistryBuilder( reactContext, this, mLazyNativeModulesEnabled);
之后会对 packages 进行遍历,对每个 package,在内部会调用另一个同名方法:
processPackage(reactPackage, nativeModuleRegistryBuilder);
我们进入这个方法来看,是如何处理这个包的。在方法内部,核心调用方法是:
nativeModuleRegistryBuilder.processPackage(reactPackage);
我们看出,关键的逻辑都写在了 NativeModuleRegistryBuilder 中。因此进入 NativeModuleRegistryBuilder 的 processPackage 方法:
NativeModuleRegistryBuilder 对 Package 有两种加载方式,一种是懒加载,一种是直接加载。在 processPackage 中通过一个 if else 结构来分别处理。
在这里,我们来看直接加载的方式:
List<NativeModule> nativeModules; if (reactPackage instanceof ReactInstancePackage) { ReactInstancePackage reactInstancePackage = (ReactInstancePackage) reactPackage; nativeModules = reactInstancePackage.createNativeModules( mReactApplicationContext, mReactInstanceManager); } else { nativeModules = reactPackage.createNativeModules(mReactApplicationContext); } for (NativeModule nativeModule : nativeModules) { addNativeModule(nativeModule); }
其中:
在 addNativeModule 中:
mModules 成员用于存放 Native Modules,它的类型是:
Map<Class<? extends NativeModule>, ModuleHolder> mModules = new HashMap<>();
首先 addNativeModule 会进行一个去重操作,之后:
namesToType.put(name, type); ModuleHolder moduleHolder = new ModuleHolder(nativeModule); mModules.put(type, moduleHolder);
这里的 ModuleHolder 是什么呢?它主要是用于懒加载功能的。在这里我们不做详细介绍,等以后有机会在 Native Module 的懒加载机制中再来介绍这一块。现在只要知道 ModuleHolder 中含有 Native Module 实例即可。
最后将 nativeModule 按照类型 put 进 mModules 中。
再次回到 createReactContext,如今我们已经有了 NativeModuleRegistry 实例:
NativeModuleRegistry nativeModuleRegistry = processPackages(reactContext, mPackages, false); CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder() .setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault()) .setJSExecutor(jsExecutor) .setRegistry(nativeModuleRegistry) .setJSBundleLoader(jsBundleLoader) .setNativeModuleCallExceptionHandler(exceptionHandler);
其中:
CatalystInstanceImpl.Builder 很简单,我们直接看 CatalystInstanceImpl。
nativeModuleRegistry 通过构造函数传入,保存在成员中:
private final NativeModuleRegistry mNativeModuleRegistry;
在构造函数中,将 mNativeModuleRegistry 传入 initializeBridge 方法中:
initializeBridge( new BridgeCallback(this), jsExecutor, mReactQueueConfiguration.getJSQueueThread(), mNativeModulesQueueThread, mNativeModuleRegistry.getJavaModules(this), mNativeModuleRegistry.getCxxModules());
initializeBridge 的签名如下:
private native void initializeBridge( ReactCallback callback, JavaScriptExecutor jsExecutor, MessageQueueThread jsQueue, MessageQueueThread moduleQueue, Collection<JavaModuleWrapper> javaModules, Collection<ModuleHolder> cxxModules);
它是一个 JNI 方法,因此我们转入 C++ 层的 ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.cpp 的 CatalystInstanceImpl::initializeBridge。
它的方法签名如下:
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) {
我们可以对照一下,同一个对象,在 Java 侧与 C++ 的对应关系。
这个方法首先对 nativeModulesQueue 建立一份在 C++ 侧的引用:
moduleMessageQueue_ = std::make_shared<JMessageQueueThread>(nativeModulesQueue);
之后调用:
moduleRegistry_ = std::make_shared<ModuleRegistry>( buildNativeModuleList( std::weak_ptr<Instance>(instance_), javaModules, cxxModules, moduleMessageQueue_));
其中:
下面来看 buildNativeModuleList:
std::vector<std::unique_ptr<NativeModule>> buildNativeModuleList( std::weak_ptr<Instance> winstance, jni::alias_ref<jni::JCollection<JavaModuleWrapper::javaobject>::javaobject> javaModules, jni::alias_ref<jni::JCollection<ModuleHolder::javaobject>::javaobject> cxxModules, std::shared_ptr<MessageQueueThread> moduleMessageQueue) { std::vector<std::unique_ptr<NativeModule>> modules; if (javaModules) { for (const auto& jm : *javaModules) { modules.emplace_back(folly::make_unique<JavaNativeModule>( winstance, jm, moduleMessageQueue)); } } if (cxxModules) { for (const auto& cm : *cxxModules) { modules.emplace_back(folly::make_unique<CxxNativeModule>( winstance, cm->getName(), cm->getProvider(), moduleMessageQueue)); } } return modules; }
其中:
我们主要来看对 javaModules 的初始化部分,也就是这一部分:
for (const auto& jm : *javaModules) { modules.emplace_back(folly::make_unique<JavaNativeModule>( winstance, jm, moduleMessageQueue)); }
在这里,我们将 JavaModuleWrapper::javaobject
(jm) 作为参数传入 JavaNativeModule 的构造方法中。
JavaNativeModule 声明在在 ReactAndroid/src/main/jni/react/jni/JavaModuleWrapper.h。
我们来看它的成员变量:
std::weak_ptr<Instance> instance_; jni::global_ref<JavaModuleWrapper::javaobject> wrapper_; std::shared_ptr<MessageQueueThread> messageQueueThread_; std::vector<folly::Optional<MethodInvoker>> syncMethods_;
其中:
instance_
这个一定是 React Native 在 C++ 层的核心类wrapper_
这是我们的 Native Module(从 Java 层一层一层传进来不容易啊……)messageQueueThread_
对队列也有引用syncMethods_
暂时不知道是啥构造函数如下,保存各个成员:
JavaNativeModule( std::weak_ptr<Instance> instance, jni::alias_ref<JavaModuleWrapper::javaobject> wrapper, std::shared_ptr<MessageQueueThread> messageQueueThread) : instance_(std::move(instance)) , wrapper_(make_global(wrapper)) , messageQueueThread_(std::move(messageQueueThread)) {}
JavaModule 中在 Java 侧定义的方法是如何触发的呢?
答案在 JavaNativeModule::invoke 中(ReactAndroid/src/main/jni/react/jni/JavaModuleWrapper.cpp):
void JavaNativeModule::invoke(unsigned int reactMethodId, folly::dynamic&& params, int callId) { messageQueueThread_->runOnQueue([this, reactMethodId, params=std::move(params), callId] { static auto invokeMethod = wrapper_->getClass()->getMethod<void(jint, ReadableNativeArray::javaobject)>("invoke"); #ifdef WITH_FBSYSTRACE if (callId != -1) { fbsystrace_end_async_flow(TRACE_TAG_REACT_APPS, "native", callId); } #endif invokeMethod( wrapper_, static_cast<jint>(reactMethodId), ReadableNativeArray::newObjectCxxArgs(std::move(params)).get()); }); }
其中:
wrapper_=( =jni::global_ref<JavaModuleWrapper::javaobject>
)获取方法(invoke)我们再从 C++ 层网上看,触发 invokeMethod 最终执行的是 ReactAndroid/src/main/java/com/facebook/react/bridge/JavaModuleWrapper.java 的 invoke:
public void invoke(int methodId, ReadableNativeArray parameters) { if (mMethods == null || methodId >= mMethods.size()) { return; } mMethods.get(methodId).invoke(mJSInstance, parameters); }
其中:
下面来到了 NativeModule.NativeMethod 的 invoke 方法:
它是一个接口:
public interface NativeModule { interface NativeMethod { void invoke(JSInstance jsInstance, ReadableNativeArray parameters); String getType(); }
JavaMethodWrapper 实现了这个接口。它的 invoke 方法核心逻辑如下:
@Override public void invoke(JSInstance jsInstance, ReadableNativeArray parameters) { processArguments(); // 处理参数 mMethod.invoke(mModuleWrapper.getModule(), mArguments); // 调用方法,这是一个反射方法
其中:
至此,我们就梳理完成了,Native Module 中所定义的方法,是如何在 React Native 底层保存并调用的。
在前面有一个问题,就是我们在触发 Native Module 方法时是通过 reactMethodId 来调用的,这个 id 是怎么生成的呢?
Native Module 中的方法都使用 @ReactMethod 进行标注。
JavaModuleWrapper.findMethods 中会处理这个注解。
具体做法出,拿出被注解方法的 Method(我们前面 invoke 的那个)。
最终封装成一个 JavaMethodWrapper,放进 JavaModuleWrapper 的 mMethods 中:
private final ArrayList<NativeModule.NativeMethod> mMethods;
我们在回头来看 JavaModuleWrapper 的 invoke:
public void invoke(int methodId, ReadableNativeArray parameters) { if (mMethods == null || methodId >= mMethods.size()) { return; } mMethods.get(methodId).invoke(mJSInstance, parameters); } }
所谓的 reactMethodId 实际上是方法在 mMethods 中的 index。
NativeModule 中所提供的方法(被 @ReactMethod 注解的方法),是在运行时通过反射扫描的。
这样会导致性能降低,不过好在 NativeModule 在整个生命周期内只加载一次,因此问题不大。
但是也架不住量大,如果 Package 太多,最起码在冷启动时也会稍微卡一下。
我想这也是为啥,React Native 给 Native Module 做了懒加载,能环节不少这种的问题。
如果要彻底优化消除这里反射的性能影响的话,可以通过注解处理器在编译时就生成好 mMethods,运行时就不用再扫描了,直接拿来用就行。
在本文中,我们从 ReactPackage 入手,分析了 React Native 是如何处理 ReactPackage 的。
同时以 ReactPackage 中的 Native Module 为准,分析了 Native Module 在 React Native 中是如何注册,如何在底层使用,以及如何被底层触发的。