簡析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個線程隊列,分別是 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)文章
React?Hooks使用startTransition與useTransition教程示例
這篇文章主要為大家介紹了React?Hooks使用startTransition與useTransition教程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01React教程之封裝一個Portal可復(fù)用組件的方法
react的核心之一是組件,下面這篇文章主要給大家介紹了關(guān)于React教程之封裝一個Portal可復(fù)用組件的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2018-01-01React獲取Java后臺文件流并下載Excel文件流程解析
這篇文章主要介紹了React獲取Java后臺文件流下載Excel文件,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-06-06深入學(xué)習(xí)TypeScript 、React、 Redux和Ant-Design的最佳實(shí)踐
這篇文章主要介紹了深入學(xué)習(xí)TypeScript 、React、 Redux和Ant-Design的最佳實(shí)踐,TypeScript 增加了代碼的可讀性和可維護(hù)性,擁有活躍的社區(qū),,需要的朋友可以參考下2019-06-06淺談react-native熱更新react-native-pushy集成遇到的問題
下面小編就為大家?guī)硪黄獪\談react-native熱更新react-native-pushy集成遇到的問題。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-09-09在react項目中使用antd的form組件,動態(tài)設(shè)置input框的值
這篇文章主要介紹了在react項目中使用antd的form組件,動態(tài)設(shè)置input框的值,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10React中的useState和useEffect詳細(xì)解析
useState和useEffect是React的兩個重要Hook,用于組件狀態(tài)管理和處理副作用,useState允許添加狀態(tài)變量,控制組件渲染,而useEffect用于執(zhí)行渲染后的副作用操作,本文給大家介紹React中的useState和useEffect詳細(xì)解析,感興趣的朋友跟隨小編一起看看吧2024-10-10