簡析React Native startReactApplication 方法
在 React Native 啟動流程簡析 這篇文章里,我們梳理了 RN 的啟動流程,最后的 startReactApplication 由于相對復(fù)雜且涉及到最終執(zhí)行前端 js 的流程,我們單獨(dú)將其提取出來,獨(dú)立成文加以分析。
首先來看 startReactApplication 的調(diào)用之處:
mReactRootView.startReactApplication(
getReactNativeHost().getReactInstanceManager(), appKey, mLaunchOptions);
可以看到是在 rootView 上調(diào)用 startReactApplication,入?yún)?instanceManager、appKey、mLaunchOptions。
順著 startReactApplication 扒出其調(diào)用鏈:
mReactInstanceManager.createReactContextInBackground() -> recreateReactContextInBackgroundInner() -> recreateReactContextInBackgroundFromBundleLoader() -> recreateReactContextInBackground() -> runCreateReactContextOnNewThread()
recreateReactContextInBackground 為 ReactInstanceManager 中的方法,做了兩件事:
1.創(chuàng)建 ReactContextInitParams 實(shí)例 initParams,如下,其入?yún)?jsExecutorFactory 為創(chuàng)建 ReactInstanceManager 時傳入。
final ReactContextInitParams initParams =
new ReactContextInitParams(jsExecutorFactory, jsBundleLoader);
2.調(diào)用 runCreateReactContextOnNewThread
runCreateReactContextOnNewThread 為 ReactInstanceManager 中的方法,主要做了兩件事:
- 創(chuàng)建一個新的線程,并在新線程中通過
createReactContext創(chuàng)建ReactContext上下文; - 通過
setupReactContext來設(shè)置上下文環(huán)境,并最終調(diào)用到AppRegistry.js啟動App。
createReactContext
先看其調(diào)用的地方:
final ReactApplicationContext reactApplicationContext =
createReactContext(
initParams.getJsExecutorFactory().create(),
initParams.getJsBundleLoader());
其兩個入?yún)⒎謩e為 JsExecutorFactory 創(chuàng)建的 JavaScriptExecutor 實(shí)例,和 JsBundleLoader 實(shí)例。
JavaScriptExecutor
startReactApplication 第一個入?yún)?getReactNativeHost().getReactInstanceManager() 獲取 ReactInstanceManager 實(shí)例。ReactInstanceManager 實(shí)例在 RN 應(yīng)用中只有一個,先前在創(chuàng)建 MainActivity 時已創(chuàng)建。
回顧 React Native 啟動流程簡析,在創(chuàng)建過程中實(shí)際上是調(diào)用下面的方法:
ReactInstanceManager reactInstanceManager = builder.build()
builder 即 ReactInstanceManagerBuilder,我們來到該類的 build 方法,發(fā)現(xiàn)其最終是執(zhí)行 return new ReactInstanceManager(...),在構(gòu)造參數(shù)中第 4 個參數(shù)即為:getDefaultJSExecutorFactory,來到其定義處:
private JavaScriptExecutorFactory getDefaultJSExecutorFactory(
String appName, String deviceName, Context applicationContext) {
try {
// If JSC is included, use it as normal
initializeSoLoaderIfNecessary(applicationContext);
SoLoader.loadLibrary("jscexecutor");
return new JSCExecutorFactory(appName, deviceName);
} catch (UnsatisfiedLinkError jscE) { /* ... */ }
}
也就是說在創(chuàng)建 ReactInstanceManagerBuilder 時我們就創(chuàng)建了 JSCExecutorFactory,并在隨后調(diào)用其 create 方法創(chuàng)建 JSCExecutor 。JSCExecutorFactory 實(shí)現(xiàn)了 JavaScriptExecutorFactory 接口,其 create 方法如下,返回了 JSCExecutor 實(shí)例:
@Override
public JavaScriptExecutor create() throws Exception {
WritableNativeMap jscConfig = new WritableNativeMap();
jscConfig.putString("OwnerIdentity", "ReactNative");
jscConfig.putString("AppIdentity", mAppName);
jscConfig.putString("DeviceIdentity", mDeviceName);
return new JSCExecutor(jscConfig);
}
再往下看 JSCExecutor 的定義,其繼承自 JavaScriptExecutor 類:
@DoNotStrip
/* package */ class JSCExecutor extends JavaScriptExecutor {
static {
SoLoader.loadLibrary("jscexecutor");
}
/* package */ JSCExecutor(ReadableNativeMap jscConfig) {
super(initHybrid(jscConfig));
}
@Override
public String getName() {
return "JSCExecutor";
}
private static native HybridData initHybrid(ReadableNativeMap jscConfig);
}
于是就很清楚了,createReactContext 第一個參數(shù)為 JSCExecutor 實(shí)例,是通過 SoLoader 加載的 C++ 模塊。
JsBundleLoader
同樣的,在 return new ReactInstanceManager(...),其構(gòu)造參數(shù)中第 5 個參數(shù)為:JSBundleLoader.createAssetLoader(mApplication, mJSBundleAssetUrl, false)
來到其定義之處,發(fā)現(xiàn)其返回了 JSBundleLoader 實(shí)例,并重寫了其 loadScript 方法。
public static JSBundleLoader createAssetLoader(
final Context context, final String assetUrl, final boolean loadSynchronously) {
return new JSBundleLoader() {
@Override
public String loadScript(JSBundleLoaderDelegate delegate) {
delegate.loadScriptFromAssets(context.getAssets(), assetUrl, loadSynchronously);
return assetUrl;
}
};
}
在創(chuàng)建完 JSCExecutor 實(shí)例和 JSBundleLoader 實(shí)例后,正式進(jìn)入到 createReactContext 方法。
createReactContext
private ReactApplicationContext createReactContext(
final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
CatalystInstanceImpl.Builder catalystInstanceBuilder = /* ... */
try {
catalystInstance = catalystInstanceBuilder.build();
} finally { /* ... */ }
reactContext.initializeWithInstance(catalystInstance);
TurboModuleManager turboModuleManager =
new TurboModuleManager( /* ... */ )
catalystInstance.setTurboModuleManager(turboModuleManager);
if (mJSIModulePackage != null) {
catalystInstance.addJSIModules( /* ... */ );
}
catalystInstance.runJSBundle();
return reactContext;
在這里面,首先創(chuàng)建了 reactContext,并通過 catalystInstanceBuilder 創(chuàng)建了 catalystInstance。接著通過 initializeWithInstance 方法將 reactContext 和 catalystInstance 關(guān)聯(lián)起來,并進(jìn)行了一系列的為 catalystInstance 初始化的工作。最后進(jìn)入到方法 catalystInstance.runJSBundle() 中。
initializeWithInstance
通過調(diào)用 getUIQueueThread、getNativeModulesQueueThread、getJSQueueThread創(chuàng)建了3個線程隊(duì)列,分別是 UI線程、NativeModules 線程,和 JS 線程。
runJSBundle
public void runJSBundle() {
mJSBundleLoader.loadScript(CatalystInstanceImpl.this);
synchronized (mJSCallsPendingInitLock) {
mAcceptCalls = true;
for (PendingJSCall function : mJSCallsPendingInit) {
function.call(this);
}
mJSCallsPendingInit.clear();
mJSBundleHasLoaded = true;
}
Systrace.registerListener(mTraceListener);
}
通過先前返回的 mJSBundleLoader 執(zhí)行其 loadScript 方法:
public String loadScript(JSBundleLoaderDelegate delegate) {
delegate.loadScriptFromAssets(context.getAssets(), assetUrl, loadSynchronously);
return assetUrl;
}
loadScriptFromAssets 方法在 CatalystInstanceImpl 中:
public void loadScriptFromAssets(
AssetManager assetManager, String assetURL, boolean loadSynchronously) {
mSourceURL = assetURL;
jniLoadScriptFromAssets(assetManager, assetURL, loadSynchronously);
}
這里的 assetURL 是在 createAssetLoader 創(chuàng)建 mJSBundleLoader 時傳入,其賦值時機(jī)是在 reactInstanceManagerBuilder 實(shí)例中,由 reactNativeHost 實(shí)例的 createReactInstanceManager 方法。若 開發(fā)者在 MainApplication.java 中通過重寫 getJSBundleFile 方法自定義了 assetURL 則使用該 url,否則使用系統(tǒng)默認(rèn),如:file://sdcard/myapp_cache/index.android.bundle。
jniLoadScriptFromAssets 方法為 C++ 側(cè)定義的方法,用于讀取 js 文件。為什么 Java 代碼中可以直接調(diào)用 C++ 方法,這里還要打個問號,后續(xù)在分析 Java 與 C++ 通信及 Java 與 JS 通信時闡釋。
通過 createReactContext 創(chuàng)建了 reactContext,創(chuàng)建了 catalystInstance 實(shí)例,并將上述兩者關(guān)聯(lián),接著通過 catalystInstance 讀入 js 文件。接下來就進(jìn)入到 setupReactContext 的環(huán)節(jié)。
setupReactContext
private void setupReactContext(final ReactApplicationContext reactContext) {
synchronized (mAttachedReactRoots) {
catalystInstance.initialize();
for (ReactRoot reactRoot : mAttachedReactRoots) {
if (reactRoot.getState().compareAndSet(ReactRoot.STATE_STOPPED, ReactRoot.STATE_STARTED)) {
attachRootViewToInstance(reactRoot);
}
}
}
UiThreadUtil.runOnUiThread(
public void run() {
listener.onReactContextInitialized(reactContext);
}
)
reactContext.runOnJSQueueThread(
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
}
)
reactContext.runOnNativeModulesQueueThread(
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
}
)
}
這里面做的事情如下:
- catalystInstance.initialize(): 所有原生模塊的初始化
- attachRootViewToInstance(reactRoot): 繪制所有的 RootView 并添加到相應(yīng)實(shí)例并設(shè)置相應(yīng)的監(jiān)聽事件
- 創(chuàng)建 UI 模塊、JS 模塊和原生模塊線程,并設(shè)置 JS 模塊和原生模塊所在線程的優(yōu)先級
總結(jié)本文
從 createReactContext 和 setupReactContext 兩個方法的源碼出發(fā),分析了 RN startReactApplication 方法的執(zhí)行過程,其中:
createReactContext 的主要作用是:創(chuàng)建 reactContext、創(chuàng)建 catalystInstance 實(shí)例,并將上述兩者關(guān)聯(lián),接著通過 catalystInstance 讀入 js 文件。
setupReactContext的主要作用是:初始化所有原生模塊,繪制所有 rootview,創(chuàng)建 UI 模塊、JS 模塊和原生模塊線程,并設(shè)置優(yōu)先級。
到此這篇關(guān)于React Native startReactApplication 方法簡析的文章就介紹到這了,更多相關(guān)React Native startReactApplication內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
一文詳解ReactNative狀態(tài)管理rematch使用
這篇文章主要為大家介紹了ReactNative狀態(tài)管理rematch使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
React實(shí)現(xiàn)生成和導(dǎo)出Word文檔的方法詳解
React是一個流行的JavaScript庫,用于構(gòu)建現(xiàn)代前端應(yīng)用程序,本文將深入探討如何在React中生成和導(dǎo)出Word文檔,感興趣的小伙伴可以學(xué)習(xí)一下2023-09-09
React RenderProps模式運(yùn)用過程淺析
render props是指一種在 React 組件之間使用一個值為函數(shù)的 prop 共享代碼的技術(shù)。簡單來說,給一個組件傳入一個prop,這個props是一個函數(shù),函數(shù)的作用是用來告訴這個組件需要渲染什么內(nèi)容,那么這個prop就成為render prop2023-03-03
React項(xiàng)目中動態(tài)插入HTML內(nèi)容的實(shí)現(xiàn)
本文主要介紹了React項(xiàng)目中動態(tài)插入HTML內(nèi)容的實(shí)現(xiàn),通過使用React的dangerouslySetInnerHTML屬性,我們可以將HTML內(nèi)容插入到組件中,具有一定的參考價值,感興趣的可以了解一下2023-10-10
React結(jié)合Drag?API實(shí)現(xiàn)拖拽示例詳解
這篇文章主要為大家介紹了React結(jié)合Drag?API實(shí)現(xiàn)拖拽示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
React手寫簽名組件react-signature實(shí)現(xiàn)簽字demo
這篇文章主要為大家介紹了React手寫簽名組件react-signature實(shí)現(xiàn)簽字demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12

