// C++ parts

private final HybridData mHybridData;

private native static HybridData initHybrid();

private CatalystInstanceImpl(

final ReactQueueConfigurationSpec ReactQueueConfigurationSpec,

final JavaScriptExecutor jsExecutor,

final NativeModuleRegistry registry,

final JavaScriptModuleRegistry jsModuleRegistry,

final JSBundleLoader jsBundleLoader,

NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {

FLog.d(ReactConstants.TAG, “Initializing React Xplat Bridge.”);

//native C++方法,用来初始化JNI相关状态然后返回mHybridData。

mHybridData = initHybrid();

//创建ReactNative的三个线程nativeModulesThread和jsThread、uiThread,都是通过Handler来管理的。

mReactQueueConfiguration = ReactQueueConfigurationImpl.create(

ReactQueueConfigurationSpec,

new NativeExceptionHandler());

mBridgeIdleListeners = new CopyOnWriteArrayList<>();

mJavaRegistry = registry;

mJSModuleRegistry = jsModuleRegistry;

mJSBundleLoader = jsBundleLoader;

mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;

mTraceListener = new JSProfilerTraceListener(this);

//native C++方法,用来初始化Bridge。

initializeBridge(

new BridgeCallback(this),

jsExecutor,

mReactQueueConfiguration.getJSQueueThread(),

mReactQueueConfiguration.getNativeModulesQueueThread(),

mJavaRegistry.getModuleRegistryHolder(this));

mMainExecutorToken = getMainExecutorToken();

}

private native void initializeBridge(ReactCallback callback,

JavaScriptExecutor jsExecutor,

MessageQueueThread jsQueue,

MessageQueueThread moduleQueue,

ModuleRegistryHolder registryHolder);

}

刚刚分析 createReactContext() 方法的总结没错,CatalystInstanceImpl 这货就是个封装总管,负责了 Java 层代码到 JNI 封装初始化的任务和 Java 与 JS 调用的 Java 端控制中心。所以我们先看看调用 native initializeBridge 方法时传入的 5 个参数吧,分别如下:

1. callback参数: CatalystInstanceImpl 的内部静态实现类 BridgeCallback,负责相关接口回调回传。

2. jsExecutor参数: 前面分析的 XReactInstanceManagerImpl 中赋值为 JSCJavaScriptExecutor 实例,JSCJavaScriptExecutor 中也有自己的 native initHybrid 的 C++ 方法被初始化时调用,具体在 OnLoad.cpp 的 JSCJavaScriptExecutorHolder 类中。

3. jsQueue参数: 来自于 mReactQueueConfiguration.getJSQueueThread(),mReactQueueConfiguration就是 CatalystInstanceImpl 中创建的 ReactQueueConfigurationImpl.create(

ReactQueueConfigurationSpec,

new NativeExceptionHandler()); 第一个参数来自于 XReactInstanceManagerImpl 中 CatalystInstanceImpl 的建造者,实质为包装相关线程名字、类型等,然后通过 ReactQueueConfigurationImpl 的 create 创建对应线程的 Handler,这里就是名字为 js 的后台线程 Handler,第二个参数为异常捕获回调实现。

4. moduleQueue参数: 来自于 mReactQueueConfiguration.getNativeModulesQueueThread(),mReactQueueConfiguration就是 CatalystInstanceImpl 中创建的 ReactQueueConfigurationImpl.create(

ReactQueueConfigurationSpec,

new NativeExceptionHandler()); 第一个参数来自于 XReactInstanceManagerImpl 中 CatalystInstanceImpl 的建造者,实质为包装相关线程名字、类型等,然后通过 ReactQueueConfigurationImpl 的 create 创建对应线程的 Handler,这里就是名字为 native_modules 的后台线程 Handler,第二个参数为异常捕获回调实现。

5. registryHolder参数: mJavaRegistry 对象来自于 XReactInstanceManagerImpl 中 CatalystInstanceImpl 的建造者,通过 mJavaRegistry.getModuleRegistryHolder(this) 传递一个 Java 层的 ModuleRegistryHolder 实例到同名的 C++ 中,具体在 mJavaRegistry.getModuleRegistryHolder(this) 的返回值处为 return new ModuleRegistryHolder(catalystInstanceImpl, javaModules, cxxModules); 而 ModuleRegistryHolder 的构造方法中调用了 C++ 的 initHybrid(catalystInstanceImpl, javaModules, cxxModules); 方法。

