20分鐘理解React Native For Android原理
前言
- 文中所有 RN 縮寫指代React Native For Android
- 分析的 RN 代碼基于
{
"react": "15.4.1",
"react-native": "0.39.2"
}
- 本文主要分析 Java 層實現,對 C++ 和 JS 筆墨較少。
- 閱讀正文將花費您大約20分鐘。
背景
公司內幾個 APP 已經接入并上線了多個 RN 模塊,后續規劃的定制化需求及性能優化需要我們對 RN 底層原理有更深入的理解。下面通過研讀源代碼來分析和總結下 Android 中的 RN 實現原理。
從示例入手
示例代碼如下:
public class MainActivity extends ReactActivity {
@Override
protected String getMainComponentName() {
return "RN_Demo";
}
}
可以發現 RN 容器外層本質也是一個 Activity ,繼承了 ReactActivity ,需要我們覆寫 getMainComponentName() 方法,更改其返回值為組件名。
ReactActivity
接著跟蹤到 ReactActivity 中,類結構如下:
根據上述的結構轉換成 UML 圖如下(后面相關類將直接給出 UML ):
這里使用了 委托模式 將 Activity 的生命周期及事件傳遞委托給 ReactActivityDelegate 的實例對象 mDelegate 進行處理,之所以使用這種形式是為了讓 ReactFragmentActivity 也能復用該處理邏輯。 此外如果你有自定義的委托實現,可以在自己的 Activity 中覆寫 createReactActivityDelegate() 方法。這個方法將在 ReactActivity 的構造函數中調用生成 mDelegate 實例,之后在 onCreate() 方法調用這個委托對象的執行入口,也就是 loadApp() 方法。
ReactActivityDelegate
public class ReactActivityDelegate {
protected void onCreate(Bundle savedInstanceState) {
// 彈窗權限判斷邏輯
if (getReactNativeHost().getUseDeveloperSupport() && Build.VERSION.SDK_INT >= 23) {
// 省略具體實現
}
// 組件加載邏輯
if (mMainComponentName != null) {
loadApp(mMainComponentName);
}
// 雙擊判斷工具類 DoubleTapReloadRecognizer ,省略代碼
}
protected void loadApp(String appKey) {
// 省略判空代碼
// 創建 RN 容器根視圖,父類為 FrameLayout
mReactRootView = createRootView();
mReactRootView.startReactApplication(
getReactNativeHost().getReactInstanceManager(),
appKey,
getLaunchOptions());
// 將 RN 視圖放入 Activity
getPlainActivity().setContentView(mReactRootView);
}
}</code></pre>
ReactRootView
因此可以認為所謂的 RN 其實就是一個特殊的“自定義 View ”-- ReactRootView 。

這里的關鍵調用則是 startReactApplication() 方法。下面是該方法需要的三個參數:
形參
類型
功能描述
reactInstanceManager
ReactInstanceManager
用來創建及管理 CatalyInstance (提供原生與JS互調環境)的實例,同時連接調試功能,其生命周期與 ReactRootView 所在 Activity 保持一致。
moduleName
String
即實參 appKey ,需要保證JS中的 AppRegistry.registerComponent 參數值與 Acitvity 中的 getMainComponentName 返回值一致。
launchOptions
Bundle(后續版本可能更改為POJO)
默認為null,如果你需要傳 Props 給 JS 的話,請覆寫 createReactActivityDelegate() 方法,并覆寫 getLaunchOptions() 的返回值即可。

