久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

如何進行ReactNative For Android 框架啟動核心路徑剖析

154次閱讀
沒有評論

共計 13157 個字符,預計需要花費 33 分鐘才能閱讀完成。

如何進行 ReactNative For Android 框架啟動核心路徑剖析,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。

前面給大家分析過 ReactNative For Android (RN4A) 的通信機制,這次我們從源碼出發,分析下 RN4A 的啟動過程。啟動過程基于通信機制,涉及通信機制原理大家可以查看前一篇文章。

上面是 2016 React.js Conf FB 工程師分享的 RN 啟動時序圖,整個過程比較清晰,先啟動終端運行時,隨后由終端上下文去啟動 JS 的運行時,進而布局,最后再由終端進行渲染,最后將 View 添加到 RootView 上。那接下來,我們先理解幾個概念,方便后續我們對整個啟動過程的理解。

模塊:

模塊即暴露給調用方的 API 集合,在 RN4A 存在兩種模塊。

一種是 Native 層暴露給 Js 層的 API 集合模塊,即 NativeModule,如 ToastModule,DialogModule, 或是創建 View 的 UIManagerModule。業務方可以通過實現 NativeModule 自定義模塊,通過重寫 getName 將模塊名暴露給 Js 層,通過注解的方式將 API 暴露給 Js 層調用。

另一種是 Js 層暴露給 Java 層的 API 集合模塊,即 JavascriptModule,如 DeviceEventEmitter,AppRegistry 等。業務方可以通過繼承 JavaScriptModule 接口自定義接口模塊,申明與 Js 層相應的方法即可。

無論是 NativeModule 還是 JavascriptModule,在 Js 層存在與之相互映射同名的 Module,Js 層通過 require 引用 Module。

模塊注冊表:

各模塊信息統一收集到模塊注冊表。同樣,在 RN4A 中存在兩種模塊注冊表,一是由集合所有 Java 層模塊接口信息的 NativeModuleRegistry,另一種是集合所有 Js 層模塊接口信息的 JavascriptModuleRegistry。在啟動 RN4A 后,終端將注冊表信息存入與前端互通的全局變量__fbBatchedBridgeConfig  中,使得 Js 層與 Java 層存在同樣的模塊注冊表。

正如上面 FB 攻城獅提出的時序圖,從終端啟動,入口是 ReactRootView.startReactApplication,在構造 JavaScriptExecutor JSBundleLoader 后,進而通過 ReactContextInitAsycnTask 去創建 ReactContext,這部分主要創建了 NativeModules,JavaScriptModule 及其對的注冊表,負責 Js 與 Java 通信的高層接口 CatalystInstance 等。在創建完 ReactContext 后,通過 CatalystInstance 獲取 AppRegistry 并調用其 runApplication 啟動 Js Application。整體流程如下:

接下來進入正題,從源碼來分析 RN4A 的啟動(為閱讀方便,源碼適當裁剪)

ReactInstanceManager createReactContextInBackground,通過 AysncTask 初始化 ReactNative 上下文。mJSModuleName 是與前端約定好所要啟動的 JS Application Name。mLauncahOptions 是終端啟動前端 Application 可選的傳入的參數。

/**
 * ReactRootView.java
 */