CatalystInstanceImpl 这货会玩,自己在 Java 层直接把持住了 JavaScriptModuleRegistry 映射表,把 NativeModuleRegistry 映射表、BridgeCallback 回调、JSCJavaScriptExecutor、js 队列 MessageQueueThread、native 队列 MessageQueueThread 都通过 JNI 嫁接到了 C++ 中。那我们现在先把目光转移到 CatalystInstanceImpl.cpp 的 initializeBridge 方法上(关于 JNI 的 OnLoad 中初始化注册模块等等就不介绍了),如下:

void CatalystInstanceImpl::initializeBridge(

jni::alias_refReactCallback::javaobject callback,

// This executor is actually a factory holder.

JavaScriptExecutorHolder* jseh,

jni::alias_refJavaMessageQueueThread::javaobject jsQueue,

jni::alias_refJavaMessageQueueThread::javaobject moduleQueue,

ModuleRegistryHolder* mrh) {

// Java CatalystInstanceImpl -> C++ CatalystInstanceImpl -> Bridge -> Bridge::Callback

// --weak–> ReactCallback -> Java CatalystInstanceImpl

//instance_为ReactCommon目录下 Instance.h 中类的实例;JNI封装规则就不介绍了,之前写过文章的。

//第一个参数为JInstanceCallback实现类,父类在cxxreact/Instance.h中。

//第二个参数为JavaScriptExecutorHolder,实质对应java中JavaScriptExecutor,也就是上面分析java的initializeBridge方法第二个参数JSCJavaScriptExecutor。

//第三第四个参数都是java线程透传到C++,纯C++的JMessageQueueThread。

//第五个参数为C++的ModuleRegistryHolder的getModuleRegistry()方法。

instance_->initializeBridge(folly::make_unique(callback),

jseh->getExecutorFactory(),

folly::make_unique(jsQueue),

folly::make_unique(moduleQueue),

mrh->getModuleRegistry());

}

到此 CatalystInstance 的实例 CatalystInstanceImpl 对象也就初始化 OK 了,同时通过 initializeBridge 建立了 Bridge 连接。关于这个 Bridge 在RN 中是通过 libjsc.so 中 JSObjectRef.h 的 JSObjectSetProperty(m_context, globalObject, jsPropertyName, valueToInject, 0, NULL); 来关联的,这样就可以在 Native 设置 JS 执行,反之同理。

由于这一小节我们只讨论 RN 的加载启动流程,所以 initializeBridge 的具体实现我们下面分析互相通信交互时再仔细分析,故我们先把思路还是回到 XReactInstanceManagerImpl 中 createReactContext 方法的 reactContext.initializeWithInstance(catalystInstance); 一行,可以看见,这行代码意思就是将刚刚初始化的 catalystInstance 传递给全局唯一的 reactContext 对象,同时在 reactContext 中通过 catalystInstance 拿到 JS、Native、UI 几个 Thread 的引用,方便快速访问使用这些对象。接着调用了 catalystInstance.runJSBundle(); 方法,这个方法实现如下:

@Override

public void runJSBundle() {

mJSBundleHasLoaded = true;

//mJSBundleLoader就是前面分析的依据不同设置决定是JSBundleLoader的createAssetLoader还是createFileLoader等静态方法的匿名实现类。

// incrementPendingJSCalls();

mJSBundleLoader.loadScript(CatalystInstanceImpl.this);

}

通过注释我们假设 Loader 是默认的,也即 JSBundleLoader 类的如下方法:

public static JSBundleLoader createAssetLoader(

final Context context,

final String assetUrl) {

return new JSBundleLoader() {

@Override

public void loadScript(CatalystInstanceImpl instance) {

instance.loadScriptFromAssets(context.getAssets(), assetUrl);

}

@Override

public String getSourceUrl() {

return assetUrl;

}

};

}

可以看见,它实质又调用了 CatalystInstanceImpl 的 loadScriptFromAssets 方法,我们继续跟踪 CatalystInstanceImpl 的这个方法吧,如下:

native void loadScriptFromAssets(AssetManager assetManager, String assetURL);

loadScriptFromAssets 既然是一个 native 方法,我们去 CatalystInstanceImpl.cpp 看下这个方法的实现,如下:

