關於作者
郭孝星,程式設計師,吉他手,主要從事Android平臺基礎架構方面的工作,歡迎交流技術方面的問題,可以去我的Github提issue或者發郵件至guoxiaoxingse@163.com與我交流。
文章目錄
- 一 應用的初始化流程
- 二 應用的啟動流程
本篇系列文章主要分析ReactNative原始碼,分析ReactNative的啟動流程、渲染原理、通訊機制與執行緒模型等方面內容。
- 1ReactNative原始碼篇:原始碼初識
- 2ReactNative原始碼篇:程式碼呼叫
- 3ReactNative原始碼篇:啟動流程
- 4ReactNative原始碼篇:渲染原理
- 5ReactNative原始碼篇:執行緒模型
- 6ReactNative原始碼篇:通訊機制
一 應用初始化流程
在分析具體的啟動流程之前,我們先從Demo程式碼入手,對外部的程式碼有個大致的印象,我們才能進一步去了解內部的邏輯。
舉例
1 首先我們會在應用的Application裡做RN的初始化操作。
//ReactNativeHost:持有ReactInstanceManager例項,做一些初始化操作。
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage()
);
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
//SoLoader:載入C++底層庫,準備解析JS。
SoLoader.init(this, /* native exopackage */ false);
}
}複製程式碼
2 頁面繼承ReactActivity,ReactActivity作為JS頁面的容器。
public class MainActivity extends ReactActivity {
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
@Override
protected String getMainComponentName() {
//返回元件名
return "standard_project";
}
}複製程式碼
3 有了ReactActivity作為容器,我們就可以用JS開發頁面了。
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
//Component用來做UI渲染,生命週期控制,事件分發與回撥。
export default class standard_project extends Component {
//render函式返回UI的介面結構(JSX編寫,編譯完成後最終會變成JS程式碼)
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text style={styles.instructions}>
To get started, edit index.android.js
</Text>
<Text style={styles.instructions}>
Double tap R on your keyboard to reload,{'\n'}
Shake or press menu button for dev menu
</Text>
</View>
);
}
}
//建立CSS樣式
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
//註冊元件名,JS與Java格子各自維護了一個登錄檔
AppRegistry.registerComponent('standard_project', () => standard_project);複製程式碼
可以看到我們在Application裡實現了ReactApplication介面,該介面要求建立一個ReactNativeHost物件,該物件持有ReactInstanceManager例項,做一些初始化操作。
public interface ReactApplication {
/**
* Get the default {@link ReactNativeHost} for this app.
*/
ReactNativeHost getReactNativeHost();
}複製程式碼
在建立ReactNativeHost物件時,重寫了裡面的介個方法,這些方法提供一些初始化資訊,具體說來:
//是否開啟dev模式,dev模式下會有一些除錯工具,例如紅盒
public abstract boolean getUseDeveloperSupport();
//返回app需要的ReactPackage,這些ReactPackage裡包含了執行時需要用到的NativeModule
//JavaScriptModule以及ViewManager
protected abstract List<ReactPackage> getPackages();複製程式碼
ReactNativeHost主要的工作就是建立了ReactInstanceManager,它將一些資訊傳遞給了ReactInstanceManager。
public abstract class ReactNativeHost {
protected ReactInstanceManager createReactInstanceManager() {
ReactInstanceManagerBuilder builder = ReactInstanceManager.builder()
//應用上下文
.setApplication(mApplication)
//JSMainModuleP相當於應用首頁的js Bundle,可以傳遞url從伺服器拉取js Bundle
//當然這個只在dev模式下可以使用
.setJSMainModulePath(getJSMainModuleName())
//是否開啟dev模式
.setUseDeveloperSupport(getUseDeveloperSupport())
//紅盒的回撥
.setRedBoxHandler(getRedBoxHandler())
//自定義UI實現機制,這個我們一般用不到
.setUIImplementationProvider(getUIImplementationProvider())
.setInitialLifecycleState(LifecycleState.BEFORE_CREATE);
//新增ReactPackage
for (ReactPackage reactPackage : getPackages()) {
builder.addPackage(reactPackage);
}
//獲取js Bundle的載入路徑
String jsBundleFile = getJSBundleFile();
if (jsBundleFile != null) {
builder.setJSBundleFile(jsBundleFile);
} else {
builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName()));
}
return builder.build();
}
}複製程式碼
二 應用啟動流程
一句話概括啟動流程:先是應用終端啟動並建立應用上下文,應用上下文啟動JS Runtime,進行佈局,再由應用終端進行渲染,最後將渲染的View新增到ReactRootView上,最終呈現在使用者面前。
RN應用的啟動流程圖如下所示:
詳細流程:
1 在程式啟動的時候,也就是ReContextactActivity的onCreate()函式中,我們會去建立一個ReactInstanceManagerImpl物件
2 ReactRootView作為整個RN應用的根檢視,通過呼叫ReactRootView.startReactApplication()方法啟動RN應用。
3 RN應用頁面渲染前,需要先建立ReactContext的建立流程在,非同步任務ReactContextInitAsyncTask負責來完成這個任務。
4 ReactContextInitAsyncTask在後臺ReactContextInitAsyncTask.doInBackground()執行ReactContext的建立,建立ReactContext的過程中,會依據ReactPackage建立JavaScriptModuleRegistry與
NativeModuleRegistry登錄檔以及它們的管理類CatalystInstanceImpl,同時建立JS、Native與UI執行緒佇列,並最終呼叫CatalystInstanceImpl.runJSBundle()去非同步
載入JS Bundle檔案。
5 後臺任務執行完成後,在ReactContextInitAsyncTask.onPostExecute()會呼叫ReactInstanceManager.setupReactContext()設定建立好的ReactContext,並將
ReactRootView載入進來,並呼叫RN應用的JS入口APPRegistry來啟動應用。
6 JS層找到已經註冊的對應的啟動元件,執行renderApplication()來渲染整個應用。複製程式碼
好,我們先從ReactActivity入手。
ReactActivity繼承於Activity,並實現了它的生命週期方法。ReactActivity自己並沒有做什麼事情,所有的功能都由它的委託類ReactActivityDelegate來完成。
如下所示:
所以我們主要來關注ReactActivityDelegate的實現。我們先來看看ReactActivityDelegate的onCreate()方法。
2.1 ReactActivityDelegate.onCreate(Bundle savedInstanceState)
public class ReactActivityDelegate {
protected void onCreate(Bundle savedInstanceState) {
boolean needsOverlayPermission = false;
//開發模式判斷以及許可權檢查
if (getReactNativeHost().getUseDeveloperSupport() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Get permission to show redbox in dev builds.
if (!Settings.canDrawOverlays(getContext())) {
needsOverlayPermission = true;
Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getContext().getPackageName()));
FLog.w(ReactConstants.TAG, REDBOX_PERMISSION_MESSAGE);
Toast.makeText(getContext(), REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show();
((Activity) getContext()).startActivityForResult(serviceIntent, REQUEST_OVERLAY_PERMISSION_CODE);
}
}
//mMainComponentName就是上面ReactActivity.getMainComponentName()返回的元件名
if (mMainComponentName != null && !needsOverlayPermission) {
//載入app頁面
loadApp(mMainComponentName);
}
mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer();
}
protected void loadApp(String appKey) {
if (mReactRootView != null) {
throw new IllegalStateException("Cannot loadApp while app is already running.");
}
//建立ReactRootView作為根檢視,它本質上是一個FrameLayout
mReactRootView = createRootView();
//啟動RN應用
mReactRootView.startReactApplication(
getReactNativeHost().getReactInstanceManager(),
appKey,
getLaunchOptions());
//Activity的setContentView()方法
getPlainActivity().setContentView(mReactRootView);
}
}複製程式碼
可以發現ReactActivityDelegate在建立時主要做了以下事情:
1 建立ReactRootView作為應用的容器,它本質上是一個FrameLayout。
2 呼叫ReactRootView.startReactApplication()進一步執行應用啟動流程。
3 呼叫Activity.setContentView()將建立的ReactRootView作為ReactActivity的content view。複製程式碼
尅看出RN真正核心的地方就在於ReactRootView,它就是一個View,你可以像用其他UI元件那樣把它用在Android應用的任何地方。好,我們進一步去ReactRootView看啟動流程。
2.2 ReactRootView.startReactApplication( ReactInstanceManager reactInstanceManager, String moduleName, @Nullable Bundle launchOptions)
public class ReactRootView extends SizeMonitoringFrameLayout implements RootView {
/**
* Schedule rendering of the react component rendered by the JS application from the given JS
* module (@{param moduleName}) using provided {@param reactInstanceManager} to attach to the
* JS context of that manager. Extra parameter {@param launchOptions} can be used to pass initial
* properties for the react component.
*/
public void startReactApplication(
ReactInstanceManager reactInstanceManager,
String moduleName,
@Nullable Bundle launchOptions) {
UiThreadUtil.assertOnUiThread();
// TODO(6788889): Use POJO instead of bundle here, apparently we can't just use WritableMap
// here as it may be deallocated in native after passing via JNI bridge, but we want to reuse
// it in the case of re-creating the catalyst instance
Assertions.assertCondition(
mReactInstanceManager == null,
"This root view has already been attached to a catalyst instance manager");
mReactInstanceManager = reactInstanceManager;
mJSModuleName = moduleName;
mLaunchOptions = launchOptions;
//建立RN應用上下文
if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
mReactInstanceManager.createReactContextInBackground();
}
// We need to wait for the initial onMeasure, if this view has not yet been measured, we set which
// will make this view startReactApplication itself to instance manager once onMeasure is called.
if (mWasMeasured) {
attachToReactInstanceManager();
}
}
}複製程式碼
這個方法用來建立應用的上下文,RN應用的上下文用ReactContext來描述。
我們來看看這個函式的3個引數:
···
ReactInstanceManager reactInstanceManager:管理React例項。
String moduleName:模組的名字,對應ReactActivity.getMainComponentName()與AppRegistry.registerComponent()。
Bundle launchOptions:Bundle型別的資料,如果我們不繼承ReactActivity而是自己實現頁面容器,可以通過這個引數在startActivity()時傳遞引數到JS層。
···
我們可以看到,ReactRootView.startReactApplication()方法裡最終會呼叫ReactInstanceManager.createReactContextInBackground()來建立RN應用的上下文。
2.3 ReactInstanceManager.createReactContextInBackground()
public class ReactInstanceManager {
/**
* Trigger react context initialization asynchronously in a background async task. This enables
* applications to pre-load the application JS, and execute global code before
* {@link ReactRootView} is available and measured. This should only be called the first time the
* application is set up, which is enforced to keep developers from accidentally creating their
* application multiple times without realizing it.
*
* Called from UI thread.
*/
public void createReactContextInBackground() {
Assertions.assertCondition(
!mHasStartedCreatingInitialContext,
"createReactContextInBackground should only be called when creating the react " +
"application for the first time. When reloading JS, e.g. from a new file, explicitly" +
"use recreateReactContextInBackground");
mHasStartedCreatingInitialContext = true;
//進一步呼叫recreateReactContextInBackgroundInner()
recreateReactContextInBackgroundInner();
}
/**
* Recreate the react application and context. This should be called if configuration has
* changed or the developer has requested the app to be reloaded. It should only be called after
* an initial call to createReactContextInBackground.
*
* Called from UI thread.
*/
public void recreateReactContextInBackground() {
Assertions.assertCondition(
mHasStartedCreatingInitialContext,
"recreateReactContextInBackground should only be called after the initial " +
"createReactContextInBackground call.");
recreateReactContextInBackgroundInner();
}
private void recreateReactContextInBackgroundInner() {
UiThreadUtil.assertOnUiThread();
//開發模式,實現線上更新Bundle,晃動彈出除錯選單等功能,這一部分屬於除錯功能流程。
if (mUseDeveloperSupport && mJSMainModuleName != null) {
final DeveloperSettings devSettings = mDevSupportManager.getDevSettings();
// If remote JS debugging is enabled, load from dev server.
//判斷是否處於開發模式,如果處於開發模式,則從Dev Server中獲取JSBundle,如果不是則從檔案中獲取。
if (mDevSupportManager.hasUpToDateJSBundleInCache() &&
!devSettings.isRemoteJSDebugEnabled()) {
// If there is a up-to-date bundle downloaded from server,
// with remote JS debugging disabled, always use that.
onJSBundleLoadedFromServer();
} else if (mBundleLoader == null) {
mDevSupportManager.handleReloadJS();
}
else {
mDevSupportManager.isPackagerRunning(
new PackagerStatusCallback() {
@Override
public void onPackagerStatusFetched(final boolean packagerIsRunning) {
UiThreadUtil.runOnUiThread(
new Runnable() {
@Override
public void run() {
if (packagerIsRunning) {
mDevSupportManager.handleReloadJS();
} else {
// If dev server is down, disable the remote JS debugging.
devSettings.setRemoteJSDebugEnabled(false);
recreateReactContextInBackgroundFromBundleLoader();
}
}
});
}
});
}
return;
}
//線上模式
recreateReactContextInBackgroundFromBundleLoader();
}
private void recreateReactContextInBackgroundFromBundleLoader() {
//mJSCConfig可以在ReactNativeHost建立ReactInstanceManager時進行配置。mJSCConfig會通過JSCJavaScriptExecutor的
//Native方法HybridData initHybrid(ReadableNativeArray jscConfig)傳遞到C++層。
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) {
//初始化一個非同步任務,建立ReactApplicationContext
// No background task to create react context is currently running, create and execute one.
mReactContextInitAsyncTask = new ReactContextInitAsyncTask();
mReactContextInitAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, initParams);
} else {
//建立ReactContext的後臺任務已經開啟,快取initParams在佇列中等待重新建立ReactContext
// Background task is currently running, queue up most recent init params to recreate context
// once task completes.
mPendingReactContextInitParams = initParams;
}
}
}複製程式碼
整個程式碼的呼叫鏈,最終開啟非同步任務ReactContextInitAsyncTask來建立上下文ReactApplicationContext。
ReactInstanceManager.createReactContextInBackground()
->ReactInstanceManager.recreateReactContextInBackground()
->ReactInstanceManager.recreateReactContextInBackgroundInner()
->ReactInstanceManager.recreateReactContextInBackgroundFromBundleLoader()
->ReactInstanceManager.recreateReactContextInBackground(JavaScriptExecutor.Factory jsExecutorFactory, JSBundleLoader jsBundleLoader)
->ReactContextInitAsyncTask
該方法啟動了一個ReactContextInitAsyncTask的非同步任務去執行的建立。
2.4 ReactInstanceManager.ReactContextInitAsyncTask.doInBackground(ReactContextInitParams... params)
public class ReactInstanceManager {
/*
* Task class responsible for (re)creating react context in the background. These tasks can only
* be executing one at time, see {@link #recreateReactContextInBackground()}.
*/
private final class ReactContextInitAsyncTask extends
AsyncTask<ReactContextInitParams, Void, Result<ReactApplicationContext>> {
@Override
protected Result<ReactApplicationContext> doInBackground(ReactContextInitParams... params) {
// TODO(t11687218): Look over all threading
// Default priority is Process.THREAD_PRIORITY_BACKGROUND which means we'll be put in a cgroup
// that only has access to a small fraction of CPU time. The priority will be reset after
// this task finishes: https://android.googlesource.com/platform/frameworks/base/+/d630f105e8bc0021541aacb4dc6498a49048ecea/core/java/android/os/AsyncTask.java#256
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
Assertions.assertCondition(params != null && params.length > 0 && params[0] != null);
try {
//利用getJsExecutorFactory建立jsExecutor,並傳遞到C++層。
JavaScriptExecutor jsExecutor = params[0].getJsExecutorFactory().create();
//非同步執行createReactContext()方法,建立ReactContext
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);
}
}
}複製程式碼
ReactContextInitAsyncTask的doInBackground()方法裡呼叫ReactInstanceManager.createReactContext()最終執行了ReactApplicationContext的建立。
我們重點來看看傳入ReactInstanceManager.createReactContext()的2個引數:
JSCJavaScriptExecutor jsExecutor:JSCJavaScriptExecutor繼承於JavaScriptExecutor,當該類被載入時,它會自動去載入"reactnativejnifb.so"庫,並會呼叫Native方
法initHybrid()初始化C++層RN與JSC通訊的框架。
JSBundleLoader jsBundleLoader:快取了JSBundle的資訊,封裝了上層載入JSBundle的相關介面,CatalystInstance通過其簡介呼叫ReactBridge去載入JS檔案,不同的場景會建立
不同的載入器,具體可以檢視類JSBundleLoader。複製程式碼
這兩個引數是ReactInstanceManager.recreateReactContextInBackground()建立ReactContextInitAsyncTask傳遞進來的,有兩個地方呼叫了ReactInstanceManager.recreateReactContextInBackground()
方法,
接下來呼叫ReactInstanceManager.createReactContext(),真正開始建立ReactContext。
2.5 ReactInstanceManager.createReactContext( JavaScriptExecutor jsExecutor, JSBundleLoader jsBundleLoader)
public class ReactInstanceManager {
/**
* @return instance of {@link ReactContext} configured a {@link CatalystInstance} set
*/
private ReactApplicationContext createReactContext(
JavaScriptExecutor jsExecutor,
JSBundleLoader jsBundleLoader) {
FLog.i(ReactConstants.TAG, "Creating react context.");
ReactMarker.logMarker(CREATE_REACT_CONTEXT_START);
//ReactApplicationContext是ReactContext的包裝類。
final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
//建立JavaModule登錄檔Builder,用來建立JavaModule登錄檔,JavaModule登錄檔將所有的JavaModule註冊到CatalystInstance中。
NativeModuleRegistryBuilder nativeModuleRegistryBuilder = new NativeModuleRegistryBuilder(
reactContext,
this,
mLazyNativeModulesEnabled);
//建立JavaScriptModule登錄檔Builder,用來建立JavaScriptModule登錄檔,JavaScriptModule登錄檔將所有的JavaScriptModule註冊到CatalystInstance中。
JavaScriptModuleRegistry.Builder jsModulesBuilder = new JavaScriptModuleRegistry.Builder();
if (mUseDeveloperSupport) {
//如果處於開發模式,則設定NativeModuleCallExceptionHandler,將錯誤交由DevSupportManager處理(彈出紅框,提示錯誤)。
reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager);
}
ReactMarker.logMarker(PROCESS_PACKAGES_START);
Systrace.beginSection(
TRACE_TAG_REACT_JAVA_BRIDGE,
"createAndProcessCoreModulesPackage");
try {
//建立CoreModulesPackage例項,CoreModulesPackage裡面封裝了RN Framework核心功能,包括:通訊、除錯等。
CoreModulesPackage coreModulesPackage =
new CoreModulesPackage(
this,
mBackBtnHandler,
mUIImplementationProvider,
mLazyViewManagersEnabled);
//呼叫processPackage(0處理CoreModulesPackage,處理的過程就是把各自的Module新增到對應的登錄檔中。
processPackage(coreModulesPackage, nativeModuleRegistryBuilder, jsModulesBuilder);
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
// TODO(6818138): Solve use-case of native/js modules overriding
for (ReactPackage reactPackage : mPackages) {
Systrace.beginSection(
TRACE_TAG_REACT_JAVA_BRIDGE,
"createAndProcessCustomReactPackage");
try {
//迴圈處理我們在Application裡注入的ReactPackage,處理的過程就是把各自的Module新增到對應的登錄檔中。
processPackage(reactPackage, nativeModuleRegistryBuilder, jsModulesBuilder);
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
ReactMarker.logMarker(PROCESS_PACKAGES_END);
ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_START);
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "buildNativeModuleRegistry");
NativeModuleRegistry nativeModuleRegistry;
try {
//生成Java Module登錄檔
nativeModuleRegistry = nativeModuleRegistryBuilder.build();
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_END);
}
//檢視外部是否設定NativeModuleCallExceptionHandler,它是在ReactInstanceManagerBuilder構建ReactInstanceManager是傳遞進來的
//如果設定了則使用外部NativeModuleCallExceptionHandler,如果沒有設定則使用DevSupportManager。
NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null
? mNativeModuleCallExceptionHandler
: mDevSupportManager;
//jsExecutor、nativeModuleRegistry、nativeModuleRegistry等各種引數處理好之後,開始構建CatalystInstanceImpl例項。
CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
.setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
.setJSExecutor(jsExecutor)
.setRegistry(nativeModuleRegistry)
//生成JS Module登錄檔
.setJSModuleRegistry(jsModulesBuilder.build())
.setJSBundleLoader(jsBundleLoader)
.setNativeModuleCallExceptionHandler(exceptionHandler);
ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_START);
// CREATE_CATALYST_INSTANCE_END is in JSCExecutor.cpp
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstance");
final CatalystInstance catalystInstance;
try {
catalystInstance = catalystInstanceBuilder.build();
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_END);
}
if (mBridgeIdleDebugListener != null) {
catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);
}
if (Systrace.isTracing(TRACE_TAG_REACT_APPS | TRACE_TAG_REACT_JSC_CALLS)) {
//呼叫CatalystInstanceImpl的Native方法把Java Registry轉換為Json,再由C++層傳送到JS層。
catalystInstance.setGlobalVariable("__RCTProfileIsProfiling", "true");
}
//關聯ReacContext與CatalystInstance
reactContext.initializeWithInstance(catalystInstance);
//通過CatalystInstance開始載入JS Bundle
catalystInstance.runJSBundle();
return reactContext;
}
}複製程式碼
這個方法有點長,它主要做了以下事情:
1 建立JavaModule登錄檔與JavaScriptModule登錄檔,這兩張表最後都交由CatalystInstance管理。
3 處理ReactPackage,將JavaModule與JavaScriptModule放進各自對應的登錄檔裡。
3 通過上面jsExecutor、nativeModuleRegistry、jsModulesRegistry、jsBundleLoader、exceptionHandler等引數建立CatalystInstance例項。
4 關聯ReactContext與CatalystInstance,並將JS Bundle載入進來,等待ReactContextInitAsyncTask結束以後呼叫JS入口渲染頁面。複製程式碼
該函式的最後呼叫CatalystInstance.runJSBundle()去載入JS Bundle,該載入過程的函式呼叫鏈如下所示:
CatalystInstance.runJSBundle() -> JSBundleLoader.loadScript() -> CatalystInstance.jniLoadScriptFromAssets()/jniLoadScriptFromFile()
-> CatalystInstance::jniLoadScriptFromAssets()/jniLoadScriptFromFile() -> Instance::loadScriptFromString()/loadScriptFromFile()
-> NativeToJsBridge::loadApplication() -> JSCExecutor::loadApplicationScript()複製程式碼
最終由C++中的JSCExecutor.cpp完成了JS Bundle的載入,核心邏輯都在JSCExecutor.cpp中,這一塊的內容我們後續的文章在詳細分析,我們先來看看CatalystInstanceImpl的建立流程。
2.6 CatalystInstanceImpl.CatalystInstanceImpl( final ReactQueueConfigurationSpec ReactQueueConfigurationSpec, final JavaScriptExecutor jsExecutor, final NativeModuleRegistry registry, final JavaScriptModuleRegistry jsModuleRegistry, final JSBundleLoader jsBundleLoader, NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler)
public class CatalystInstanceImpl implements CatalystInstance {
private CatalystInstanceImpl(
final ReactQueueConfigurationSpec ReactQueueConfigurationSpec,
final JavaScriptExecutor jsExecutor,
final NativeModuleRegistry registry,
final JavaScriptModuleRegistry jsModuleRegistry,
final JSBundleLoader jsBundleLoader,
NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
FLog.w(ReactConstants.TAG, "Initializing React Xplat Bridge.");
//Native方法,用來建立JNI相關狀態,並返回mHybridData。
mHybridData = initHybrid();
//RN中的三個執行緒:Native Modules Thread、JS Thread、UI Thread,都是通過Handler來管理的。
mReactQueueConfiguration = ReactQueueConfigurationImpl.create(
ReactQueueConfigurationSpec,
new NativeExceptionHandler());
mBridgeIdleListeners = new CopyOnWriteArrayList<>();
mJavaRegistry = registry;
mJSModuleRegistry = jsModuleRegistry;
mJSBundleLoader = jsBundleLoader;
mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;
mTraceListener = new JSProfilerTraceListener(this);
FLog.w(ReactConstants.TAG, "Initializing React Xplat Bridge before initializeBridge");
//Native方法,呼叫initializeBridge()方法,並建立BridgeCallback例項,初始化Bridge。
initializeBridge(
new BridgeCallback(this),
jsExecutor,
mReactQueueConfiguration.getJSQueueThread(),
mReactQueueConfiguration.getNativeModulesQueueThread(),
mJavaRegistry.getJavaModules(this),
mJavaRegistry.getCxxModules());
FLog.w(ReactConstants.TAG, "Initializing React Xplat Bridge after initializeBridge");
mMainExecutorToken = getMainExecutorToken();
}
//在C++層初始化通訊橋ReactBridge
private native void initializeBridge(
ReactCallback callback,
JavaScriptExecutor jsExecutor,
MessageQueueThread jsQueue,
MessageQueueThread moduleQueue,
Collection<JavaModuleWrapper> javaModules,
Collection<ModuleHolder> cxxModules);
}複製程式碼
從CatalystInstanceImpl的構建過程可以看出,CatalystInstanceImpl是個封裝管理類,封裝了各種登錄檔,以及初始化JNI,我們來看看最後初始化Bridge傳入的6個引數:
ReactCallback callback:CatalystInstanceImpl的靜態內部類ReactCallback,負責介面回撥。
JavaScriptExecutor jsExecutor:JS執行器,將JS的呼叫傳遞給C++層。
MessageQueueThread jsQueue.getJSQueueThread():JS執行緒,通過mReactQueueConfiguration.getJSQueueThread()獲得,mReactQueueConfiguration通過ReactQueueConfigurationSpec.createDefault()建立。
MessageQueueThread moduleQueue:Native執行緒,通過mReactQueueConfiguration.getNativeModulesQueueThread()獲得,mReactQueueConfiguration通過ReactQueueConfigurationSpec.createDefault()建立。
Collection<JavaModuleWrapper> javaModules:java modules,來源於mJavaRegistry.getJavaModules(this)。
Collection<ModuleHolder> cxxModules):c++ modules,來源於mJavaRegistry.getCxxModules()。複製程式碼
CatalystInstanceImpl被建立以後,便進行JS的載入。從上面第5步:ReactInstanceManager.createReactContext()方法可以知道,該函式會調
用CatalystInstanceImpl.runJSBundle()來載入JS Bundle。我們開看一下它的實現。
2.7 CatalystInstanceImpl.runJSBundle()
···java
public class CatalystInstanceImpl{
@Override
public void runJSBundle() {
Assertions.assertCondition(!mJSBundleHasLoaded, "JS bundle was already loaded!");
mJSBundleHasLoaded = true;
// incrementPendingJSCalls();
//呼叫載入器載入JS Bundle,不同情況下載入器各不相同。
mJSBundleLoader.loadScript(CatalystInstanceImpl.this);
synchronized (mJSCallsPendingInitLock) {
// Loading the bundle is queued on the JS thread, but may not have
// run yet. It's safe to set this here, though, since any work it
// gates will be queued on the JS thread behind the load.
mAcceptCalls = true;
for (PendingJSCall call : mJSCallsPendingInit) {
jniCallJSFunction(call.mExecutorToken, call.mModule, call.mMethod, call.mArguments);
}
mJSCallsPendingInit.clear();
}
// This is registered after JS starts since it makes a JS call
Systrace.registerListener(mTraceListener);複製程式碼
}
}
···
CatalystInstanceImpl.runJSBundle()會呼叫JSBundleLoader去載入JS Bundle,由於不同的情況可能會有不同的JSBundleLoader,我們假設用的是第一種:
public abstract class JSBundleLoader {
/**
* This loader is recommended one for release version of your app. In that case local JS executor
* should be used. JS bundle will be read from assets in native code to save on passing large
* strings from java to native memory.
*/
public static JSBundleLoader createAssetLoader(
final Context context,
final String assetUrl) {
return new JSBundleLoader() {
@Override
public String loadScript(CatalystInstanceImpl instance) {
instance.loadScriptFromAssets(context.getAssets(), assetUrl);
return assetUrl;
}
};
}
}複製程式碼
可以看出,它會繼續呼叫CatalystInstanceImpl.loadScriptFromAssets()方法去載入JS Bundle,該方法的實現如下所示:
2.8 CatalystInstanceImpl.loadScriptFromAssets(AssetManager assetManager, String assetURL)
public class CatalystInstanceImpl {
/* package */ void loadScriptFromAssets(AssetManager assetManager, String assetURL) {
mSourceURL = assetURL;
jniLoadScriptFromAssets(assetManager, assetURL);
}
private native void jniLoadScriptFromAssets(AssetManager assetManager, String assetURL);
}複製程式碼
CatalystInstanceImpl.java最終還是呼叫C++層的CatalystInstanceImpl.cpp去載入JS Bundle,我們去C++層看一下實現。
可以看出該方法最終呼叫Native方法jniLoadScriptFromAssets去載入JS Bundle,該方法的實現如下所示:
2.9 CatalystInstanceImpl::jniLoadScriptFromAssets(jni::alias_ref assetManager, const std::string& assetURL)
CatalystInstanceImpl.cpp
void CatalystInstanceImpl::jniLoadScriptFromAssets(
jni::alias_ref<JAssetManager::javaobject> assetManager,
const std::string& assetURL) {
const int kAssetsLength = 9; // strlen("assets://");
//獲取source js Bundle的路徑名,這裡預設的就是index.android.bundle
auto sourceURL = assetURL.substr(kAssetsLength);
//assetManager是Java層傳遞過來的AssetManager,呼叫JSLoade.cpo裡的extractAssetManager()方法,extractAssetManager()再
//呼叫android/asset_manager_jni.h裡的AAssetManager_fromJava()方法獲取AAssetManager物件。
auto manager = react::extractAssetManager(assetManager);
//呼叫JSLoader.cpp的loadScriptFromAssets()方法讀取JS Bundle裡的內容。
auto script = react::loadScriptFromAssets(manager, sourceURL);
//判斷是不是unbundle命令打包,build.gradle預設裡是bundle打包方式。
if (JniJSModulesUnbundle::isUnbundle(manager, sourceURL)) {
instance_->loadUnbundle(
folly::make_unique<JniJSModulesUnbundle>(manager, sourceURL),
std::move(script),
sourceURL);
return;
} else {
//bundle命令打包走次流程,instance_是Instan.h中類的例項。
instance_->loadScriptFromString(std::move(script), sourceURL);
}
}複製程式碼
接著會呼叫Instance.cpp的loadScriptFromString()方法去解析JS Bundle裡的內容。
2.10 Instance::loadScriptFromString(std::unique_ptr string, std::string sourceURL)
Instance.cpp
void Instance::loadScriptFromString(std::unique_ptr<const JSBigString> string,
std::string sourceURL) {
callback_->incrementPendingJSCalls();
SystraceSection s("reactbridge_xplat_loadScriptFromString",
"sourceURL", sourceURL);
//nativeToJsBridge_也是在Instance::initializeBridget()方法裡初始化的,具體實現在NativeToJsBridge.cpp裡。
nativeToJsBridge_->loadApplication(nullptr, std::move(string), std::move(sourceURL));
}複製程式碼
loadScriptFromString()進一步呼叫NativeToJsBridge.cpp的loadApplication()方法,它的實現如下所示:
2.11 NativeToJsBridge::loadApplication(std::unique_ptr unbundle, std::unique_ptr startupScript, std::string startupScriptSourceURL)
NativeToJsBridge.cpp
void NativeToJsBridge::loadApplication(
std::unique_ptr<JSModulesUnbundle> unbundle,
std::unique_ptr<const JSBigString> startupScript,
std::string startupScriptSourceURL) {
//獲取一個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));
}
//executor從runOnExecutorQueue()返回的map中取得,與OnLoad中的JSCJavaScriptExecutorHolder對應,也與
//Java中的JSCJavaScriptExecutor對應。它的例項在JSExecutor.cpp中實現。
executor->loadApplicationScript(std::move(*startupScript),
std::move(startupScriptSourceURL));
});
}
關於unbundle命令
<unbundle命令,使用方式和bundle命令完全相同。unbundle命令是在bundle命令的基礎上增加了一項功能,除了生成整合JS檔案index.android.bundle外,還會
生成各個單獨的未整合JS檔案(但會被優化),全部放在js-modules目錄下,同時會生成一個名為UNBUNDLE的標識檔案,一併放在其中。UNBUNDLE標識檔案的前4個位元組
固定為0xFB0BD1E5,用於載入前的校驗。複製程式碼
我們先來看看這個函式的3個引數:
std::unique_ptr<JSModulesUnbundle> unbundle:空指標,因為我們用的bundle方式打包。
std::unique_ptr<const JSBigString> startupScript:bundle的檔案內容。
std::string startupScriptSourceURL:bundle的檔名。複製程式碼
該函式進一步呼叫JSExecutor.cpp的loadApplicationScript()方法。
2.12 JSCExecutor::loadApplicationScript(std::unique_ptr script, std::string sourceURL)
到了這個方法,就是去真正載入JS檔案了。
JSExecutor.cpp
void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> script, std::string sourceURL) {
SystraceSection s("JSCExecutor::loadApplicationScript",
"sourceURL", sourceURL);
ReactMarker::logMarker("RUN_JS_BUNDLE_START");
String jsSourceURL(m_context, sourceURL.c_str());
// TODO t15069155: reduce the number of overrides here
#ifdef WITH_FBJSCEXTENSIONS
if (auto fileStr = dynamic_cast<const JSBigFileString *>(script.get())) {
JSLoadSourceStatus jsStatus;
auto bcSourceCode = JSCreateSourceCodeFromFile(fileStr->fd(), jsSourceURL, nullptr, &jsStatus);
switch (jsStatus) {
case JSLoadSourceIsCompiled:
if (!bcSourceCode) {
throw std::runtime_error("Unexpected error opening compiled bundle");
}
//使用Webkit JSC去解釋執行JS
evaluateSourceCode(m_context, bcSourceCode, jsSourceURL);
//繫結bridge,核心就是通過getGlobalObject()將JS與C++通過Webkit jSC實現繫結
bindBridge();
flush();
ReactMarker::logMarker("CREATE_REACT_CONTEXT_END");
ReactMarker::logMarker("RUN_JS_BUNDLE_END");
return;
case JSLoadSourceErrorVersionMismatch:
throw RecoverableError(explainLoadSourceStatus(jsStatus));
case JSLoadSourceErrorOnRead:
case JSLoadSourceIsNotCompiled:
// Not bytecode, fall through.
break;
}
}
#elif defined(__APPLE__)
BundleHeader header;
memcpy(&header, script->c_str(), std::min(script->size(), sizeof(BundleHeader)));
auto scriptTag = parseTypeFromHeader(header);
if (scriptTag == ScriptTag::BCBundle) {
using file_ptr = std::unique_ptr<FILE, decltype(&fclose)>;
file_ptr source(fopen(sourceURL.c_str(), "r"), fclose);
int sourceFD = fileno(source.get());
JSValueRef jsError;
JSValueRef result = JSC_JSEvaluateBytecodeBundle(m_context, NULL, sourceFD, jsSourceURL, &jsError);
if (result == nullptr) {
formatAndThrowJSException(m_context, jsError, jsSourceURL);
}
} else
#endif
{
#ifdef WITH_FBSYSTRACE
fbsystrace_begin_section(
TRACE_TAG_REACT_CXX_BRIDGE,
"JSCExecutor::loadApplicationScript-createExpectingAscii");
#endif
ReactMarker::logMarker("loadApplicationScript_startStringConvert");
String jsScript = jsStringFromBigString(m_context, *script);
ReactMarker::logMarker("loadApplicationScript_endStringConvert");
#ifdef WITH_FBSYSTRACE
fbsystrace_end_section(TRACE_TAG_REACT_CXX_BRIDGE);
#endif
evaluateScript(m_context, jsScript, jsSourceURL);
}
bindBridge();
flush();
ReactMarker::logMarker("CREATE_REACT_CONTEXT_END");
ReactMarker::logMarker("RUN_JS_BUNDLE_END");
}複製程式碼
evaluateScript()方法呼叫Webkit jSC開始解析執行JS,並呼叫bindBridge()繫結bridge,我們這裡主要分析的啟動流程,先不分析JS渲染過程,先看看Bridge繫結
流程,bindBridge()的實現如下所示:
JSExecutor.cpp
void JSCExecutor::bindBridge() throw(JSException) {
SystraceSection s("JSCExecutor::bindBridge");
if (!m_delegate || !m_delegate->getModuleRegistry()) {
return;
}
auto global = Object::getGlobalObject(m_context);
auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");
if (batchedBridgeValue.isUndefined()) {
throwJSExecutionException("Could not get BatchedBridge, make sure your bundle is packaged correctly");
}
auto batchedBridge = batchedBridgeValue.asObject();
//callFunctionReturnFlushedQueue這些都是MessageQueue.js層裡的方法
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");
if (!m_delegate) {
// do nothing
} else if (!m_delegate->getModuleRegistry()) {
callNativeModules(Value::makeNull(m_context));
} else {
// If this is failing, chances are you have provided a delegate with a
// module registry, but haven't loaded the JS which enables native function
// queueing. Add BatchedBridge.js to your bundle, pass a nullptr delegate,
// or make delegate->getModuleRegistry() return nullptr.
CHECK(m_flushedQueueJS) << "Attempting to use native methods without loading BatchedBridge.js";
//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為JsToNativeBridge物件。
m_delegate->callNativeModules(*this, folly::parseJson(calls), true);
} catch (...) {
std::string message = "Error in callNativeModules()";
try {
message += ":" + value.toString().str();
} catch (...) {
// ignored
}
std::throw_with_nested(std::runtime_error(message));
}
}複製程式碼
m_flushedQueueJS支線的是MessageQueue.js的flushedQueue()方法,此時JS已經被載入到佇列中,等待Java層來驅動它。載入完JS後
ReactContextInitAsyncTask的後臺任務執行完成,進入到非同步任務的onPostExecute()方法繼續
JS Bundle載入並解析完成後,ReactContextInitAsyncTask的後臺任務完成,進入onPostExecute()方法,我們繼續跟進它的實現。
當ReactContext被建立以後,變回繼續執行ReactContextInitAsyncTask.onPostExecute()方法。
2.13 ReactInstanceManager.ReactContextInitAsyncTask.onPostExecute(Result result)
public class ReactInstanceManager {
/*
* Task class responsible for (re)creating react context in the background. These tasks can only
* be executing one at time, see {@link #recreateReactContextInBackground()}.
*/
private final class ReactContextInitAsyncTask extends
AsyncTask<ReactContextInitParams, Void, Result<ReactApplicationContext>> {
@Override
protected void onPostExecute(Result<ReactApplicationContext> result) {
try {
//設定ReacContext
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;
}
}
}複製程式碼
doInBackground()做完事情之後,onPostExecute()會去呼叫ReactInstanceManager.setupReactContext(),它的實現如下所示:
2.14 ReactInstanceManager.setupReactContext(ReactApplicationContext reactContext)
public class ReactInstanceManager {
private void setupReactContext(ReactApplicationContext reactContext) {
ReactMarker.logMarker(SETUP_REACT_CONTEXT_START);
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "setupReactContext");
UiThreadUtil.assertOnUiThread();
Assertions.assertCondition(mCurrentReactContext == null);
mCurrentReactContext = Assertions.assertNotNull(reactContext);
CatalystInstance catalystInstance =
Assertions.assertNotNull(reactContext.getCatalystInstance());
//執行Native Java module的初始化
catalystInstance.initialize();
//重置DevSupportManager的ReactContext
mDevSupportManager.onNewReactContextCreated(reactContext);
//記憶體狀態回撥設定
mMemoryPressureRouter.addMemoryPressureListener(catalystInstance);
//復位生命週期
moveReactContextToCurrentLifecycleState();
//mAttachedRootViews儲存的是ReactRootView
for (ReactRootView rootView : mAttachedRootViews) {
attachMeasuredRootViewToInstance(rootView, catalystInstance);
}
ReactInstanceEventListener[] listeners =
new ReactInstanceEventListener[mReactInstanceEventListeners.size()];
listeners = mReactInstanceEventListeners.toArray(listeners);
for (ReactInstanceEventListener listener : listeners) {
listener.onReactContextInitialized(reactContext);
}
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(SETUP_REACT_CONTEXT_END);
}
private void attachMeasuredRootViewToInstance(
ReactRootView rootView,
CatalystInstance catalystInstance) {
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "attachMeasuredRootViewToInstance");
UiThreadUtil.assertOnUiThread();
//移除並重置所有頁面UI元素
// Reset view content as it's going to be populated by the application content from JS
rootView.removeAllViews();
rootView.setId(View.NO_ID);
//將ReactRootView作為根佈局
UIManagerModule uiManagerModule = catalystInstance.getNativeModule(UIManagerModule.class);
int rootTag = uiManagerModule.addMeasuredRootView(rootView);
//設定相關
rootView.setRootViewTag(rootTag);
//包裝啟動引數launchOptions與模組名jsAppModuleName
@Nullable Bundle lau∂nchOptions與模組名 = rootView.getLaunchOptions();
WritableMap initialProps = Arguments.makeNativeMap(launchOptions);
String jsAppModuleName = rootView.getJSModuleName();
WritableNativeMap appParams = new WritableNativeMap();
appParams.putDouble("rootTag", rootTag);
appParams.putMap("initialProps", initialProps);
//啟動流程入口:由Java層呼叫啟動
catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);
rootView.onAttachedToReactInstance();
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
}複製程式碼
ReactInstanceManager.attachMeasuredRootViewToInstance()最終進入了RN應用的啟動流程入口,呼叫catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams),
AppRegistry.class是JS層暴露給Java層的介面方法。它的真正實現在AppRegistry.js裡,AppRegistry.js是執行所有RN應用的JS層入口,我們來看看它的實現:
2.15 AppRegistry.runApplication(appKey: string, appParameters: any)
AppRegistry.js
//上面程式碼最終呼叫的就是這個函式
runApplication(appKey: string, appParameters: any): void {
const msg =
'Running application "' + appKey + '" with appParams: ' +
JSON.stringify(appParameters) + '. ' +
'__DEV__ === ' + String(__DEV__) +
', development-level warning are ' + (__DEV__ ? 'ON' : 'OFF') +
', performance optimizations are ' + (__DEV__ ? 'OFF' : 'ON');
infoLog(msg);
BugReporting.addSource('AppRegistry.runApplication' + runCount++, () => msg);
invariant(
runnables[appKey] && runnables[appKey].run,
'Application ' + appKey + ' has not been registered.\n\n' +
'Hint: This error often happens when you\'re running the packager ' +
'(local dev server) from a wrong folder. For example you have ' +
'multiple apps and the packager is still running for the app you ' +
'were working on before.\nIf this is the case, simply kill the old ' +
'packager instance (e.g. close the packager terminal window) ' +
'and start the packager in the correct app folder (e.g. cd into app ' +
'folder and run \'npm start\').\n\n' +
'This error can also happen due to a require() error during ' +
'initialization or failure to call AppRegistry.registerComponent.\n\n'
);
runnables[appKey].run(appParameters);
},複製程式碼
到這裡就會去呼叫JS進行元件渲染,再通過Java層的UIManagerModule將JS元件轉換為Android元件,最終顯示在ReactRootView上。