public void startReactApplication(
ReactInstanceManager reactInstanceManager,
String moduleName,
@Nullable Bundle launchOptions) {
// 確保在 UI 線程執行
// 判斷 ReactContext 是否已初始化,沒有就異步在后臺線程創建
if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
mReactInstanceManager.createReactContextInBackground();
}
// 寬高計算完成后添加布局監聽
}</code></pre>
startReactApplication() 將調用其中的 createReactContextInBackground() 方法,我們下面來看看這個通過 構造者模式 創建的實現類-- XReactInstanceManagerImpl 。
ReactInstanceManager
/**
- 在后臺線程異步初始化,這個方法只會在 Application 創建時調用一次,
重新加載 JS 時將會調用 recreateReactContextInBackground 方法
*/
@Override
public void createReactContextInBackground() {
// 首次執行判斷邏輯
mHasStartedCreatingInitialContext = true;
recreateReactContextInBackgroundInner();
}</code></pre>
可以看到不管是 createReactContextInBackground() 還是 recreateReactContextInBackground() ,都是通過 recreateReactContextInBackgroundInner() 來初始化 ReactContext 的。
private void recreateReactContextInBackgroundInner() {
// 確保在UI線程執行
// 調試模式,從服務器加載 bundle
if (mUseDeveloperSupport && mJSMainModuleName != null) {
// 省略代碼
return;
}
// 正式環境,從本地加載 bundle
recreateReactContextInBackgroundFromBundleLoader();
}
private void recreateReactContextInBackgroundFromBundleLoader() {
recreateReactContextInBackground(
new JSCJavaScriptExecutor.Factory(mJSCConfig.getConfigMap()),
mBundleLoader);
}
}</code></pre>
形參
類型
功能描述
jsExecutorFactory
JavaScriptExecutor.Factory
管理Webkit 的 JavaScriptCore,JS與C++的雙向通信在這里中轉
jsBundleLoader
JSBundleLoader
bundle加載器,根據 ReactNativeHost 中的配置決定從哪里加載bundle文件
XReactInstanceManagerImpl
private void recreateReactContextInBackground(
JavaScriptExecutor.Factory jsExecutorFactory,
JSBundleLoader jsBundleLoader) {
// 省略代碼
/ 把兩個參數封裝成ReactContextInitParams對象
ReactContextInitParams initParams =
new ReactContextInitParams(jsExecutorFactory, jsBundleLoader);
if (mReactContextInitAsyncTask == null) {
// 核心代碼,創建后臺線程,初始化 ReactContext
mReactContextInitAsyncTask = new ReactContextInitAsyncTask();
mReactContextInitAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, initParams);
} else {
// 省略代碼
}
}</code></pre>
ReactContextInitAsyncTask
private ReactApplicationContext createReactContext(
JavaScriptExecutor jsExecutor,
JSBundleLoader jsBundleLoader) {
// 省略代碼
// 注冊JS層模塊,通過它把所有的 JavaScriptModule 注冊到 CatalystInstance,將 JS 的可調用 API 暴露給 Java。
JavaScriptModuleRegistry.Builder jsModulesBuilder = new JavaScriptModuleRegistry.Builder();
// 包裝 ApplicationContext 為 ReactApplicationContext
final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
// 調試模式
if (mUseDeveloperSupport) {
// 調試模式下 ReactApplicationContext 中崩潰信息由 mDevSupportManager 進行攔截處理(紅色背景的錯誤頁)
reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager);
}
// 省略代碼
try {
// 加載給定的大量核心 ReactPackage,詳細列表請查看后文的表格
CoreModulesPackage coreModulesPackage =
new CoreModulesPackage(this, mBackBtnHandler, mUIImplementationProvider);
processPackage(
coreModulesPackage,
reactContext,
moduleSpecs,
reactModuleInfoMap,
jsModulesBuilder);
} finally {
// 省略代碼
}
// 加載 MainPackage 和開發者自定義的 ReactPackage,邏輯同 CoreModulesPackage
for (ReactPackage reactPackage : mPackages) {
// 省略代碼
try {
processPackage(
reactPackage,
reactContext,
moduleSpecs,
reactModuleInfoMap,
jsModulesBuilder);
} finally {
// 省略代碼
}
}
// 省略代碼
// 注冊 Java 層模塊,通過它把所有的 NativeModule 注冊到 CatalystInstance ,將 Java 的可調用 API 暴露給 JS。
NativeModuleRegistry nativeModuleRegistry;
try {
nativeModuleRegistry = new NativeModuleRegistry(moduleSpecs, reactModuleInfoMap);
} finally {
// 省略代碼
}
// 異常處理器選擇邏輯
NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null
? mNativeModuleCallExceptionHandler
: mDevSupportManager;
// 核心邏輯, Builder 模式創建 CatalystInstance 實例
CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
.setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
// 將 JS 執行通信類傳給 CatalystInstance
.setJSExecutor(jsExecutor)
// 將 Java 模塊映射表傳給 CatalystInstance
.setRegistry(nativeModuleRegistry)
// 將 JS 模塊映射表傳給 CatalystInstance
.setJSModuleRegistry(jsModulesBuilder.build())
// 將 Bundle 加載工具類傳給 CatalystInstance
.setJSBundleLoader(jsBundleLoader)
// 將異常處理器傳給 CatalystInstance
.setNativeModuleCallExceptionHandler(exceptionHandler);
// 省略代碼
final CatalystInstance catalystInstance;
try {
catalystInstance = catalystInstanceBuilder.build();
} finally {
// 省略代碼
}
if (mBridgeIdleDebugListener != null) {
catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);
}
// 使用 CatalystInstance 實例初始化 ReactContext 部分成員變量
reactContext.initializeWithInstance(catalystInstance);
// 解析 Bundle 文件
catalystInstance.runJSBundle();
return reactContext;
}</code></pre>
原生模塊
功能描述
AndroidInfoModule
獲取Android版本號和本地服務器地址
AnimationsDebugModule
監聽動畫過渡性能
DeviceEventManagerModule
事件監聽,比如后退鍵
ExceptionsManagerModule
異常處理
HeadlessJsTaskSupportModule
通知部分JS任務執行完成
SourceCodeModule
傳遞Bundle文件地址
Timing
在繪制幀率變化時觸發JS定時器
UIManagerModule
提供JS去創建和更新原生視圖的能力
DebugComponentOwnershipModule
調試功能:異步請求視圖結構
JSCHeapCapture
調試功能:獲取堆內存信息
JSCSamplingProfiler
調試功能:dump工具
CatalystInstance
private CatalystInstanceImpl(
final ReactQueueConfigurationSpec ReactQueueConfigurationSpec,
final JavaScriptExecutor jsExecutor,
final NativeModuleRegistry registry,
final JavaScriptModuleRegistry jsModuleRegistry,
final JSBundleLoader jsBundleLoader,
NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
// 省略代碼
// Android UI 線程,JS 線程和 NativeModulesQueue 線程
mReactQueueConfiguration = ReactQueueConfigurationImpl.create(
ReactQueueConfigurationSpec,
new NativeExceptionHandler());
// 調用 C++ 層代碼進行初始化
initializeBridge(
new BridgeCallback(this),
jsExecutor,
mReactQueueConfiguration.getJSQueueThread(),
mReactQueueConfiguration.getNativeModulesQueueThread(),
// 將所有 Java Module 傳遞給 C++ 層
mJavaRegistry.getModuleRegistryHolder(this));
mMainExecutorToken = getMainExecutorToken();
}
@Override
public void runJSBundle() {
// 省略代碼
mJSBundleHasLoaded = true;
// Bundle 的加載邏輯請參看下文的流程圖
mJSBundleLoader.loadScript(CatalystInstanceImpl.this);
// 省略代碼
}</code></pre>