void CatalystInstanceImpl::loadScriptFromAssets(jobject assetManager,

const std::string& assetURL) {

const int kAssetsLength = 9; // strlen(“assets://”);

//获取source路径名,不计前缀,这里默认就是index.android.bundle

auto sourceURL = assetURL.substr(kAssetsLength);

//assetManager是Java传递的AssetManager。

//extractAssetManager是JSLoader.cpp中通过系统动态链接库android/asset_manager_jni.h的AAssetManager_fromJava方法来获取AAssetManager对象的。

auto manager = react::extractAssetManager(assetManager);

//通过JSLoader对象的loadScriptFromAssets方法读文件,得到大字符串script(即index.android.bundle文件的JS内容)。

auto script = react::loadScriptFromAssets(manager, sourceURL);

//判断是不是Unbundle,这里不是Unbundle,因为打包命令我们用了react.gradle的默认bundle,没用unbundle命令(感兴趣的自己分析这条路线)。

if (JniJSModulesUnbundle::isUnbundle(manager, sourceURL)) {

instance_->loadUnbundle(

folly::make_unique(manager, sourceURL),

std::move(script),

sourceURL);

return;

} else {

//bundle命令打包的,所以走这里。

//instance_为ReactCommon目录下 Instance.h 中类的实例,前面分析过了。

instance_->loadScriptFromString(std::move(script), sourceURL);

}

}

看来还没到头,这货又走到了 ReactCommon 目录下 Instance 实例的 loadScriptFromString 方法去了(由此可以看出来前面 ReactNativeAndroid 目录下的 jni 代码都是 Android 平台特有的封装,直到 ReactCommon 才是通用的),如下:

//string为index.android.bundle内容。

//sourceURL在这里默认为index.android.bundle。

void Instance::loadScriptFromString(std::unique_ptr string,

std::string sourceURL) {

//callback_就是initializeBridge传进来的,实质实现是CatalystInstanceImpl的BridgeCallback。

//说白了就是回传一个状态,要开始搞loadScriptFromString了

callback_->incrementPendingJSCalls();

SystraceSection s(“reactbridge_xplat_loadScriptFromString”,

“sourceURL”, sourceURL);

//厉害了,Word哥,年度大戏啊!

//nativeToJsBridge_也是Instance::initializeBridge方法里初始化的,实现在Common的NativeToJsBridge类里。

nativeToJsBridge_->loadApplication(nullptr, std::move(string), std::move(sourceURL));

}

妈的,没完没了了,继续跟吧,到 Common 的 NativeToJsBridge.cpp 看看 loadApplication 方法吧,如下:

//unbundle传入的是个空指针。

//startupScript为bundle文件内容。

//startupScript为bundle文件名。

void NativeToJsBridge::loadApplication(

std::unique_ptr unbundle,

std::unique_ptr startupScript,

std::string startupScriptSourceURL) {

//runOnExecutorQueue实质就是获取一个MessageQueueThread,然后在其线程中执行一个task。

runOnExecutorQueue(

m_mainExecutorToken,

[unbundleWrap=folly::makeMoveWrapper(std::move(unbundle)),

startupScript=folly::makeMoveWrapper(std::move(startupScript)),

startupScriptSourceURL=std::move(startupScriptSourceURL)]

(JSExecutor* executor) mutable {

auto unbundle = unbundleWrap.move();

if (unbundle) {

executor->setJSModulesUnbundle(std::move(unbundle));

}

//因为我们是bundle命令打包的,所以走这里继续执行!!!

executor->loadApplicationScript(std::move(*startupScript),

std::move(startupScriptSourceURL));

});

}

靠靠靠,还不到头,又特么绕到 JSExecutor 的 loadApplicationScript 方法里面去了,继续跟吧(这个 executor 是 runOnExecutorQueue 方法中回传的一个 map 中取的,实质是 OnLoad 中 JSCJavaScriptExecutorHolder 对应,也即 java 中 JSCJavaScriptExecutor,所以 JSExecutor 实例为 JSCExecutor.cpp 中实现),如下:

//script为bundle文件内容,sourceURL为bundle文件名

void JSCExecutor::loadApplicationScript(std::unique_ptr script, std::string sourceURL) throw(JSException) {

SystraceSection s(“JSCExecutor::loadApplicationScript”,

“sourceURL”, sourceURL);

//把bundle文件和文件名等内容转换成js可以识别的String

String jsScript = jsStringFromBigString(*script);

String jsSourceURL(sourceURL.c_str());

//使用webkit JSC去真正解释执行Javascript了!

evaluateScript(m_context, jsScript, jsSourceURL);

//绑定桥,核心是通过getGlobalObject将JS与C++通过webkit JSC bind

bindBridge();

flush();

}