public void startReactApplication(
 ReactInstanceManager reactInstanceManager,
 String moduleName,
 @Nullable Bundle launchOptions) { UiThreadUtil.assertOnUiThread();
 mReactInstanceManager = reactInstanceManager;
 mJSModuleName = moduleName;
 mLaunchOptions = launchOptions;
 if (!mReactInstanceManager.hasStartedCreatingInitialContext()) { mReactInstanceManager.createReactContextInBackground();
 }
 if (mWasMeasured   mIsAttachedToWindow) { mReactInstanceManager.attachMeasuredRootView(this);
 mIsAttachedToInstance = true;
 getViewTreeObserver().addOnGlobalLayoutListener(getKeyboardListener());
 } else {
 mAttachScheduled = true;
 }
`

createReactContextInBackground 最終調用到 recreateReactContextInBackgroundFromBundleFile。這里會創建兩個 Key Obj : JSCJavaScriptExecutor JSBundleLoader。
JSCJavaScriptExecutor 繼承自 JavaScriptExecutor,在 JSCJavaScriptExecutor.class 加載會加載 ReactNative 的 SO,并且,在初始 JSCJavaScriptExecutor 時會調用 initialze 去初始 C ++ 層 ReactNative 與 JSC 的通信框架等。
JSBundleLoader 緩存了 JsBundle 的信息,封裝了上層加載 JsBundle 相關接口,CatalystInstance 通過其間接調用 ReactBridge 去加載文件。

/**
 * ReactInstanceManagerImpl.java
 */
private void recreateReactContextInBackgroundFromBundleFile() {
 recreateReactContextInBackground( new JSCJavaScriptExecutor.Factory(),
 JSBundleLoader.createFileLoader(mApplicationContext, mJSBundleFile));
}

創建完 JSCJavaScriptExecutor JSBundleLoader 后,execute ReactContextInitAsyncTask 繼續初始化 ReactContext。

/**
 * ReactInstanceManagerImpl.java
 */
private void recreateReactContextInBackground(
 JavaScriptExecutor.Factory jsExecutorFactory,
 JSBundleLoader jsBundleLoader) {
 ReactContextInitParams initParams =
 new ReactContextInitParams(jsExecutorFactory, jsBundleLoader);
 if (!mIsContextInitAsyncTaskRunning) { ReactContextInitAsyncTask initTask = new ReactContextInitAsyncTask();
 initTask.execute(initParams);
 mIsContextInitAsyncTaskRunning = true;
 } else {
 mPendingReactContextInitParams = initParams;
 }
}

ReactContextInitAsyncTask 為創建 ReactContext 的核心類,在執行初始化前會銷毀先前的上下文,保證只存在一個上下文。隨后,調用 createReactContext 進一步創建 ReactContext。在創建完 React Context 后會調用 setUpReactContext,進而通知 DevSupportManager 更新上下文,更新生命周期,將 ReactRootView 做為 Root View 傳遞給 UIManagerModule,調用 AppRegistry 的 runApplication 去啟動 Js Application 等。

/**
 * ReactInstanceManagerImpl$ReactContextInitAsynTask.java
 */
private final class ReactContextInitAsyncTask extends
 AsyncTask ReactContextInitParams, Void, Result ReactApplicationContext  {
 @Override
 protected void onPreExecute() { if (mCurrentReactContext != null) { tearDownReactContext(mCurrentReactContext);
 mCurrentReactContext = null;
 }
 }
 @Override
 protected Result ReactApplicationContext  doInBackground(ReactContextInitParams... params) { Assertions.assertCondition(params != null   params.length   0   params[0] != null);
 try { JavaScriptExecutor jsExecutor = params[0].getJsExecutorFactory().create();
 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);
 }
 }
 @Override
 protected void onPostExecute(Result ReactApplicationContext  result) {
 try { setupReactContext(result.get());
 } catch (Exception e) { mDevSupportManager.handleException(e);
 } finally {
 mIsContextInitAsyncTaskRunning = false;
 }
 // Handle enqueued request to re-initialize react context.
 if (mPendingReactContextInitParams != null) {
 recreateReactContextInBackground( mPendingReactContextInitParams.getJsExecutorFactory(),
 mPendingReactContextInitParams.getJsBundleLoader());
 mPendingReactContextInitParams = null;
 }
 }
}

在 CreateReactContext 中,主要有以下 5 個 key path:

通過 Builder 構建上文概念講過的 NativeModuleRegistry 及 JavaScriptModuleConfig;

創建 ReactApplicationContext。ReactApplicationContext 繼承自 ContextWrapper,主要緩存了 Application Context,Activity Context,ReactNative 處理消息的三個 thread(下篇講述),還有就是全局控制 JS 調用導致 Native Module Crash 的 NativeModuleCallExceptionHandler,在初始化 ReactInstanceManager 的時候傳入,并且要關閉 DeveloperSupport 后才可以啟用,假如不傳,則默認交由 DevSupportManger 去處理;

創建 ReactPackage。ReactPackage 主要通過 createNativeModules、createJSModules 和 createViewManagers 等 API 去創建本地模塊,JS 模塊及視圖組件等。ReactPackage 分為 framework 的 CoreModulesPackage 和業務方可選的基礎 MainReactPackage,CoreModulesPackage 封裝了大部分通信,調試核心類,如 UIManagerModule,這個負責控制 Js 層 Dom 到 Native View 的核心類;

創建 CatalystInstance。CatalystInstance 并不直接面向開發者,開發者通 ReactInstanceManger 間接操作 CatalystInstance。CatalystInstance 持有對 ReactBridge 的引用,主要通過 ReactBridge 這個 JNI 類去實現 Java 層與 Js 層的通信,ReactBridge 由 CatalystInstance 的 Constructor 創建。同時初始化的時候調用了 ReactQueueConfigurationSpec.createDefault 創建了 ReactNative 通信的兩個線程 JsQueueThread NativeModulesQueueThread;

調用 reactContext.initializeWithInstance 進一步將創建完的 CatalystInstance 及線程等緩存在 ReactContext 中;

調用 catalystInstance.runJSBundle 加載解析 Jsbundle;

/**
 * ReactInstanceManagerImpl.java
 *
 * @return instance of {@link ReactContext} configured a {@link CatalystInstance} set
 */
private ReactApplicationContext createReactContext(
 JavaScriptExecutor jsExecutor,
 JSBundleLoader jsBundleLoader) { mSourceUrl = jsBundleLoader.getSourceUrl();
 NativeModuleRegistry.Builder nativeRegistryBuilder = new NativeModuleRegistry.Builder();
 JavaScriptModulesConfig.Builder jsModulesBuilder = new JavaScriptModulesConfig.Builder();
 ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
 if (mUseDeveloperSupport) { reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager);
 }
 CoreModulesPackage coreModulesPackage =
 new CoreModulesPackage(this, mBackBtnHandler, mUIImplementationProvider);
 processPackage(coreModulesPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
 for (ReactPackage reactPackage : mPackages) { processPackage(reactPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
 }
 nativeModuleRegistry = nativeRegistryBuilder.build();
 javaScriptModulesConfig = jsModulesBuilder.build();
 NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null
 ? mNativeModuleCallExceptionHandler
 : mDevSupportManager;
 CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
 .setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
 .setJSExecutor(jsExecutor)
 .setRegistry(nativeModuleRegistry)
 .setJSModulesConfig(javaScriptModulesConfig)
 .setJSBundleLoader(jsBundleLoader)
 .setNativeModuleCallExceptionHandler(exceptionHandler);
 CatalystInstance catalystInstance= catalystInstanceBuilder.build();
 if (mBridgeIdleDebugListener != null) { catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);
 }
 reactContext.initializeWithInstance(catalystInstance);
 catalystInstance.runJSBundle();
 return reactContext;
}

ReactBridge 由 CatalystInstance 的 Constructor 創建。

/**
 * CatalystInstanceImpl.java
 */
private CatalystInstanceImpl(
 final ReactQueueConfigurationSpec ReactQueueConfigurationSpec,
 final JavaScriptExecutor jsExecutor,
 final NativeModuleRegistry registry,
 final JavaScriptModulesConfig jsModulesConfig,
 final JSBundleLoader jsBundleLoader,
 NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
 mReactQueueConfiguration = ReactQueueConfigurationImpl.create(
 ReactQueueConfigurationSpec,
 new NativeExceptionHandler());
 mBridgeIdleListeners = new CopyOnWriteArrayList ();
 mJavaRegistry = registry;
 mJSModuleRegistry = new JavaScriptModuleRegistry(CatalystInstanceImpl.this, jsModulesConfig);
 mJSBundleLoader = jsBundleLoader;
 mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;
 mTraceListener = new JSProfilerTraceListener();
 try { mBridge = mReactQueueConfiguration.getJSQueueThread().callOnQueue( new Callable ReactBridge () {
 @Override
 public ReactBridge call() throws Exception {
 Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,  initializeBridge 
 try { return initializeBridge(jsExecutor, jsModulesConfig);
 } finally { Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
 }
 }
 }).get();
 } catch (Exception t) { throw new RuntimeException( Failed to initialize bridge , t);
 }
}

ReactBridge 將注冊表信息存入與前端互通的全局變量 __fbBatchedBridgeConfig 中,使得 Js 層與 Java 層存在同樣的模塊注冊表。

/**
 * CatalystInstanceImpl.java
 */
private ReactBridge initializeBridge(
 JavaScriptExecutor jsExecutor,
 JavaScriptModulesConfig jsModulesConfig) { ReactBridge bridge = new ReactBridge(jsExecutor, new NativeModulesReactCallback(),
 mReactQueueConfiguration.getNativeModulesQueueThread());
 Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,  setBatchedBridgeConfig 
 bridge.setGlobalVariable(
  __fbBatchedBridgeConfig ,
 buildModulesConfigJSONProperty(mJavaRegistry, jsModulesConfig));
 bridge.setGlobalVariable(
  __RCTProfileIsProfiling ,
 return bridge;
}

調用 catalystInstance.runJSBundle 加載解析 Jsbundle。假如在解析過程中出現 Exception,統一交給 NativeModuleCallExceptionHandler 處理,建議開發者設置自己的 NativeModuleCallExceptionHandler,可以歸避部分 Crash(SyntaxError: Unexpected token‘‘或 SyntaxError: Unexpected end of script)。

/**
 * CatalystInstanceImpl.java
 */
public void runJSBundle() {
 try { mJSBundleHasLoaded = mReactQueueConfiguration.getJSQueueThread().callOnQueue( new Callable Boolean () {
 @Override
 public Boolean call() throws Exception { incrementPendingJSCalls();
 try { mJSBundleLoader.loadScript(mBridge);
 Systrace.registerListener(mTraceListener);
 } catch (JSExecutionException e) { mNativeModuleCallExceptionHandler.handleException(e);
 } finally { Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
 }
 return true;
 }
 }).get();
 } catch (Exception t) { throw new RuntimeException(t);
 }
}

在創建完 React Context 后會執行 ReactContextInitAsyncTask 的 onPostExecute,從而調用 setUpReactContext,會將 ReactRootView 做為 Root View 傳遞給 UIManagerModule,此后 Js 通過 UIManager 創建的 View 都會 add 到該 View 上。

/**
 * ReactInstanceManagerImpl.java
 */
@Override
public void attachMeasuredRootView(ReactRootView rootView) { UiThreadUtil.assertOnUiThread();
 if(mIsNeedDetachView){
 Log.d(ReactConstants.QZONE_REACT_SRC_TAG, attachMeasuredRootView do add 
 mAttachedRootViews.add(rootView);
 // If react context is being created in the background, JS application will be started
 // automatically when creation completes, as root view is part of the attached root view list.
 if (!mIsContextInitAsyncTaskRunning   mCurrentReactContext != null) { attachMeasuredRootViewToInstance(rootView, mCurrentReactContext.getCatalystInstance());
 }
 }else{
 Log.d(ReactConstants.QZONE_REACT_SRC_TAG, attachMeasuredRootView do nothing 
 }
}

在綁定完 RootView 后,通過 CatalystInstance 獲取 AppRegistry 這個 JSModule 后,進一步調用 runApplication 啟動 Js Application。

/**
 * ReactInstanceManagerImpl.java
 */
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);
 @Nullable Bundle launchOptions = rootView.getLaunchOptions();
 WritableMap initialProps = launchOptions != null
 ? Arguments.fromBundle(launchOptions)
 : Arguments.createMap();
 String jsAppModuleName = rootView.getJSModuleName();
 WritableNativeMap appParams = new WritableNativeMap();
 appParams.putDouble(rootTag , rootTag);
 appParams.putMap(initialProps , initialProps);
 catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);
}

ReactNative 中 Java 與 Js 通信不再贅述。至此,啟動 Js 層 AppRegistry 的 runApplication 啟動 Js Application。

/**
* AppRegistry.js
runApplication: function(appKey: string, appParameters: any): void {
 console.log(
  Running application   + appKey +   with appParams:   +
 JSON.stringify(appParameters) +  .   +
  __DEV__ ===   + String(__DEV__) +
  , development-level warning are   + (__DEV__ ?  ON  :  OFF) +
  , performance optimizations are   + (__DEV__ ?  OFF  :  ON)
 );
 invariant( runnables[appKey]   runnables[appKey].run,
  Application   + appKey +   has not been registered. This   +
  is either due to a require() error during initialization   +
  or failure to call AppRegistry.registerComponent. 
 );
 runnables[appKey].run(appParameters);
},

看完上述內容,你們掌握如何進行 ReactNative For Android 框架啟動核心路徑剖析的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注丸趣 TV 行業資訊頻道,感謝各位的閱讀!

正文完
 
丸趣
版權聲明:本站原創文章,由 丸趣 2023-08-16發表,共計13157字。
轉載說明:除特殊說明外本站除技術相關以外文章皆由網絡搜集發布,轉載請注明出處。
評論(沒有評論)
主站蜘蛛池模板: 元氏县| 灌南县| 区。| 长子县| 永仁县| 科技| 托里县| 八宿县| 浦县| 乐平市| 墨竹工卡县| 应用必备| 宾阳县| 徐州市| 泸溪县| 通州区| 星子县| 县级市| 怀宁县| 鹤山市| 都匀市| 姚安县| 志丹县| 扎囊县| 新平| 开远市| 汶川县| 阳春市| 凤山县| 潜山县| 开原市| 军事| 区。| 鄢陵县| 武宁县| 剑阁县| 嘉荫县| 申扎县| 抚州市| 当雄县| 法库县|