private native void initializeBridge(ReactCallback callback,
JavaScriptExecutor jsExecutor,
MessageQueueThread jsQueue,
MessageQueueThread moduleQueue,
ModuleRegistryHolder registryHolder);
native void loadScriptFromAssets(AssetManager assetManager, String assetURL);
native void loadScriptFromFile(String fileName, String sourceURL);
native void loadScriptFromOptimizedBundle(String path, String sourceURL, int flags);</code></pre>
此時已經發現Java層的邏輯已經走完,不管是 CatalystInstance 實例的初始化還是 Bundle 的加載邏輯都將由 C++ 層進行處理。
CatalystInstance的創建
CatalystInstanceImpl.cpp 中是空實現,具體實現在 Instance.cpp 中。
void CatalystInstanceImpl::initializeBridge(
// JInstanceCallback 實現類,父類在 cxxreact/Instance.h 中。
std::unique_ptr<InstanceCallback> callback,
// 對應 Java 中的 JavaScriptExecutor
std::shared_ptr<JSExecutorFactory> jsef,
// C++ 的 JMessageQueueThread 。
std::shared_ptr<MessageQueueThread> jsQueue,
// C++ 的 JMessageQueueThread 。
std::unique_ptr<MessageQueueThread> nativeQueue,
// C++ 的 ModuleRegistryHolder 的 getModuleRegistry() 方法
std::sharedptr<ModuleRegistry> moduleRegistry) {
callback = std::move(callback);
jsQueue->runOnQueueSync(
[this, &jsef, moduleRegistry, jsQueue,
nativeQueue=folly::makeMoveWrapper(std::move(nativeQueue))] () mutable {
// 創建 NativeToJsBridge 對象
nativeToJsBridge_ = folly::makeunique<NativeToJsBridge>(
jsef.get(), moduleRegistry, jsQueue, nativeQueue.move(), callback);
std::lock_guard<std::mutex> lock(m_syncMutex);
m_syncReady = true;
m_syncCV.notify_all();
});
CHECK(nativeToJsBridge_);
}</code></pre>
Bundle 的加載
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文件內容)。
auto script = react::loadScriptFromAssets(manager, sourceURL);
// 判斷是否為 Unbundle
if (JniJSModulesUnbundle::isUnbundle(manager, sourceURL)) {
instance_->loadUnbundle(
folly::make_unique<JniJSModulesUnbundle>(manager, sourceURL),
std::move(script),
sourceURL);
return;
} else {
// instance_ 為 ReactCommon 目錄下 Instance.h 中類的實例
instance_->loadScriptFromString(std::move(script), sourceURL);
}
}