去他大爷的,没完没了了,继续看看 bindBridge() 方法和 flush() 方法,如下:

void JSCExecutor::bindBridge() throw(JSException) {

auto global = Object::getGlobalObject(m_context);

auto batchedBridgeValue = global.getProperty(“__fbBatchedBridge”);

auto batchedBridge = batchedBridgeValue.asObject();

m_callFunctionReturnFlushedQueueJS = batchedBridge.getProperty(“callFunctionReturnFlushedQueue”).asObject();

m_invokeCallbackAndReturnFlushedQueueJS = batchedBridge.getProperty(“invokeCallbackAndReturnFlushedQueue”).asObject();

//通过webkit JSC获取MessageQueue.js的flushedQueue

m_flushedQueueJS = batchedBridge.getProperty(“flushedQueue”).asObject();

m_callFunctionReturnResultAndFlushedQueueJS = batchedBridge.getProperty(“callFunctionReturnResultAndFlushedQueue”).asObject();

}

void JSCExecutor::flush() {

SystraceSection s(“JSCExecutor::flush”);

//m_flushedQueueJS->callAsFunction({})即调用MessageQueue.js的flushedQueue方法。

//即把JS端相关通信交互数据通过flushedQueue返回传给callNativeModules。

callNativeModules(m_flushedQueueJS->callAsFunction({}));

}

void JSCExecutor::callNativeModules(Value&& value) {

SystraceSection s(“JSCExecutor::callNativeModules”);

try {

//把JS端相关通信数据转为JSON格式字符串数据

auto calls = value.toJSONString();

//m_delegate实质为Executor.h中ExecutorDelegate类的实现类JsToNativeBridge对象。

//故callNativeModules为JsToNativeBridge.cpp中实现的方法,把calls json字符串pase成格式结构。

m_delegate->callNativeModules(*this, folly::parseJson(calls), true);

} catch (…) {

}

}

卧槽!又绕回到了 JsToNativeBridge.cpp 的 callNativeModules 方法,那就看下吧,如下:

//executor即为前面的JSCExecutor。

//calls为被解析OK的JS端JSON通信参数结构。

//isEndOfBatch通知是否一个批次处理OK了,这里传递了true进来,说明JS文件Loader OK了。

void callNativeModules(

JSExecutor& executor, folly::dynamic&& calls, bool isEndOfBatch) override {

//拿到token

ExecutorToken token = m_nativeToJs->getTokenForExecutor(executor);

//扔到nativeQueue的线程队列去等待执行

m_nativeQueue->runOnQueue([this, token, calls=std::move(calls), isEndOfBatch] () mutable {

// An exception anywhere in here stops processing of the batch. This

// was the behavior of the Android bridge, and since exception handling

// terminates the whole bridge, there’s not much point in continuing.

for (auto& call : react::parseMethodCalls(std::move(calls))) {

//调用Native registry表中的java NativeMethod方法。

m_registry->callNativeMethod(

token, call.moduleId, call.methodId, std::move(call.arguments), call.callId);

}

//一些类似数据库事务操作的机制,用来告诉OK了

if (isEndOfBatch) {

m_callback->onBatchComplete();

m_callback->decrementPendingJSCalls();

}

});

}

终于尼玛明朗了,上面这段调用不就是前面分析的那个回调么,说白了就是 CatalystInstanceImpl.java 中 CatalystInstanceImpl 构造方法中调用 C++ 的 initializeBridge 方法时传入的第一个参数 BridgeCallback 么,也就是说 JS bundle 文件被加载完成以后 JS 端调用 Java 端时会触发 Callback 的 onBatchComplete 方法,这货最终又会触发 OnBatchCompleteListener 接口的 onBatchComplete 方法,这不就把 JS Bundle 文件加载完成以后回调 Java 通知 OK 了么,原来主要的流程是这么回事。为了接下来不迷糊,赶紧先来一把小梳理总结,用图说话,如下:

