reactjs react native-native 的headless.js Java API 的代码怎么使用

6504人阅读
react-native/web周边(3)
【工匠若水
未经允许严禁转载,请尊重作者劳动成果。】
有了前面和两篇文章的学习我们 React Native 已经能够基本接入处理一些事情了,那接下来的事情就是渐渐理解 RN 框架的一些东西,以便裁剪和对 RN 有个更深入的认识,所以本篇总结了我这段时间阅读源码的一些感触,主要总结了 React Native 启动流程、JS 调用 Java 流程、Java 调用 JS 流程。
涉及到源码分析了,所以有必要先交代下相关源码版本,以便引来不必要疑惑,如下:
"dependencies": {
"react": "15.3.2",
"react-native": "0.37.0"
首先通过前面的踩坑经历和编译流程浅析(编译流程已经暴露很多细节)我们能意识到 React Native 的大致框架流程应该是如下这样的:
也就是说其实需要我们编写代码是 Java 端(少)和 JS 端(多),其他的基本不变的,作为桥梁的核心是 C/C++ 来处理的,同时 JS(JSX)代码又是通过 Virtual DOM 来进行虚拟适应的,所以才有了 React Native 官方放话的 Learn once, do anywhere. 之说。下面我们就来解析下这个神奇的 React Native Android 框架吧。
【工匠若水
未经允许严禁转载,请尊重作者劳动成果。】
2 RN 启动流程框架浅析
还记得我们在中是怎么集成的 RN 吗?集成 RN 无非就是通过继承 ReactActivity 或者自己通过 ReactRootView 进行处理,但是实质都是触发了 ReactRootView 的 startReactApplication 方法,所以我们整个启动流程的核心入口就是这玩意;下面为了一致,我们直接从 ReactActivityDelegate 类的 onCreate 方法进行启动分析(分析源码本来就比较枯燥,坚持看下去收获是巨大的),如下:
public class ReactActivityDelegate {
protected void onCreate(Bundle savedInstanceState) {
if (getReactNativeHost().getUseDeveloperSupport() && Build.VERSION.SDK_INT &= 23) {
if (mMainComponentName != null) {
loadApp(mMainComponentName);
protected void loadApp(String appKey) {
if (mReactRootView != null) {
throw new IllegalStateException("Cannot loadApp while app is already running.");
mReactRootView = createRootView();
mReactRootView.startReactApplication(
getReactNativeHost().getReactInstanceManager(),
getLaunchOptions());
getPlainActivity().setContentView(mReactRootView);
可以看见,ReactActivityDelegate 只是一个抽出来的封装,上面的实质就是 new 了一个 ReactRootView(实质是 Android 的 FrameLayout),接着调用 ReactRootView 的 startReactApplication 方法,完事就像常规 Android 代码一样直接通过 Activity 的 setContentView 方法把 View 设置进去。所以可以看出来,RN 的神秘之处一定在于 ReactRootView 中,Activity 对于 RN 来说只是为了让 RN 依附符合 Android 的框架而已,所以说,说白了 RN 依旧是标准 Android,因此在我们集成开发中我们可以选择整个界面(包含多级跳转)都用 React Native 实现,或者一个 Android 现有界面中部分采用 React Native 实现,因为这货就是一个 View,爱咋咋地,具体如下所示:
既然明白了 RN 就是个 View,那就接着看看 ReactRootView 呗,如下:
React Native 的 Root View,负责监听标准 Android 的 View 相关各种东东及事件分发和子 View 渲染等。
public class ReactRootView extends SizeMonitoringFrameLayout implements RootView {
public void startReactApplication(ReactInstanceManager reactInstanceManager, String moduleName) {
startReactApplication(reactInstanceManager, moduleName, null);
public void startReactApplication(
ReactInstanceManager reactInstanceManager,
String moduleName,
@Nullable Bundle launchOptions) {
UiThreadUtil.assertOnUiThread();
if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
mReactInstanceManager.createReactContextInBackground();
if (mWasMeasured) {
attachToReactInstanceManager();
可以看见,ReactRootView 果然是个牛逼的类,我也不多解释了,大段的英文注释已经交代很清楚用途和地位了,我们直接看上面代码的 startReactApplication 方法吧,可以看见他又调用了一个三个参数的同名方法,具体这三个参数来历如下(也是我们自己集成 RN 时手动 builder 模式创建的):
1. reactInstanceManager: 大内总管接口类,提供一个构造者模式的初始化 Builder,实现类是 XReactInstanceManagerImpl,这类也是我们在集成 RN 时 new ReactRootView 的之前自己创建的。
2. moduleName: 与 JS 代码约定的 String 类型识别 name,JS 端通过 AppRegistry.registerComponent 方法设置这个 name,Java 端重写基类的 getMainComponentName 方法设置这个 name,这样两边入口就对上了。
3. launchOptions: 这里默认是 null 的,如果自己不继承 ReactActivity 而自己实现的话可以通过这个参数在 startActivity 时传入一些参数到 JS 代码,用来依据参数初始化 JS 端代码。
这些参数都初始化传递好了以后,可以看见接着调用了 mReactInstanceManager 的 createReactContextInBackground 方法,mReactInstanceManager 就是上面说的第一个参数,实质是通过一个构造者模式创建的,实现类是 XReactInstanceManagerImpl,所以我们直接跳到 XReactInstanceManagerImpl 的 createReactContextInBackground 方法看看,如下:
public void createReactContextInBackground() {
recreateReactContextInBackgroundInner();
private void recreateReactContextInBackgroundInner() {
UiThreadUtil.assertOnUiThread();
if (mUseDeveloperSupport && mJSMainModuleName != null) {
recreateReactContextInBackgroundFromBundleLoader();
private void recreateReactContextInBackgroundFromBundleLoader() {
recreateReactContextInBackground(
new JSCJavaScriptExecutor.Factory(mJSCConfig.getConfigMap()),
mBundleLoader);
private void recreateReactContextInBackground(
JavaScriptExecutor.Factory jsExecutorFactory,
JSBundleLoader jsBundleLoader) {
UiThreadUtil.assertOnUiThread();
ReactContextInitParams initParams =
new ReactContextInitParams(jsExecutorFactory, jsBundleLoader);
if (mReactContextInitAsyncTask == null) {
mReactContextInitAsyncTask = new ReactContextInitAsyncTask();
mReactContextInitAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, initParams);
mPendingReactContextInitParams = initP
通过上面注释可以看见,我们的热更新面纱也是从这个地方开始揭晓的,ReactInstanceManager 的 setJSBundleFile 如下:
* Path to the JS bundle file to be loaded from the file system.
* Example: {@code "assets://index.android.js" or "/sdcard/main.jsbundle"}
public Builder setJSBundleFile(String jsBundleFile) {
if (jsBundleFile.startsWith("assets://")) {
mJSBundleAssetUrl = jsBundleF
mJSBundleLoader = null;
return this;
return setJSBundleLoader(JSBundleLoader.createFileLoader(jsBundleFile));
我们先记住这个提示,后面再边分析主加载流程边插入介绍热更新的原理,所以我们还是把思路先回到 XReactInstanceManagerImpl 内部类的 ReactContextInitAsyncTask 上,如下:
private final class ReactContextInitAsyncTask extends
AsyncTask&ReactContextInitParams, Void, Result&ReactApplicationContext&& {
protected Result&ReactApplicationContext& doInBackground(ReactContextInitParams... params) {
JavaScriptExecutor jsExecutor = params[0].getJsExecutorFactory().create();
return Result.of(createReactContext(jsExecutor, params[0].getJsBundleLoader()));
} catch (Exception e) {
return Result.of(e);
protected void onPostExecute(Result&ReactApplicationContext& result) {
setupReactContext(result.get());
} catch (Exception e) {
mDevSupportManager.handleException(e);
} finally {
mReactContextInitAsyncTask = null;
可以看见,这就是典型的 AsyncTask 用法,我们先关注 doInBackground 方法,onPostExecute 方法等会回头再看;doInBackground 中首先把上面封装的 ReactContextInitParams 对象里 JavaScriptExecutor.Factory 工厂对象拿到,接着调用了工厂类的 create 方法创建 JavaScriptExecutor 抽象类的实现类 JSCJavaScriptExecutor 对象(因为上面分析 recreateReactContextInBackground 方法时第一个参数传入的是 new JSCJavaScriptExecutor.Factory(mJSCConfig.getConfigMap()))。接着往下执行了 createReactContext 方法,两个参数分别是前面封装的 ReactContextInitParams 对象中的 JSCJavaScriptExecutor 实例和 JSBundleLoader.createAssetLoader 静态方法创建的匿名内部类 JSBundleLoader 对象(热更新的话可能是另一个 Loader,参见前面分析);createReactContext 方法如下(有点长,但是句句核心啊):
private ReactApplicationContext createReactContext(
JavaScriptExecutor jsExecutor,
JSBundleLoader jsBundleLoader) {
mSourceUrl = jsBundleLoader.getSourceUrl();
List&ModuleSpec& moduleSpecs = new ArrayList&&();
Map&Class, ReactModuleInfo& reactModuleInfoMap = new HashMap&&();
JavaScriptModuleRegistry.Builder jsModulesBuilder = new JavaScriptModuleRegistry.Builder();
final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
if (mUseDeveloperSupport) {
reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager);
CoreModulesPackage coreModulesPackage =
new CoreModulesPackage(this, mBackBtnHandler, mUIImplementationProvider);
processPackage(
coreModulesPackage,
reactContext,
moduleSpecs,
reactModuleInfoMap,
jsModulesBuilder);
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
for (ReactPackage reactPackage : mPackages) {
Systrace.beginSection(
TRACE_TAG_REACT_JAVA_BRIDGE,
"createAndProcessCustomReactPackage");
processPackage(
reactPackage,
reactContext,
moduleSpecs,
reactModuleInfoMap,
jsModulesBuilder);
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
NativeModuleRegistry nativeModuleR
nativeModuleRegistry = new NativeModuleRegistry(moduleSpecs, reactModuleInfoMap);
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_END);
NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null
? mNativeModuleCallExceptionHandler
: mDevSupportM
CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
.setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
.setJSExecutor(jsExecutor)
.setRegistry(nativeModuleRegistry)
.setJSModuleRegistry(jsModulesBuilder.build())
.setJSBundleLoader(jsBundleLoader)
.setNativeModuleCallExceptionHandler(exceptionHandler);
final CatalystInstance catalystI
catalystInstance = catalystInstanceBuilder.build();
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_END);
if (mBridgeIdleDebugListener != null) {
catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);
reactContext.initializeWithInstance(catalystInstance);
catalystInstance.runJSBundle();
return reactC
可以发现,上面这段代码做的事情真特么多,不过总的来说 createReactContext() 方法做的都是一些取数据组表放表的过程,核心就是通过 ReactPackage 实现类的 createNativeModules()、createJSModules() 等方法把所有 NativeModule 包装后放入 NativeModuleRegistry 及 JavaScriptModule 包装后放入 JavaScriptModuleRegistry,然后把这两张映射表交给 CatalystInstanceImpl,同时包装创建 ReactContext 对象,然后通过 CatalystInstanceImpl 的 runJSBundle() 方法把 JS bundle 文件的 JS 代码加载进来等待 Task 结束以后调用 JS 入口进行渲染 RN。既然这样就去看看 CatalystInstanceImpl 的 build 方法中调用的 CatalystInstanceImpl 构造方法到底干了哪些鸟事,如下:
public class CatalystInstanceImpl implements CatalystInstance {
private final HybridData mHybridD
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.");
mHybridData = initHybrid();
mReactQueueConfiguration = ReactQueueConfigurationImpl.create(
ReactQueueConfigurationSpec,
new NativeExceptionHandler());
mBridgeIdleListeners = new CopyOnWriteArrayList&&();
mJavaRegistry =
mJSModuleRegistry = jsModuleR
mJSBundleLoader = jsBundleL
mNativeModuleCallExceptionHandler = nativeModuleCallExceptionH
mTraceListener = new JSProfilerTraceListener(this);
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(
jni&ReactCallback& callback,
JavaScriptExecutorHolder* jseh,
jni&JavaMessageQueueThread& jsQueue,
jni&JavaMessageQueueThread& moduleQueue,
ModuleRegistryHolder* mrh) {
instance_-&initializeBridge(folly&JInstanceCallback&(callback),
jseh-&getExecutorFactory(),
folly&JMessageQueueThread&(jsQueue),
folly&JMessageQueueThread&(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(); 方法,这个方法实现如下:
public void runJSBundle() {
mJSBundleHasLoaded = true;
mJSBundleLoader.loadScript(CatalystInstanceImpl.this);
通过注释我们假设 Loader 是默认的,也即 JSBundleLoader 类的如下方法:
public static JSBundleLoader createAssetLoader(
final Context context,
final String assetUrl) {
return new JSBundleLoader() {
public void loadScript(CatalystInstanceImpl instance) {
instance.loadScriptFromAssets(context.getAssets(), assetUrl);
public String getSourceUrl() {
return assetU
可以看见,它实质又调用了 CatalystInstanceImpl 的 loadScriptFromAssets 方法,我们继续跟踪 CatalystInstanceImpl 的这个方法吧,如下:
native void loadScriptFromAssets(AssetManager assetManager, String assetURL);
loadScriptFromAssets 既然是一个 native 方法,我们去 CatalystInstanceImpl.cpp 看下这个方法的实现,如下:
void CatalystInstanceImpl(jobject assetManager,
const std& assetURL) {
const int kAssetsLength = 9;
auto sourceURL = assetURL.substr(kAssetsLength);
auto manager = react(assetManager);
auto script = react(manager, sourceURL);
if (JniJSModulesUnbundle(manager, sourceURL)) {
instance_-&loadUnbundle(
folly&JniJSModulesUnbundle&(manager, sourceURL),
std(script),
sourceURL);
instance_-&loadScriptFromString(std(script), sourceURL);
看来还没到头,这货又走到了 ReactCommon 目录下 Instance 实例的 loadScriptFromString 方法去了(由此可以看出来前面 ReactNativeAndroid 目录下的 jni 代码都是 Android 平台特有的封装,直到 ReactCommon 才是通用的),如下:
void Instance(std&const JSBigString& string,
std sourceURL) {
callback_-&incrementPendingJSCalls();
SystraceSection s("reactbridge_xplat_loadScriptFromString",
"sourceURL", sourceURL);
nativeToJsBridge_-&loadApplication(nullptr, std(string), std(sourceURL));
妈的,没完没了了,继续跟吧,到 Common 的 NativeToJsBridge.cpp 看看 loadApplication 方法吧,如下:
void NativeToJsBridge::loadApplication(
std::unique_ptr&JSModulesUnbundle& unbundle,
std::unique_ptr&const JSBigString& startupScript,
std::string startupScriptSourceURL) {
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));
executor-&loadApplicationScript(std::move(*startupScript),
std::move(startupScriptSourceURL));
靠靠靠,还不到头,又特么绕到 JSExecutor 的 loadApplicationScript 方法里面去了,继续跟吧(这个 executor 是 runOnExecutorQueue 方法中回传的一个 map 中取的,实质是 OnLoad 中 JSCJavaScriptExecutorHolder 对应,也即 java 中 JSCJavaScriptExecutor,所以 JSExecutor 实例为 JSCExecutor.cpp 中实现),如下:
void JSCExecutor(std&const JSBigString& script, std sourceURL) throw(JSException) {
SystraceSection s("JSCExecutor::loadApplicationScript",
"sourceURL", sourceURL);
String jsScript = jsStringFromBigString(*script);
String jsSourceURL(sourceURL.c_str());
evaluateScript(m_context, jsScript, jsSourceURL);
bindBridge();
去他大爷的,没完没了了,继续看看 bindBridge() 方法和 flush() 方法,如下:
void JSCExecutor() throw(JSException) {
auto global = Object(m_context);
auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");
auto batchedBridge = batchedBridgeValue.asObject();
m_callFunctionReturnFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnFlushedQueue").asObject();
m_invokeCallbackAndReturnFlushedQueueJS = batchedBridge.getProperty("invokeCallbackAndReturnFlushedQueue").asObject();
m_flushedQueueJS = batchedBridge.getProperty("flushedQueue").asObject();
m_callFunctionReturnResultAndFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnResultAndFlushedQueue").asObject();
void JSCExecutor() {
SystraceSection s("JSCExecutor::flush");
callNativeModules(m_flushedQueueJS-&callAsFunction({}));
void JSCExecutor(Value&& value) {
SystraceSection s("JSCExecutor::callNativeModules");
auto calls = value.toJSONString();
m_delegate-&callNativeModules(*this, folly(calls), true);
} catch (...) {
卧槽!又绕回到了 JsToNativeBridge.cpp 的 callNativeModules 方法,那就看下吧,如下:
void callNativeModules(
JSExecutor& executor, folly::dynamic&& calls, bool isEndOfBatch) override {
ExecutorToken token = m_nativeToJs-&getTokenForExecutor(executor);
m_nativeQueue-&runOnQueue([this, token, calls=std::move(calls), isEndOfBatch] () mutable {
for (auto& call : react::parseMethodCalls(std::move(calls))) {
m_registry-&callNativeMethod(
token, call.moduleId, call.methodId, std::move(call.arguments), call.callId);
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());
catalystInstance.initialize();
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) {
rootView.removeAllViews();
rootView.setId(View.NO_ID);
UIManagerModule uiManagerModule = catalystInstance.getNativeModule(UIManagerModule.class);
int rootTag = uiManagerModule.addMeasuredRootView(rootView);
rootView.setRootViewTag(rootTag);
@Nullable Bundle launchOptions = rootView.getLaunchOptions();
WritableMap initialProps = Arguments.makeNativeMap(launchOptions);
String jsAppModuleName = rootView.getJSModuleName();
WritableNativeMap appParams = new WritableNativeMap();
appParams.putDouble("rootTag", rootTag);
appParams.putMap("initialProps", initialProps);
catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);
坚持一下,分析源码就是个苦逼的过程,坚持下来就好了,马上看到希望了;我们知道 AppRegistry.class 是 JS 端暴露给 Java 端的接口方法,所以 catalystInstance.getJSModule(AppRegistry.class) 实质就桥接到 JS 端代码去了,那就去看看 AppRegistry.js 的代码吧,如下:
var AppRegistry = {
registerComponent: function(appKey: string, getComponentFunc: ComponentProvider): string {
runnables[appKey] = {
run: (appParameters) =&
renderApplication(getComponentFunc(), appParameters.initialProps, appParameters.rootTag)
return appK
runApplication: function(appKey: string, appParameters: any): void {
runnables[appKey].run(appParameters);
真他妈不容易啊,总算到头了,原来 React Native 是这么被启动起来的。现在回过头来看发现其实主启动流程也就那么回事,还以为很神秘嘻嘻的,现在总算被揭开了。总结一下吧,如下图所示即为整个 React Native 加载主流成的主要情况:
到这里 React Native 的启动流程就分析完了,不过,我猜你看到这里的时候一定会骂我,因为我知道上面的主流程中你会有很多疑惑,这也是我写这篇阅读 RN 源码总结最纠结的地方,因为想尽可能的将主加载流程和通信方式分开来分析,以便做到模块化理解,但是后来发现关联性又很强,揉一起分析更乱套,所以就有了这么一篇很长的文章,前面就当是主流程综述概要分析,细节在下面通信方式分析时会继续提及浅析,所以建议带着上面的疑惑继续向下看完这篇文章再回到 Part 2 RN 启动流程框架浅析 这一部分来看一遍,这样你的疑惑就全部揭开了。
【工匠若水
未经允许严禁转载,请尊重作者劳动成果。】
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 方法:
public &T extends JavaScriptModule& T getJSModule(Class&T& jsInterface) {
return getJSModule(mMainExecutorToken, jsInterface);
public &T extends JavaScriptModule& T getJSModule(ExecutorToken executorToken, Class&T& jsInterface) {
return Assertions.assertNotNull(mJSModuleRegistry)
.getJavaScriptModule(this, executorToken, jsInterface);
接着去 JavaScriptModuleRegistry 映射表中看看 getJavaScriptModule() 方法,如下:
public synchronized &T extends JavaScriptModule& T getJavaScriptModule(
CatalystInstance instance,
ExecutorToken executorToken,
Class&T& moduleInterface) {
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) interfaceP
从上面这段代码我们可以看见,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 = [&] {
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));
callNativeModules(std::move(result));
既然都说了 m_callFunctionReturnFlushedQueueJS 是 JSCExecutor::bindBridge() 方法中初始化的,实质依赖 Webkit JSC 架起了 JS 代码与 C++ 的桥梁,那我们就去 JS 端看看 MessageQueue.js 的 callFunctionReturnFlushedQueue() 方法,如下:
callFunctionReturnFlushedQueue(module: string, method: string, args: Array&any&) {
guard(() =& {
this.__callFunction(module, method, args);
this.__callImmediates();
return this.flushedQueue();
继续跟下 this.__callFunction(module, method, args),如下:
__callFunction(module: string, method: string, args: Array&any&) {
const moduleMethods = this._callableModules[module];
const result = moduleMethods[method].apply(moduleMethods, args);
Systrace.endEvent();
握草,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 启动流程源码浅析的疑惑点就完全明白了,也就串起来了。
【工匠若水
未经允许严禁转载,请尊重作者劳动成果。】
4 RN JS 调用 Java 端框架浅析
上面已经总结了 React Native 的启动流程与 Java 端代码调用 JS 端代码的通信流程,下面我们再来分析 JS 端代码调用 Java 端代码的通信流程,这样整个 React Native 核心框架的三大问题都得到浅析了,方便日后裁剪 React Native。既然要分析 JS 调用 Java 端通信框架了,我们一样有必要先找到一个例子来做突破口,那我们就以 RN 官方的为例来说明,因为这是我们经常要封装且最熟悉的东西,可以看见文档中举了一个封装 ToastAndroid 的例子,那就拿它下手吧。我们在 JS 端使用 Android 端封装的 Java 模块时是如下这样用的:
from 'react-native';
module.exports = NativeModules.ToastA
import ToastAndroid from './ToastAndroid';
ToastAndroid.show('Awesome', ToastAndroid.SHORT);
可以看见,JS 调用 Java 的第一步就是通过 JS 端的 NativeModules 拿到 相关的 Java 映射 Module,那我们就去 JS 的 NativeModules 里看看吧,如下:
global.__fbGenNativeModule = genM
let NativeModules : {[moduleName: string]: Object} = {};
if (global.nativeModuleProxy) {
NativeModules = global.nativeModuleP
module.exports = NativeM
我们看下 JSCExecutor::getNativeModule() 方法,如下:
JSValueRef JSCExecutor(JSObjectRef object, JSStringRef propertyName) {
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 端相关方法:
folly&Object& JSCNativeModules(const std& name, JSContextRef context) {
m_genNativeModuleJS = global.getProperty("__fbGenNativeModule").asObject();
m_genNativeModuleJS-&makeProtected();
auto result = m_moduleRegistry-&getConfig(name);
if (!result.hasValue()) {
Value moduleInfo = m_genNativeModuleJS-&callAsFunction({
Value(context, result-&config),
JSValueMakeNumber(context, result-&index)
return moduleInfo.asObject().getProperty("module").asObject();
上面主要分两步,通过 C++ 获取 Java 层映射表、通过 JSC 调用 JS 端方法,我们先看下 ModuleRegistry::getConfig(const std::string& name) 源码,如下:
folly::Optional&ModuleConfig& ModuleRegistry::getConfig(const std::string& name) {
NativeModule* module = modules_[it-&second].get();
folly::dynamic config = folly::dynamic::array(name);
config.push_back(module-&getConstants());
std::vector&MethodDescriptor& methods = module-&getMethods();
folly::dynamic methodNames = folly::dynamic::array;
folly::dynamic promiseMethodIds = folly::dynamic::array;
folly::dynamic syncMethodIds = folly::dynamic::array;
for (auto& descriptor : methods) {
methodNames.push_back(std::move(descriptor.name));
if (descriptor.type == "promise") {
promiseMethodIds.push_back(methodNames.size() - 1);
} else if (descriptor.type == "sync") {
syncMethodIds.push_back(methodNames.size() - 1);
if (!methodNames.empty()) {
config.push_back(std::move(methodNames));
if (!promiseMethodIds.empty() || !syncMethodIds.empty()) {
config.push_back(std::move(promiseMethodIds));
if (!syncMethodIds.empty()) {
config.push_back(std::move(syncMethodIds));
return ModuleConfig({it-&second, config});
真相渐渐浮出水面了,我们继续回到前面 JSCNativeModules::createModule(const std::string& name, JSContextRef context) 中看看最后调用的 NativeModules.js 中的 global.__fbGenNativeModule = genModule 方法,如下:
function genModule(config: ?ModuleConfig, moduleID: number): ?{name: string, module?: Object} {
const [moduleName, constants, methods, promiseMethods, syncMethods] =
const module = {};
methods && methods.forEach((methodName, methodID) =& {
const isPromise = promiseMethods && arrayContains(promiseMethods, methodID);
const isSync = syncMethods && arrayContains(syncMethods, methodID);
invariant(!isPromise || !isSync, 'Cannot have a method that is both async and a sync hook');
const methodType = isPromise ? 'promise' : isSync ? 'sync' : 'async';
module[methodName] = genMethod(moduleID, methodID, methodType);
Object.assign(module, constants);
return { name: moduleName, module };
到此 JS 调用 Java 的准备工作已经就绪了(即 JS 是如何拿到 Native 映射表与方法的),JSC会准备好一个 JS 使用的 NativeModule 对象。
有了 JS 端的 Module 映射对象,访问就变得明朗了许多;还记得刚刚 NativeModules.js 中 genMethod() 方法生成的 JS Module 的方法属性吗?当我们 JS 真正调用 ToastAndroid.show(‘Awesome’, ToastAndroid.SHORT); 时实质就是调用 genMethod() 设置的方法;那我们就仔细看看这个
genMethod() 方法,可以发现它是依据调用的 JS 方法是不是 promise、sync 等走不同逻辑,但是主线核心都是调用了 BatchedBridge.enqueueNativeCall() 方法,那我们看看 MessageQueue.js 的 enqueueNativeCall 方法,如下:
enqueueNativeCall(moduleID: number, methodID: number, params: Array&any&, onFail: ?Function, onSucc: ?Function) {
this._callID++;
this._queue[MODULE_IDS].push(moduleID);
this._queue[METHOD_IDS].push(methodID);
this._queue[PARAMS].push(params);
const 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 =
这时候 JS 端调用 Java 通信的 JS 端调用流程就结束了,主要就是通过 JSC 桥接获取 Java 端 Module 的映射表转换为 JS NativeModule 的属性和相关方法,当 JS 端通过 NativeModule.XXX.method(); 使用时实质就是把 method 方法扔进了 JS 的队列,然后在队列中分两种情况,一种是方法调用超过 5ms 时直接触发 nativeFlushQueueImmediate 方法,另一种是当 Java 调用 JS 时也会把之前队列里存的方法调用通过 JSCExecutor::flush()
处理(这回就明白上面分析启动流程为毛 Java 拉起 JS 后又来了一个反回调了吧)。
那我们先看看 JS 直接触发 nativeFlushQueueImmediate 的流程吧,由于 JS 端调用了 global.nativeFlushQueueImmediate 方法,所以实质是通过 JSC 调用了 C++ 的 JSCExecutor::nativeFlushQueueImmediate(size_t argumentCount, const JSValueRef arguments[]) 方法,因为在启动流程中 C++ 初始化 JSCExecutor 对象时通过 initOnJSVMThread() 调用 JSCExecutor::initOnJSVMThread() 进而调用 installGlobalFunction() 方法通过 JSC 把它已经关联给了 JS 。回过头发现 JSCExecutor::nativeFlushQueueImmediate 实质调用了 JSCExecutor::flushQueueImmediate(Value&& queue) 方法,进而调用了 JsToNativeBridge 的 callNativeModules(JSExecutor& executor, folly::dynamic&& calls, bool isEndOfBatch) 方法,只是 isEndOfBatch=false 而已。
接着我们再看下 Java 调用 JS 时把之前队列里存的方法通过 JSCExecutor::flush() 调用的情况,会发现其实质也是调用了 callNativeModules(m_flushedQueueJS-&callAsFunction({})) 方法,m_flushedQueueJS-&callAsFunction({})就是调用 MessageQueue.js 中的 flushedQueue() 方法,得到 JS 队列中睡觉的方法,然后传给了 callNativeModules 方法,接着发现实质也是调用了 JsToNativeBridge 的 callNativeModules(JSExecutor& executor, folly::dynamic&& calls, bool isEndOfBatch) 方法,只是 isEndOfBatch=true 而已。
这下就有意思了,两个分支实质原理是一致的,那我们直接看下 JsToNativeBridge 的 callNativeModules 方法:
void callNativeModules(
JSExecutor& executor, folly::dynamic&& calls, bool isEndOfBatch) override {
ExecutorToken token = m_nativeToJs-&getTokenForExecutor(executor);
m_nativeQueue-&runOnQueue([this, token, calls=std::move(calls), isEndOfBatch] () mutable {
for (auto& call : react::parseMethodCalls(std::move(calls))) {
m_registry-&callNativeMethod(
token, call.moduleId, call.methodId, std::move(call.arguments), call.callId);
if (isEndOfBatch) {
m_callback-&onBatchComplete();
m_callback-&decrementPendingJSCalls();
没啥说的,拿出 JS 队列里存在的 JS 调用 Java 的所有方法通过 ModuleRegistry::callNativeMethod 方法遍历调用,那就去看看这个方法,如下:
void ModuleRegistry::callNativeMethod(ExecutorToken token, unsigned int moduleId, unsigned int methodId,
folly::dynamic&& params, int callId) {
modules_[moduleId]-&invoke(token, methodId, std::move(params));
那就继续去看看 C++ 的 ModuleRegistryHolder 构造方法中包装 Java Module 的 C++ 的 NativeModule 的子类 JavaNativeModule 的 invoke 方法吧,如下:
class JavaNativeModule : public NativeModule {
void invoke(ExecutorToken token, unsigned int reactMethodId, folly::dynamic&& params) override {
static auto invokeMethod =
wrapper_-&getClass()-&getMethod&void(JExecutorToken::javaobject, jint, ReadableNativeArray::javaobject)&("invoke");
invokeMethod(wrapper_, JExecutorToken::extractJavaPartFromToken(token).get(), static_cast&jint&(reactMethodId),
ReadableNativeArray::newObjectCxxArgs(std::move(params)).get());
去他奶奶的,这不就是调用 Java 的对应方法么,直接到 JNI 映射的 Java 类中看这个方法,如下:
class JavaModuleWrapper {
@DoNotStrip
public void invoke(ExecutorToken token, int methodId, ReadableNativeArray parameters) {
if (mMethods == null || methodId &= mMethods.size()) {
mMethods.get(methodId).invoke(mCatalystInstance, token, parameters);
喔噢!明朗了!就这样 JS 就调用了 Java 模块的方法。简单通过框架流程图来总结回顾下这个流程吧,如下:
【工匠若水
未经允许严禁转载,请尊重作者劳动成果。】
此刻只想呵呵,真特么不容易,Java、C++、JS 跳来跳去的看,总算基本搞明白这三大核心主体知识点了,有了上面三个仔细的分析下面对上面三个来个综合总结,按照我个人阅读完 React Native 这一部份源码后的核心总结如下:
Java层 ReactContext(ReactApplicationContext): React Native 封装后的 Android Context,通过其访问设置 RN 包装起来的核心类实现等;
Java层 ReactInstanceManager(ReactInstanceManagerImpl): RN对 Android 层暴露的大内总管,负责掌管 CatalystInstanceImpl 实例、ReactRootView、Activity 生命周期等;
Java/C++层 CatalystInstance(CatalystInstanceImpl): RN Java、C++、JS通信总舵主,统管 JS、Java 核心 Module 映射表、回调等等,三端入口与桥梁;
C++层 NativeToJsBridge: Java 调用 JS 的桥梁,用来调用 JS Module、回调 Java(通过JsToNativeBridge)等;
C++层 JsToNativeBridge: JS 调用 Java 的桥梁,用来调用 Java Module等;
C++层 JSCExecutor: 掌管 Webkit 的 JavaScriptCore,JS 与 C++ 的转换桥接都在这里中转处理;
JS层 MessageQueue: 队列栈,用来处理 JS 的调用队列、调用 Java 或者 JS Module 的方法、处理回调、管理 JS Module 等;
多层 JavaScriptModule/BaseJavaModule(NativeModule): 双端字典映射表中的模块,负责 Java/JS 到彼此的映射调用格式申明,由 CatalystInstance 统管;
尼玛,看了好久的源码,搞明白以后其实发现主流程和互相调用就上面几幅图那么回事。。。。这篇文章差点难产了,断断续续工作之余抽空才搞定,总的来说还是要坚持;通过上面的分析和之前、两篇文章的配合,我们对于 React Native 已经渐渐不觉得陌生了,本篇已经循序渐进揭开了 React Native 比较核心的启动主流程脉络,后面抽空会对 React Native 进行更加全面的解刨分析,力求今年把 React Native 这个技能点翻页封存吧,加油。
【工匠若水
未经允许严禁转载,请尊重作者劳动成果。】
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1343131次
积分:9977
积分:9977
排名:第1752名
原创:109篇
转载:10篇
评论:1451条
文章:26篇
阅读:66294
(1)(4)(1)(1)(1)(2)(3)(1)(2)(6)(1)(1)(5)(2)(2)(1)(2)(29)(25)(1)(3)(2)(1)(1)(2)(9)(10)

我要回帖

更多关于 react native nodejs 的文章

 

随机推荐