至此 C++ 層的調用邏輯到此為止,感興趣的同學可以繼續跟蹤進入,或者可以參考文末的資料,我們下面假設底層執行完成,回到 ReactContextInitAsyncTask 的 onPostExecute 方法。
@Override
protected void onPostExecute(Result<ReactApplicationContext> result) {
// 省略代碼
setupReactContext(result.get());
// 省略代碼
// 處理隊列再次初始化 ReactContext 的邏輯代碼
}
private void setupReactContext(ReactApplicationContext reactContext) {
// 省略代碼
// 確保在 UI 線程中執行并且當前 ReactContext 為空
Assertions.assertCondition(mCurrentReactContext == null);
// 確保當前 CatalystInstance 實例非空
CatalystInstance catalystInstance =
Assertions.assertNotNull(reactContext.getCatalystInstance());
// 初始化所有 Native Module
catalystInstance.initialize();
mDevSupportManager.onNewReactContextCreated(reactContext);
// 添加內存警告的回調
mMemoryPressureRouter.addMemoryPressureListener(catalystInstance);
moveReactContextToCurrentLifecycleState();
for (ReactRootView rootView : mAttachedRootViews) {
// 核心代碼,給 RootView 添加 view
attachMeasuredRootViewToInstance(rootView, catalystInstance);
}
// 省略代碼
}
private void attachMeasuredRootViewToInstance(
ReactRootView rootView,
CatalystInstance catalystInstance) {
// 省略代碼
// 確保在 UI 線程執行
// 重置視圖內容
rootView.removeAllViews();
rootView.setId(View.NO_ID);
// 設置 RootView
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();
// 獲取 AppRegistry 的代理對象
WritableNativeMap appParams = new WritableNativeMap();
appParams.putDouble("rootTag", rootTag);
appParams.putMap("initialProps", initialProps);
// AppRegistry 是 JS 暴露給 Java 層的 API
catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);
// 省略代碼
}</code></pre>
AppRegistry
public interface AppRegistry extends JavaScriptModule {
void runApplication(String appKey, WritableMap appParameters);
void unmountApplicationComponentAtRootTag(int rootNodeTag);
void startHeadlessTask(int taskId, String taskKey, WritableMap data);
}
AppRegistry 的 runApplication() 方法成為了加載 index.android.js 的主入口,而所有的 JS 方法調用都會經過 JavaScriptModuleInvocationHandler 。
JavaScriptModuleInvocationHandler
@Override
public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
ExecutorToken executorToken = mExecutorToken.get();
// 省略代碼
NativeArray jsArgs = args != null ? Arguments.fromJavaArgs(args) : new WritableNativeArray();
// 調用 C++ 層的 callFunction
mCatalystInstance.callFunction(
executorToken,
mModuleRegistration.getName(),
method.getName(),
jsArgs
);
return null;
}</code></pre>
以上就是 RN Android 的執行主流程,如有疏漏,歡迎留言。
參考資料
- https://github.com/非死book/react-native
- ReactNative Android源碼分析
- React Native Android 源碼框架淺析(主流程及 Java 與 JS 雙邊通信)
- React Native通訊原理
- ReactNativeAndroid源碼分析-Js如何調用Native的代碼
來自:https://blog.souche.com/react-native-source-code-analysis/