上面这幅图已经囊括了我们上面那些枯燥的启动流程的部分流程分析了,好了,从上图可以知道我们前面贴出来的 AsyncTask 的 onPostExecute 方法还没分析,所以我们把目光再回到 XReactInstanceManagerImpl 的那个 ReactContextInitAsyncTask 中,doInBackground 方法执行完成后返回了 Result 包装的 reactContext,所以我们看下 onPostExecute 方法中调用的核心方法 setupReactContext,如下:

private void setupReactContext(ReactApplicationContext reactContext) {

CatalystInstance catalystInstance =

Assertions.assertNotNull(reactContext.getCatalystInstance());

//执行Native Java Module 的 initialize

catalystInstance.initialize();

//重置DevSupportManager实现类的reactContext相关

mDevSupportManager.onNewReactContextCreated(reactContext);

//内存状态回调设置

mMemoryPressureRouter.addMemoryPressureListener(catalystInstance);

//置位生命周期

moveReactContextToCurrentLifecycleState();

//核心方法!!!!

for (ReactRootView rootView : mAttachedRootViews) {

attachMeasuredRootViewToInstance(rootView, catalystInstance);

}

}

到此我们再追一下 mAttachedRootViews 这个列表赋值的地方吧,依旧是这个类的 attachMeasuredRootView(ReactRootView rootView) 方法,然而这个方法唯一被调用的地方在 ReactRootView 的 attachToReactInstanceManager() 中,再次发现 attachToReactInstanceManager 又是在 ReactRootView 已经 measure 的情况下才会触发,所以也就是说 mAttachedRootViews 中保存的都是 ReactRootView。那我们继续回到 XReactInstanceManagerImpl 中 setupReactContext 方法的 attachMeasuredRootViewToInstance(rootView, catalystInstance); 里看看,如下:

private void attachMeasuredRootViewToInstance(

ReactRootView rootView,

CatalystInstance catalystInstance) {

//彻底reset ReactRootView中的UI

// Reset view content as it’s going to be populated by the application content from JS

rootView.removeAllViews();

rootView.setId(View.NO_ID);

//通过UIManagerModule设置根布局为ReactRootView

UIManagerModule uiManagerModule = catalystInstance.getNativeModule(UIManagerModule.class);

int rootTag = uiManagerModule.addMeasuredRootView(rootView);

//设置相关tag

rootView.setRootViewTag(rootTag);

//把Java端启动传递的launchOptions包装成JS用的类型

@Nullable Bundle launchOptions = rootView.getLaunchOptions();

WritableMap initialProps = Arguments.makeNativeMap(launchOptions);

//获取我们startReactApplication设置的JS端入口name,继承ReactActivity的话值为getMainComponentName()设置的

String jsAppModuleName = rootView.getJSModuleName();

//包装相关参数,rootTag告知JS端Native端的ReactRootView是哪个

WritableNativeMap appParams = new WritableNativeMap();

appParams.putDouble(“rootTag”, rootTag);

appParams.putMap(“initialProps”, initialProps);

//核心大招!!!!!React Native真正的启动流程入口是被Java端在这里拉起来的!!!!!!!!

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

}

坚持一下,分析源码就是个苦逼的过程,坚持下来就好了,马上看到希望了;我们知道 AppRegistry.class 是 JS 端暴露给 Java 端的接口方法,所以 catalystInstance.getJSModule(AppRegistry.class) 实质就桥接到 JS 端代码去了,那就去看看 AppRegistry.js 的代码吧,如下:

//JS端对应代码,注意这个变量上面的英文已经交代很详细啦

var AppRegistry = {

//我们JS端自己在index.android.js文件中调用的入口就是:

//AppRegistry.registerComponent(‘TestRN’, () => TestRN);

registerComponent: function(appKey: string, getComponentFunc: ComponentProvider): string {

runnables[appKey] = {

run: (appParameters) =>

renderApplication(getComponentFunc(), appParameters.initialProps, appParameters.rootTag)

};

return appKey;

},

//上面java端 AppRegistry 调用的 JS 端就是这个方法,索引到我们设置的appkey=TestRN字符串的JS入口

runApplication: function(appKey: string, appParameters: any): void {

runnables[appKey].run(appParameters);

},

};

真他妈不容易啊,总算到头了,原来 React Native 是这么被启动起来的。现在回过头来看发现其实主启动流程也就那么回事,还以为很神秘嘻嘻的,现在总算被揭开了。总结一下吧,如下图所示即为整个 React Native 加载主流成的主要情况:

