zoukankan      html  css  js  c++  java
  • 其实没那么复杂!探究react-native通信机制

    近段时间来Android上最火的框架非react native莫属了,这里我不去评价这个框架的好坏,毕竟只有用过的人才会有深刻的体会。但是我个人有一个习惯,在使用一个开源库之前,一定要看过它的源码,不说百分百搞懂吧,至少得弄清楚它的工作原理,所以在使用RN之前我就看了看它的源码。不看不知道,一看吓一跳,它其中最核心的那一部分——java和js的通信写的确实是非常的精妙,把整个流程搞懂以后让我受益无穷。

    这里插一句题外话,阿里的weex也马上就要开源了,我身边的小伙伴也有已经拿到源码投身于其中的,等到它开源的时候我也会去看看它的源码,了解下它和RN的区别到底是什么。

    废话也不说了,让我们好好看看RN的通信机制是怎样的吧。

    前言

    在看这篇文章之前,你得确保你有一定的RN开发基础,如果是零基础的同学,建议大家先去看看这个系列的文章,里面非常清楚的介绍了如何使用RN来进行开发。当然如果你不满足仅仅是[了解]的话,也可以去网上查阅关于RN的一些资料,其实里面一些东西是非常值得一看的,比如virtual DOM,diff机制等等。

    通信方式

    我们所说的[通信],指的是RN中Java和js的通信,也就是js部分中的那些jsx代码是如何转化成一个java层真实的view和事件的,java层又是如何调用js来找出它所需要的那些view和事件的。

    简单的说,RN的两端通信靠的是一张配置表,java端和js端持有同一张表,通信的时候就是靠这张表的各个条目的对应来进行的。

    connection

    大致的就是和上面这张图一样,两端各持有一份相同的config,config中有一些已经注册的模块,两端的通信就是通过传输这样的“A”,“B”或者”C”来实现的。这个config对应到RN的代码是NativeModuleRegistry和JavaScriptModuleRegistry。如果大家想象不出来的话我可以给大家打个比喻,java端和js端的通信就好比一个中国人和一个美国人在对话,而这个config,也就是注册表就相当于两个翻译,有了翻译两个语言不通的人才能正常交流。那这两张表是如何生成的呢?还是让我们从代码中寻找答案吧。

    首先我们知道在使用RN的时候,我们对应的activity要继承自ReactActivity并且重写一个叫做getPackages的方法。

    1
    2
    3
    4
    5
    6
    @Override
    protected List<ReactPackage> getPackages() {
    return Arrays.<ReactPackage>asList(
    new MainReactPackage()
    );
    }

    这个MainReactPackage是RN帮我们生成好的,其中定义了一些基础的组件和事件,具体就不说了,大家可以自己去看一下源码。如果你想要自定义一些组件或者事件的话必须要自己去写一个package,至于怎么写大家看我前面提到的那一系列文章就知道了。而我们前面提到的那两个注册表——NativeModuleRegistry和JavaScriptModuleRegistry就是通过这样的package去生成的,具体方法我们看下去就知道了。

    既然我们的activity继承自了ReactActivity,那我们就去看看ReactActivity里面做了什么。第一个要看的当然是onCreate函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if (getUseDeveloperSupport() && Build.VERSION.SDK_INT >= 23) {
    // Get permission to show redbox in dev builds.
    if (!Settings.canDrawOverlays(this)) {
    Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
    startActivity(serviceIntent);
    FLog.w(ReactConstants.TAG, REDBOX_PERMISSION_MESSAGE);
    Toast.makeText(this, REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show();
    }
    }

    mReactInstanceManager = createReactInstanceManager();
    ReactRootView mReactRootView = createRootView();
    mReactRootView.startReactApplication(mReactInstanceManager, getMainComponentName(), getLaunchOptions());
    setContentView(mReactRootView);
    }

    可以看到我们创建了一个ReactInstanceManager,看看是怎么创建的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    protected ReactInstanceManager createReactInstanceManager() {
    ReactInstanceManager.Builder builder = ReactInstanceManager.builder()
    .setApplication(getApplication())
    .setJSMainModuleName(getJSMainModuleName())
    .setUseDeveloperSupport(getUseDeveloperSupport())
    .setInitialLifecycleState(mLifecycleState);

    for (ReactPackage reactPackage : getPackages()) {
    builder.addPackage(reactPackage);
    }

    String jsBundleFile = getJSBundleFile();

    if (jsBundleFile != null) {
    builder.setJSBundleFile(jsBundleFile);
    } else {
    builder.setBundleAssetName(getBundleAssetName());
    }

    return builder.build();
    }

    中间有一段这样的代码

    1
    2
    3
    for (ReactPackage reactPackage : getPackages()) {
    builder.addPackage(reactPackage);
    }

    通过builder模式把我们的package注入到了builder中并且最后调用build方法创建出一个ReactInstanceManagerImpl实例。

    我们回过头来看onCreate函数,在这之后我们创建一个ReactRootView作为我们的根视图,并且调用它的startReactApplication函数,从函数名字就可以看出来,这个函数的作用非常重要,从这儿开始,算是启动了我们的RN程序。

    在startReactApplication函数中我们调用了ReactInstanceManager的createReactContextInBackground()方法去构造属于RN程序的上下文。在这个方法中会去判断是否是开发模式,大家可以在自己的activity中重写getUseDeveloperSupport()去更改模式。模式不同的主要区别在于获取JSBundle的方式不同。如果你是开发模式的,就会从server端获取,如果不是,则是从文件中获取。这里我们只关注前者。最终会走到onJSBundleLoadedFromServer方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    private void onJSBundleLoadedFromServer() {
    recreateReactContextInBackground(
    new JSCJavaScriptExecutor.Factory(),
    JSBundleLoader.createCachedBundleFromNetworkLoader(
    mDevSupportManager.getSourceUrl(),
    mDevSupportManager.getDownloadedJSBundleFile()));
    }

    private void recreateReactContextInBackground(
    JavaScriptExecutor.Factory jsExecutorFactory,
    JSBundleLoader jsBundleLoader) {
    ..........

    mReactContextInitAsyncTask = new ReactContextInitAsyncTask();
    mReactContextInitAsyncTask.execute(initParams);
    }

    在该方法中我们调用JSBundleLoader的createCachedBundleFromNetworkLoader方法去创建了一个JSBundleLoader。它的主要作用是去加载JSBundle。大家可以去看看JSBundleLoader这个类,其中还有两种创建loader的方式,如果我们不是开发模式,调用的是createFileLoader,也就是说release的情况下我们需要用gradle生成了JSBundle之后将其放在assets目录上或者文件中。

    下面让我们看看之后的recreateReactContextInBackground方法。

    它会调用了一个叫做mReactContextInitAsyncTask的AsyncTask去执行异步任务。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @Override
    protected Result<ReactApplicationContext> doInBackground(ReactContextInitParams... params) {
    Assertions.assertCondition(params != null && params.length > 0 && params[0] != null);
    try {
    JavaScriptExecutor jsExecutor =
    params[0].getJsExecutorFactory().create(
    mJSCConfig == null ? new WritableNativeMap() : mJSCConfig.getConfigMap());
    return Result.of(createReactContext(jsExecutor, params[0].getJsBundleLoader()));
    } catch (Exception e) {
    // Pass exception to onPostExecute() so it can be handled on the main thread
    return Result.of(e);
    }
    }

    我们可以看到它的doInBackground方法调用了createReactContext()方法去创建上下文。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    private ReactApplicationContext createReactContext(
    JavaScriptExecutor jsExecutor,
    JSBundleLoader jsBundleLoader) {
    FLog.i(ReactConstants.TAG, "Creating react context.");
    ReactMarker.logMarker(CREATE_REACT_CONTEXT_START);
    mSourceUrl = jsBundleLoader.getSourceUrl();
    NativeModuleRegistry.Builder nativeRegistryBuilder = new NativeModuleRegistry.Builder();
    JavaScriptModulesConfig.Builder jsModulesBuilder = new JavaScriptModulesConfig.Builder();

    ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
    if (mUseDeveloperSupport) {
    reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager);
    }

    ReactMarker.logMarker(PROCESS_PACKAGES_START);
    Systrace.beginSection(
    Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
    "createAndProcessCoreModulesPackage");
    try {
    CoreModulesPackage coreModulesPackage =
    new CoreModulesPackage(this, mBackBtnHandler, mUIImplementationProvider);
    processPackage(coreModulesPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
    } finally {
    Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    }

    // TODO(6818138): Solve use-case of native/js modules overriding
    for (ReactPackage reactPackage : mPackages) {
    Systrace.beginSection(
    Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
    "createAndProcessCustomReactPackage");
    try {
    processPackage(reactPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
    } finally {
    Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    }
    }
    ReactMarker.logMarker(PROCESS_PACKAGES_END);

    ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_START);
    Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "buildNativeModuleRegistry");
    NativeModuleRegistry nativeModuleRegistry;
    try {
    nativeModuleRegistry = nativeRegistryBuilder.build();
    } finally {
    Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_END);
    }

    ReactMarker.logMarker(BUILD_JS_MODULE_CONFIG_START);
    Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "buildJSModuleConfig");
    JavaScriptModulesConfig javaScriptModulesConfig;
    try {
    javaScriptModulesConfig = jsModulesBuilder.build();
    } finally {
    Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    ReactMarker.logMarker(BUILD_JS_MODULE_CONFIG_END);
    }

    NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null
    ? mNativeModuleCallExceptionHandler
    : mDevSupportManager;
    CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
    .setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
    .setJSExecutor(jsExecutor)
    .setRegistry(nativeModuleRegistry)
    .setJSModulesConfig(javaScriptModulesConfig)
    .setJSBundleLoader(jsBundleLoader)
    .setNativeModuleCallExceptionHandler(exceptionHandler);

    ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_START);
    // CREATE_CATALYST_INSTANCE_END is in JSCExecutor.cpp
    Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstance");
    CatalystInstance catalystInstance;
    try {
    catalystInstance = catalystInstanceBuilder.build();
    } finally {
    Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_END);
    }

    if (mBridgeIdleDebugListener != null) {
    catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);
    }

    reactContext.initializeWithInstance(catalystInstance);

    ReactMarker.logMarker(RUN_JS_BUNDLE_START);
    Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "runJSBundle");
    try {
    catalystInstance.runJSBundle();
    } finally {
    Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    ReactMarker.logMarker(RUN_JS_BUNDLE_END);
    }

    return reactContext;
    }

    这个方法的代码就比较多了,但是我们现在只看我们所关注的。大家应该还记得我们的关注点吧?[两个注册表NativeModuleRegistry和JavaScriptModuleRegistry是如何生成的]。这里给出了答案。

    1
    2
    NativeModuleRegistry.Builder nativeRegistryBuilder = new NativeModuleRegistry.Builder();
    JavaScriptModulesConfig.Builder jsModulesBuilder = new JavaScriptModulesConfig.Builder();

    首先创建出两个builder。

    1
    2
    3
    4
    5
    6
    7
    try {
    CoreModulesPackage coreModulesPackage =
    new CoreModulesPackage(this, mBackBtnHandler, mUIImplementationProvider);
    processPackage(coreModulesPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
    } finally {
    Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    }

    然后会去new一个CoreModulesPackage并且使用了processPackage方法去处理它,这个CoreModulesPackage里面定义了RN框架核心的一些Java和JS的module。

    下面让我们看看processPackage方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    private void processPackage(
    ReactPackage reactPackage,
    ReactApplicationContext reactContext,
    NativeModuleRegistry.Builder nativeRegistryBuilder,
    JavaScriptModulesConfig.Builder jsModulesBuilder) {
    for (NativeModule nativeModule : reactPackage.createNativeModules(reactContext)) {
    nativeRegistryBuilder.add(nativeModule);
    }
    for (Class<? extends JavaScriptModule> jsModuleClass : reactPackage.createJSModules()) {
    jsModulesBuilder.add(jsModuleClass);
    }
    }

    很简单,拿到具体的native和JS的module把它们添加到对应的builder中。

    再处理完了CoreModulesPackage之后,程序又会去处理我们在activity中注入的那些package。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    for (ReactPackage reactPackage : mPackages) {
    Systrace.beginSection(
    Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
    "createAndProcessCustomReactPackage");
    try {
    processPackage(reactPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
    } finally {
    Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    }
    }

    接下去就是生成注册表了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    try {
    nativeModuleRegistry = nativeRegistryBuilder.build();
    } finally {
    Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_END);
    }

    try {
    javaScriptModulesConfig = jsModulesBuilder.build();
    } finally {
    Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    ReactMarker.logMarker(BUILD_JS_MODULE_CONFIG_END);
    }

    至此,我们就把所有的packages,包括RN核心的CoreModulesPackage和我们activity自己注入的package里面的各个modules全部写到了对应Registry的builder中。

    现在这两份注册表是存在于java端的,那要怎么传输到JS端呢?我们继续看下去。

    再创建完了对应的注册表之后,ReactInstanceManagerImpl会通过builder模式去创建一个CatalystInstance的实例CatalystInstanceImpl。在CatalystInstanceImpl的构造函数中会去创建一个ReactBridge。并且会调用initializeBridge(jsExecutor, jsModulesConfig)这个方法。在这个方法中会去通过ReactBridge向C层传递一些数据,其中有这么一段:

    1
    2
    3
    bridge.setGlobalVariable(
    "__fbBatchedBridgeConfig",
    buildModulesConfigJSONProperty(mJavaRegistry, jsModulesConfig));

    调用了bridge的setGlobalVariable方法,这是一个native的方法。

    1
    public native void setGlobalVariable(String propertyName, String jsonEncodedArgument);

    这个方法的作用就是先把JavaRegistry格式化成json,并且调用C层的代码传输到js端。由于本人C层学艺不精,能看懂个大概,怕会有细节讲错,就不带大家进行分析了。

    总结

    到这里,真相就水落石出了,我们来总结一下吧。

    (1) 在程序启动的时候,也就是ReactActivity的onCreate函数中,我们会去创建一个ReactInstanceManagerImpl对象

    (2) 通过ReactRootView的startReactApplication方法开启整个RN世界的大门

    (3) 在这个方法中,我们会通过一个AsyncTask去创建ReactContext

    (4) 在创建ReactContext过程中,我们把我们自己注入(MainReactPackage)的和系统生成(CoreModulesPackage)的package通过processPackage方法将其中的各个modules注入到了对应的Registry中

    (5) 最后通过CatalystInstanceImpl中的ReactBridge将java的注册表通过jni传输到了JS层。

    这样,js层就获取到了java层的所有接口和方法,相当于一个美国人身边有了以为中文翻译。而js层的注册表本来就是由java层生成的,所以就相当于一个中国人身边有了一个英文翻译,从此他们就可以愉快的交流了。

    涉及到的重要的类:

    ReactInstanceManagerImpl,ReactContext,CatalystInstanceImpl,ReactBridge。

    Java->js

    前面我们讲了两端通信的方式和注册表是如何从Java端发送到js端的,下面让我们讲讲这样的准备工作完成以后,java是如何调用js的方法的。

    首先,经过上面的学习,其实这个问题已经有了一个初步的答案了,因为[将JavaRegistry从java端传输到js端]这个过程就是一个java向js通信的过程,具体的过程在上一节的总结中已经说了,让我们回想一下,之前所有的事情都是在ReactInstanceManagerImpl中ReactContextInitAsyncTask的doInBackground方法中完成的,那这个方法完成之后又做了什么呢?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @Override
    protected void onPostExecute(Result<ReactApplicationContext> result) {
    try {
    setupReactContext(result.get());
    } catch (Exception e) {
    mDevSupportManager.handleException(e);
    } finally {
    mReactContextInitAsyncTask = null;
    }

    // Handle enqueued request to re-initialize react context.
    if (mPendingReactContextInitParams != null) {
    recreateReactContextInBackground(
    mPendingReactContextInitParams.getJsExecutorFactory(),
    mPendingReactContextInitParams.getJsBundleLoader());
    mPendingReactContextInitParams = null;
    }
    }

    可以看到,在onPostExecute方法中调用了setupReactContext方法,在这个方法中会去调用attachMeasuredRootViewToInstance方法。

    1
    2
    3
    4
    5
    6
    7
    8
    private void attachMeasuredRootViewToInstance(
    ReactRootView rootView,
    CatalystInstance catalystInstance) {
    .......
    .......

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

    这个方法的最后用了我们的CatalystInstanceImpl的getJSModule方法,它会去调用JavaScriptModuleRegistry的getJSModule方法,获取对应的JavaScriptModule,也就是从注册表中获取对应的模块。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public synchronized  <T extends JavaScriptModule> T getJavaScriptModule(ExecutorToken executorToken, Class<T> moduleInterface) {
    HashMap<Class<? extends JavaScriptModule>, JavaScriptModule> instancesForContext =
    mModuleInstances.get(executorToken);
    if (instancesForContext == null) {
    instancesForContext = new HashMap<>();
    mModuleInstances.put(executorToken, instancesForContext);
    }

    JavaScriptModule module = instancesForContext.get(moduleInterface);
    if (module != null) {
    return (T) module;
    }

    JavaScriptModuleRegistration registration =
    Assertions.assertNotNull(
    mModuleRegistrations.get(moduleInterface),
    "JS module " + moduleInterface.getSimpleName() + " hasn't been registered!");
    JavaScriptModule interfaceProxy = (JavaScriptModule) Proxy.newProxyInstance(
    moduleInterface.getClassLoader(),
    new Class[]{moduleInterface},
    new JavaScriptModuleInvocationHandler(executorToken, mCatalystInstance, registration));
    instancesForContext.put(moduleInterface, interfaceProxy);
    return (T) interfaceProxy;
    }

    这个方法就比较神奇了,首先去缓存中找,如果找到就返回,没找到就去创建,怎么创建的呢,用的是动态代理!

    1
    2
    3
    4
    JavaScriptModule interfaceProxy = (JavaScriptModule) Proxy.newProxyInstance(
    moduleInterface.getClassLoader(),
    new Class[]{moduleInterface},
    new JavaScriptModuleInvocationHandler(executorToken, mCatalystInstance, registration));

    这里大家必须要对动态代理有所了解,可以自己去找相关的知识。让我们看看JavaScriptModuleInvocationHandler。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @Override
    public @Nullable Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    ExecutorToken executorToken = mExecutorToken.get();
    if (executorToken == null) {
    FLog.w(ReactConstants.TAG, "Dropping JS call, ExecutorToken went away...");
    return null;
    }
    String tracingName = mModuleRegistration.getTracingName(method);
    mCatalystInstance.callFunction(
    executorToken,
    mModuleRegistration.getModuleId(),
    mModuleRegistration.getMethodId(method),
    Arguments.fromJavaArgs(args),
    tracingName);
    return null;
    }

    我们看它最核心的invoke方法。里面获取了调用方法的moduleId,methodId和参数,然后调用了CatalystInstanceImpl的callFunction去执行。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @Override
    public void callFunction(
    ExecutorToken executorToken,
    int moduleId,
    int methodId,
    NativeArray arguments,
    String tracingName) {
    synchronized (mJavaToJSCallsTeardownLock) {
    if (mDestroyed) {
    FLog.w(ReactConstants.TAG, "Calling JS function after bridge has been destroyed.");
    return;
    }

    incrementPendingJSCalls();

    Assertions.assertNotNull(mBridge).callFunction(executorToken, moduleId, methodId, arguments, tracingName);
    }
    }

    直接调用了ReactBridge的同名函数。

    1
    public native void callFunction(ExecutorToken executorToken, int moduleId, int methodId, NativeArray arguments, String tracingName);

    可以看到又是一个native函数,具体作用就是将想用调用的方法对应的moduleId,methodId和arguments通过jni传递到js端进行调用。而我们这边调用的是AppRegistry的runApplication方法,这个方法在js端的作用就是开始运行整个js程序,从而将我们的RN程序真正的跑起来。

    总结

    首先,对于我们java端要调用的js端的类和方法,我们都必须要注册到js的注册表中,这个过程在上一部分的分析中已经带大家走过了。当真正要调用的时候,步骤是这样的:

    (1) 调用CatalystInstanceImpl这个类的getJSModule方法去得到对应的JSModule

    (2) CatalysInstanceImpl实际上是调用JavaScriptModuleRegistry的getJSModule方法去获取注册在其中的module

    (3) 然后通过动态代理拿到方法的各种参数,包括moduleId,methodId和params

    (4) 通过ReactBridge调用jni传递到C层

    (5) 通过C层再传递到js层。

    涉及到的重要的类:

    CatalystInstanceImpl,JavaScriptModuleRegistry,JavaScriptModuleInvocationHandler,ReactBridge。

    JavaCallJS

    通过这个图配合上代码应该能比较好的理解了。

    js->Java

    RN的js调java的流程可以说是让我觉得最新颖的地方,具体就是js不是直接通过注册接口去调用java方法的,而是将对应的的参数(moduleId和methodId)push到一个messageQueue中,等待java层的事件来驱动它,当java层的事件传递过来以后,js层把messageQueue中所有的数据返回给java层,再通过注册表JavaRegistry去调用方法。

    首先,我们说了js层是把对应的参数push到messageQueue中,具体的方法是MessageQueue.js的__nativeCall方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    __nativeCall(module, method, params, onFail, onSucc) {

    .........

    this._queue[MODULE_IDS].push(module);
    this._queue[METHOD_IDS].push(method);
    this._queue[PARAMS].push(params);

    ...........
    }

    可以看到它把对应的module,method和params push到了队列里面,然后就是等待java层的事件驱动。

    java层的事件驱动其实也可以看成java层向js层的通信,最终会走到MessageQueue.js的callFunctionReturnFlushedQueue方法和invokeCallbackAndReturnFlushedQueue方法。

    1
    2
    3
    4
    5
    6
    7
    8
    callFunctionReturnFlushedQueue(module, method, args) {
    guard(() => {
    this.__callFunction(module, method, args);
    this.__callImmediates();
    });

    return this.flushedQueue();
    }

    调用了flushedQueue将MessageQueue中的所有数据通过C层发往java层。

    到了java层以后,会回调到NativeModulesReactCallback类执行call方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    private class NativeModulesReactCallback implements ReactCallback {

    @Override
    public void call(ExecutorToken executorToken, int moduleId, int methodId, ReadableNativeArray parameters) {
    mReactQueueConfiguration.getNativeModulesQueueThread().assertIsOnThread();

    synchronized (mJSToJavaCallsTeardownLock) {
    // Suppress any callbacks if destroyed - will only lead to sadness.
    if (mDestroyed) {
    return;
    }
    mJavaRegistry.call(CatalystInstanceImpl.this, executorToken, moduleId, methodId, parameters);
    }
    }
    .....
    }

    可以看到里面通过JavaRegistry调用了它的call方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    /* package */ void call(
    CatalystInstance catalystInstance,
    ExecutorToken executorToken,
    int moduleId,
    int methodId,
    ReadableNativeArray parameters) {
    ModuleDefinition definition = mModuleTable.get(moduleId);
    if (definition == null) {
    throw new RuntimeException("Call to unknown module: " + moduleId);
    }
    definition.call(catalystInstance, executorToken, methodId, parameters);
    }

    拿到对应的module,调用call方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public void call(
    CatalystInstance catalystInstance,
    ExecutorToken executorToken,
    int methodId,
    ReadableNativeArray parameters) {
    MethodRegistration method = this.methods.get(methodId);
    Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, method.tracingName);
    try {
    this.methods.get(methodId).method.invoke(catalystInstance, executorToken, parameters);
    } finally {
    Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    }
    }
    }

    其中根据methodId拿到对应module的方法,执行invoke。

    最终执行的是BaseJavaModule的invoke方法。

    1
    2
    3
    4
    5
    6
    7
    @Override
    public void invoke(CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray parameters) {

    .........
    mMethod.invoke(BaseJavaModule.this, mArguments);
    .........
    }

    通过反射调用最终的方法。

    总结

    这里我们重点要了解的就是js向java的通信靠的是事件驱动模式。

    (1) JS将方法的对应参数push到MessageQueue中,等java端事件传递

    (2) Java端事件触发以后,JS层将MessageQueue中的数据通过C层传递给java层

    (3) C层调用一开始注册在其中的NativeModulesReactCallback

    (4 然后通过JavaRegistry这个Java注册表拿到对应的module和method

    (5) 通过反射执行方法。

    涉及到的重要的类:

    MessageQueue(JS层),NativeModulesReactCallback,JavaRegistry。

    JSCallJava

    老规矩,通过图配合代码来理解流程。

    重要类的作用

    最后我们通过总结一下前面提到的几个重要的类的作用来加深印象。

    ReactInstanceManager:它的作用是创建出ReactContext,CatalystInstance等类,解析package生成注册表,并且配合ReactRootView管理View的创建,生命周期等功能。

    ReactContext:继承自ContextWrapper,是RN程序自己的上下文,我们可以通过getContext()去获得,里面有CatalystInstance实例,可以去获得Java和JS的module。

    ReactRootView:RN程序的根视图,startReactApplication方法开启RN世界的大门。

    CatalystInstance:Java端通信的管理类,提供通信的环境,方法和回调方法,内部通过ReactBridge进行通信。

    ReactBridge:通信的核心类,通过jni的方式进行通信。

    NativeModuleRegistry:Java接口的注册表。

    JavascriptModuleRegistry:JS接口的注册表。

    CoreModulePackage:RN核心框架的package,包括Java接口和js接口,前文提到的AppResgitry就是在这里面注册的。

    MainReactPackage:RN帮我们封装的一些通用的Java组件和事件。

    JsBundleLoader:用于加载JSBundle的类,其中会根据应用的情况创建不同的loader。

    JSBundle:存有js核心逻辑,在release环境下要通过gradle任务去创建并且放在对应的目录下。

    勘误

    不好意思各位!!这里由于本人粗心的原因,代码没看全,有一个地方出错了,这里写一个勘误,如果造成了大家的困扰我再次说一声不好意思!

    前面我说过,JS调Java的机制是[JS把对应的moduleId,methodId和params push到queue中,等待native调用js,然后把MessageQueue中的数据发送到C层再通过jni转到java层]。但是今天公司里的大神在和我们讨论RN的时候纠正了我的这个错误。

    我们一起看一下MessageQueue.js的__nativeCall方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    __nativeCall(module, method, params, onFail, onSucc) {

    ..............

    this._queue[MODULE_IDS].push(module);
    this._queue[METHOD_IDS].push(method);
    this._queue[PARAMS].push(params);

    var now = new Date().getTime();
    if (global.nativeFlushQueueImmediate &&
    now - this._lastFlush >= MIN_TIME_BETWEEN_FLUSHES_MS) {
    global.nativeFlushQueueImmediate(this._queue);
    this._queue = [[], [], [], this._callID];
    this._lastFlush = now;
    }
    Systrace.counterEvent('pending_js_to_native_queue', this._queue[0].length);
    if (__DEV__ && SPY_MODE && isFinite(module)) {
    console.log('JS->N : ' + this._remoteModuleTable[module] + '.' +
    this._remoteMethodTable[module][method] + '(' + JSON.stringify(params) + ')');
    }
    }

    之前的文章只分析了前面的三个queue.push,但是大家可以看一下后面的一段:

    1
    2
    3
    4
    5
    6
    7
    var now = new Date().getTime();
    if (global.nativeFlushQueueImmediate &&
    now - this._lastFlush >= MIN_TIME_BETWEEN_FLUSHES_MS) {
    global.nativeFlushQueueImmediate(this._queue);
    this._queue = [[], [], [], this._callID];
    this._lastFlush = now;
    }

    它会去判断两次调用的时差,如果大于MIN_TIME_BETWEEN_FLUSHES_MS(5ms)的话,会调用global.nativeFlushQueueImmediate(this._queue);这个方法去主动把数据传递到C层的。也就是说,如果你两次的通信时间很短,小于5ms了,那就和我之前讲的一样,但是如果大于5ms,RN在JS端会主动传递数据的,这里一定要注意了!!

    官方文档:

        http://wiki.jikexueyuan.com/project/react-native/

  • 相关阅读:
    BootStrap行内编辑
    NPOI学习笔记
    仅当使用了列列表并且 IDENTITY_INSERT 为 ON 时,才能为表'Address'中的标识列指
    .Net MVC发布出错 Server Error in '/' Application.
    C# 新特性
    System.Runtime.InteropServices.COMException:“服务器出现意外情况。 (异常来自
    BootStrap的表格加载json数据,并且可以搜索,选择
    NPOI导入导出Excel
    读取Easy UI的DATa grid里面的所有数据
    C# 导出Excel
  • 原文地址:https://www.cnblogs.com/android-blogs/p/5623481.html
Copyright © 2011-2022 走看看