版權聲明:本文由王少鳴原創文章,轉載請註明出處:
文章原文連接:https://www.qcloud.com/community/article/144前端
來源:騰雲閣 https://www.qcloud.com/communityjava
前面給你們分析過 ReactNative For Android (RN4A) 的通訊機制,此次咱們從源碼出發,分析下RN4A的啓動過程。啓動過程基於通訊機制,涉及通訊機制原理你們能夠查看前一篇文章,本篇不贅述。react
上面是2016 React.js Conf FB 工程師分享的RN啓動時序圖,整個過程比較清晰,先啓動終端運行時,隨後由終端上下文去啓動JS的運行時,進而佈局,最後再由終端進行渲染,最後將View添加到RootView上。那接下來,咱們先理解幾個概念,方便後續咱們對整個啓動過程的理解。緩存
模塊即暴露給調用方的API集合,在RN4A存在兩種模塊。app
一種是Native層暴露給Js層的API集合模塊,即NativeModule,如ToastModule,DialogModule,或是建立View的UIManagerModule。業務方能夠經過實現NativeModule自定義模塊,經過重寫getName將模塊名暴露給Js層,經過註解的方式將API暴露給Js層調用。框架
另外一種是Js層暴露給Java層的API集合模塊,即JavascriptModule,如DeviceEventEmitter,AppRegistry等。業務方能夠經過繼承JavaScriptModule接口自定義接口模塊,申明與Js層相應的方法便可。ide
不管是NativeModule仍是JavascriptModule,在Js層存在與之相互映射同名的Module,Js層經過require引用Module。佈局
各模塊信息統一收集到模塊註冊表。一樣,在RN4A中存在兩種模塊註冊表,一是由集合全部Java層模塊接口信息的NativeModuleRegistry,另外一種是集合全部Js層模塊接口信息的JavascriptModuleRegistry。在啓動RN4A後,終端將註冊表信息存入與前端互通的全局變量__fbBatchedBridgeConfig
中,使得Js層與Java層存在一樣的模塊註冊表。ui
正如上面FB攻城獅提出的時序圖,從終端啓動,入口是ReactRootView.startReactApplication,在構造JavaScriptExecutor&JSBundleLoader後,進而經過ReactContextInitAsycnTask去建立ReactContext,這部分主要建立了NativeModules,JavaScriptModule及其對的註冊表,負責Js與Java通訊的高層接口CatalystInstance等。在建立完ReactContext後,經過CatalystInstance獲取AppRegistry並調用其runApplication啓動Js Application。總體流程以下:this
接下來進入正題,從源碼來分析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:
/** * 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); },