到这里 React Native 的启动流程就分析完了,不过,我猜你看到这里的时候一定会骂我,因为我知道上面的主流程中你会有很多疑惑,这也是我写这篇阅读 RN 源码总结最纠结的地方,因为想尽可能的将主加载流程和通信方式分开来分析,以便做到模块化理解,但是后来发现关联性又很强,揉一起分析更乱套,所以就有了这么一篇很长的文章,前面就当是主流程综述概要分析,细节在下面通信方式分析时会继续提及浅析,所以建议带着上面的疑惑继续向下看完这篇文章再回到 Part 2 RN 启动流程框架浅析 这一部分来看一遍,这样你的疑惑就全部揭开了。

【工匠若水 http://blog.csdn.net/yanbober 未经允许严禁转载,请尊重作者劳动成果。私信联系我】

3 RN Java 调用 JS 端框架浅析

=========================

这是一个悲伤的故事,看源码没有伴,RN 接入也在自己一个人搞,所以搞起来总是挺慢,好在一直在坚持,源码也断断续续在工作之余看了一个多星期,这篇文章也占用了我一个美好的周末时光,有种说不出来的感觉,唉,不扯了,我们现在来看看 RN 中 Java 是如何调用 JS 代码的。

首先,通过上面加载流程或者以前我们自定义 Java & JS 交互模块的经历我们知道 JS 端代码模块对应的 Java 端都是继承 JavaScriptModule 来实现的(可以看上面 reactPackage.createJSModules() 方法,返回的是 JS 端给 Java 端约定好的 JS 模块 Java 实现);要说 Java 端如何调用 JS 端代码就得有个例子,我们就拿上面启动流程中最后 CatalystInstanceImpl.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams); 拉起 JS 端 index.android.js 的 JS 组件入口来分析,这就是一个典型的 Java 端调用 JS 端代码的例子,首先我们可以知道 AppRegistry.java 是继承 JavaScriptModule 的,如下:

public interface AppRegistry extends JavaScriptModule {

void runApplication(String appKey, WritableMap appParameters);

void unmountApplicationComponentAtRootTag(int rootNodeTag);

void startHeadlessTask(int taskId, String taskKey, WritableMap data);

}

然后 AppRegistry.java 是在 CoreModulesPackage 的 createJSModules() 方法中被添加入列表的,CoreModulesPackage 又是在主启动流程的 processPackage() 方法中被包装后加入 JavaScriptModuleRegistry 映射表的,JavaScriptModuleRegistry 映射表又被 Java 层的 CatalystInstanceImpl 接管。所以 Java 调用 JS 方法都是通过 CatalystInstanceImpl.getJSModule(class).methodXXX() 来执行的(我们自己模块调用的话是通过 ReactContext.getJSModule(),因为 ReactContext 在主启动流程中持有了 CatalystInstanceImpl 实例,所以 CatalystInstanceImpl 是不直接对外的),那我们就沿着这条线去观摩一把,如下 CatalystInstanceImpl.java 的 getJSModule 方法:

@Override

public T getJSModule(Class jsInterface) {

//mMainExecutorToken来自于native C++代码

return getJSModule(mMainExecutorToken, jsInterface);

}

@Override

public T getJSModule(ExecutorToken executorToken, Class jsInterface) {

//mJSModuleRegistry就是启动流程中processPackage()方法加进去交给CatalystInstanceImpl托管的JS代码映射表

return Assertions.assertNotNull(mJSModuleRegistry)

.getJavaScriptModule(this, executorToken, jsInterface);

}

接着去 JavaScriptModuleRegistry 映射表中看看 getJavaScriptModule() 方法,如下:

public synchronized T getJavaScriptModule(

CatalystInstance instance,

ExecutorToken executorToken,

Class moduleInterface) {

//module加载的缓存,加载过一次且缓存存在就直接从缓存取

//获取JavaScriptModule模块的方式,以AppRegistry模块获取为例,略叼,动态代理生成获取JS 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, instance, registration));

instancesForContext.put(moduleInterface, interfaceProxy);

return (T) interfaceProxy;

}

从上面这段代码我们可以看见,getJSModule 获取 JsModule 的实质是通过 Java 的动态代理来实现的,同时 JavaScriptModuleRegistration 对 JavaScriptModule 的包装是为了检查实现 JavaScriptModule 接口的类不能存在重载,因为与 JS 端对应,JS 不支持。那我们不妨把视线转移到 JavaScriptModuleInvocationHandler 的 invoke 方法,可以发现实质是调用了 mCatalystInstance.callFunction(executorToken, mModuleRegistration.getName(), method.getName(), jsArgs); 语句,继续跟了一下发现调用了 CatalystInstanceImpl.java 的 native callJSFunction() 方法把相关参数传递到了 C++ 层,额,前面我们知道 CatalystInstanceImpl.cpp 只是 JNI 对于 Android 层适配的特有封装,实质对应了 Common 里 Instance.cpp,而这里的 native callJSFunction() 实质是通过 Instance::callJSFunction() 调用了 NativeToJsBridge::callFunction() 方法,进而放在了 JSCExecutor 的线程队列中触发了 JSCExecutor::callFunction() 方法,我们重点关注下 JSCExecutor::callFunction() 方法,如下:

void JSCExecutor::callFunction(const std::string& moduleId, const std::string& methodId, const folly::dynamic& arguments) {

auto result = [&] {

try {

//m_callFunctionReturnFlushedQueueJS来自于JSCExecutor::bindBridge()方法中初始化,JSCExecutor::bindBridge()是在前面分析启动流程时被调用的,实质是负责通过 Webkit JSC 拿到 JS 端代码的相关对象和方法引用,譬如拿到 JS 端 BatchedBridge.js 的 __fbBatchedBridge 属性与 MessageQueue.js 的 callFunctionReturnFlushedQueue 方法引用。此处实质为调用 MessageQueue.js 的 callFunctionReturnFlushedQueue 方法。

return m_callFunctionReturnFlushedQueueJS->callAsFunction({

Value(m_context, String::createExpectingAscii(moduleId)),

Value(m_context, String::createExpectingAscii(methodId)),

Value::fromDynamic(m_context, std::move(arguments))

});

} catch (…) {

std::throw_with_nested(

std::runtime_error("Error calling function: " + moduleId + “:” + methodId));

}

}();

//调用 native 模块,暂时忽略,下一小节解释,这里重点关注 Java 调用 JS 通信

callNativeModules(std::move(result));

}

既然都说了 m_callFunctionReturnFlushedQueueJS 是 JSCExecutor::bindBridge() 方法中初始化的,实质依赖 Webkit JSC 架起了 JS 代码与 C++ 的桥梁,那我们就去 JS 端看看 MessageQueue.js 的 callFunctionReturnFlushedQueue() 方法,如下:

callFunctionReturnFlushedQueue(module: string, method: string, args: Array) {

guard(() => {

this.__callFunction(module, method, args);

this.__callImmediates();

});

return this.flushedQueue();

}

继续跟下 this.__callFunction(module, method, args),如下:

__callFunction(module: string, method: string, args: Array) {

//_callableModules属性是通过registerCallableModule()方法添加的,而我们以AppRegistry.js为例,可以看见AppRegistry.js中有调用BatchedBridge.registerCallableModule(‘AppRegistry’, AppRegistry);把自己注册进去,BatchedBridge.js是与MessageQueue.js绑死的。

//说白了,这里就是在 JS 端的 Modules 中查映射表找到 AppRegistry.js

const moduleMethods = this._callableModules[module];

//拿到Java端调用的对应JS端指定Module的方法,譬如AppRegistry.js的runApplication()方法

const result = moduleMethods[method].apply(moduleMethods, args);

Systrace.endEvent();

return result;

}

握草,React Native Java 层调用 JS 层通信原来原来就这么回事;实质就是 Java 与 JS 端都准备好一个 Module 映射表,然后当 Java 端调用 JS 代码时 Java 端通过查表动态代理创建一个与 JS 对应的 Module 对象,当调用这个 Module 的方法时 Java 端通过动态代理的 invoke 方法触发 C++ 层,层层调用后通过 JSCExecutor 执行 JS 端队列中的映射查表找到 JS 端方法进行调用;同时会发现在 JSCExecutor 中每次 Java 调用 JS 之后会进行 Java 端的一个回调(从 JS 层的 MessageQueue.js 中获得累积的 JS Call)。为了不迷糊,我们对这一阶段总结如下图:

不多说了,一切都在上面图里,所以这时候如果你再回头去想第一部分 RN 启动流程源码浅析的疑惑点就完全明白了,也就串起来了。

【工匠若水 http://blog.csdn.net/yanbober 未经允许严禁转载,请尊重作者劳动成果。私信联系我】

4 RN JS 调用 Java 端框架浅析

=========================

上面已经总结了 React Native 的启动流程与 Java 端代码调用 JS 端代码的通信流程,下面我们再来分析 JS 端代码调用 Java 端代码的通信流程,这样整个 React Native 核心框架的三大问题都得到浅析了,方便日后裁剪 React Native。既然要分析 JS 调用 Java 端通信框架了,我们一样有必要先找到一个例子来做突破口,那我们就以 RN 官方的《Native Modules 》为例来说明,因为这是我们经常要封装且最熟悉的东西,可以看见文档中举了一个封装 ToastAndroid 的例子,那就拿它下手吧。我们在 JS 端使用 Android 端封装的 Java 模块时是如下这样用的:

//通过NativeModules拿到ToastAndroid

import { NativeModules } from ‘react-native’;

module.exports = NativeModules.ToastAndroid;

//使用的地方在JS中相关逻辑处调用,官方文档标准

import ToastAndroid from ‘./ToastAndroid’;

ToastAndroid.show(‘Awesome’, ToastAndroid.SHORT);

可以看见,JS 调用 Java 的第一步就是通过 JS 端的 NativeModules 拿到 相关的 Java 映射 Module,那我们就去 JS 的 NativeModules 里看看吧,如下:

//暂时只关注NativeModules.js中精彩的

//JSC全局唯一global添加一个属性,赋值为NativeModules.js中方法引用

global.__fbGenNativeModule = genModule;

let NativeModules : {[moduleName: string]: Object} = {};

//依据是否定义global.nativeModuleProxy属性来决定怎么获取。

//nativeModuleProxy属性实质是在主加载流程中JSCExecutor::JSCExecutor()构造时通过installGlobalProxy(m_context, “nativeModuleProxy”, exceptionWrapMethod<&JSCExecutor::getNativeModule>());创建的,所以当JS调用NativeModules时实质为JSCExecutor::getNativeModule()方法

if (global.nativeModuleProxy) {

NativeModules = global.nativeModuleProxy;

} else {

//只关注精彩的主线,DEV等模式的咱们不管

}

module.exports = NativeModules;

我们看下 JSCExecutor::getNativeModule() 方法,如下:

JSValueRef JSCExecutor::getNativeModule(JSObjectRef object, JSStringRef propertyName) {

//m_nativeModules来源于JsToNativeBridge的getModuleRegistry()方法,实质被转换为了JSCNativeModules.cpp

return m_nativeModules.getModule(m_context, propertyName);

}

继续跟踪上面 JSCNativeModules.cpp 的 getModule(m_context, propertyName) 可以发现其核心就是调用 JSCNativeModules::createModule(const std::string& name, JSContextRef context) 方法,而这个方法里实质是通过 JSC 获取全局设置的 JS 属性,然后通过 JNI 查找 Java 端映射表再触发 JS 端相关方法:

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频 如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)

最后

由于文章篇幅原因,我只把面试题列了出来,详细的答案,我整理成了一份PDF文档,这份文档还包括了还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 ,帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习。

pertyName) {

//m_nativeModules来源于JsToNativeBridge的getModuleRegistry()方法,实质被转换为了JSCNativeModules.cpp

return m_nativeModules.getModule(m_context, propertyName);

}

继续跟踪上面 JSCNativeModules.cpp 的 getModule(m_context, propertyName) 可以发现其核心就是调用 JSCNativeModules::createModule(const std::string& name, JSContextRef context) 方法,而这个方法里实质是通过 JSC 获取全局设置的 JS 属性,然后通过 JNI 查找 Java 端映射表再触发 JS 端相关方法:

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。 [外链图片转存中…(img-DJnE6dYo-1710671348799)] [外链图片转存中…(img-gTPSmzm6-1710671348800)] [外链图片转存中…(img-uivx9oar-1710671348800)] [外链图片转存中…(img-prTsh9Vy-1710671348801)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频 如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android) [外链图片转存中…(img-QcCRpaZP-1710671348801)]

最后

由于文章篇幅原因,我只把面试题列了出来,详细的答案,我整理成了一份PDF文档,这份文档还包括了还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 ,帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习。

需要的朋友可以私信我【答案】或者点击这里免费领取

相关阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: