如何進(jìn)行ReactNative For Android 框架啟動核心路徑剖析,相信很多沒有經(jīng)驗的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。
創(chuàng)新互聯(lián)從2013年成立,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項目成都網(wǎng)站制作、成都做網(wǎng)站、外貿(mào)營銷網(wǎng)站建設(shè)網(wǎng)站策劃,項目實施與項目整合能力。我們以讓每一個夢想脫穎而出為使命,1280元陵川做網(wǎng)站,已為上家服務(wù),為陵川各地企業(yè)和個人服務(wù),聯(lián)系電話:18982081108
前面給大家分析過 ReactNative For Android (RN4A) 的通信機(jī)制,這次我們從源碼出發(fā),分析下RN4A的啟動過程。啟動過程基于通信機(jī)制,涉及通信機(jī)制原理大家可以查看前一篇文章。
上面是2016 React.js Conf FB 工程師分享的RN啟動時序圖,整個過程比較清晰,先啟動終端運(yùn)行時,隨后由終端上下文去啟動JS的運(yùn)行時,進(jìn)而布局,最后再由終端進(jìn)行渲染,最后將View添加到RootView上。那接下來,我們先理解幾個概念,方便后續(xù)我們對整個啟動過程的理解。
模塊即暴露給調(diào)用方的API集合,在RN4A存在兩種模塊。
一種是Native層暴露給Js層的API集合模塊,即NativeModule,如ToastModule,DialogModule,或是創(chuàng)建View的UIManagerModule。業(yè)務(wù)方可以通過實現(xiàn)NativeModule自定義模塊,通過重寫getName將模塊名暴露給Js層,通過注解的方式將API暴露給Js層調(diào)用。
另一種是Js層暴露給Java層的API集合模塊,即JavascriptModule,如DeviceEventEmitter,AppRegistry等。業(yè)務(wù)方可以通過繼承JavaScriptModule接口自定義接口模塊,申明與Js層相應(yīng)的方法即可。
無論是NativeModule還是JavascriptModule,在Js層存在與之相互映射同名的Module,Js層通過require引用Module。
各模塊信息統(tǒng)一收集到模塊注冊表。同樣,在RN4A中存在兩種模塊注冊表,一是由集合所有Java層模塊接口信息的NativeModuleRegistry,另一種是集合所有Js層模塊接口信息的JavascriptModuleRegistry。在啟動RN4A后,終端將注冊表信息存入與前端互通的全局變量__fbBatchedBridgeConfig
中,使得Js層與Java層存在同樣的模塊注冊表。
正如上面FB攻城獅提出的時序圖,從終端啟動,入口是ReactRootView.startReactApplication,在構(gòu)造JavaScriptExecutor&JSBundleLoader后,進(jìn)而通過ReactContextInitAsycnTask去創(chuàng)建ReactContext,這部分主要創(chuàng)建了NativeModules,JavaScriptModule及其對的注冊表,負(fù)責(zé)Js與Java通信的高層接口CatalystInstance等。在創(chuàng)建完ReactContext后,通過CatalystInstance獲取AppRegistry并調(diào)用其runApplication啟動Js Application。整體流程如下:
接下來進(jìn)入正題,從源碼來分析RN4A的啟動(為閱讀方便,源碼適當(dāng)裁剪)
ReactInstanceManager createReactContextInBackground,通過AysncTask初始化ReactNative上下文。mJSModuleName是與前端約定好所要啟動的JS Application Name。mLauncahOptions是終端啟動前端Application可選的傳入的參數(shù)。
/** * 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最終調(diào)用到recreateReactContextInBackgroundFromBundleFile。這里會創(chuàng)建兩個Key Obj : JSCJavaScriptExecutor&JSBundleLoader。
JSCJavaScriptExecutor繼承自JavaScriptExecutor,在JSCJavaScriptExecutor.class加載會加載ReactNative的SO,并且,在初始JSCJavaScriptExecutor時會調(diào)用initialze去初始C++層ReactNative與JSC的通信框架等。
JSBundleLoader緩存了JsBundle的信息,封裝了上層加載JsBundle相關(guān)接口,CatalystInstance通過其間接調(diào)用ReactBridge去加載文件。
/** * ReactInstanceManagerImpl.java */ private void recreateReactContextInBackgroundFromBundleFile() { recreateReactContextInBackground( new JSCJavaScriptExecutor.Factory(), JSBundleLoader.createFileLoader(mApplicationContext, mJSBundleFile)); }
創(chuàng)建完JSCJavaScriptExecutor&JSBundleLoader后,execute ReactContextInitAsyncTask繼續(xù)初始化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為創(chuàng)建ReactContext的核心類,在執(zhí)行初始化前會銷毀先前的上下文,保證只存在一個上下文。隨后,調(diào)用createReactContext進(jìn)一步創(chuàng)建ReactContext。在創(chuàng)建完React Context后會調(diào)用setUpReactContext,進(jìn)而通知DevSupportManager更新上下文,更新生命周期,將ReactRootView做為Root View傳遞給UIManagerModule,調(diào)用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構(gòu)建上文概念講過的NativeModuleRegistry及JavaScriptModuleConfig;
創(chuàng)建ReactApplicationContext。ReactApplicationContext繼承自ContextWrapper,主要緩存了Application Context,Activity Context,ReactNative處理消息的三個thread(下篇講述),還有就是全局控制JS調(diào)用導(dǎo)致Native Module Crash的NativeModuleCallExceptionHandler,在初始化ReactInstanceManager的時候傳入,并且要關(guān)閉DeveloperSupport后才可以啟用,假如不傳,則默認(rèn)交由DevSupportManger去處理;
創(chuàng)建ReactPackage。ReactPackage主要通過createNativeModules、createJSModules和createViewManagers等API去創(chuàng)建本地模塊,JS模塊及視圖組件等。ReactPackage分為framework的CoreModulesPackage和業(yè)務(wù)方可選的基礎(chǔ)MainReactPackage,CoreModulesPackage封裝了大部分通信,調(diào)試核心類,如UIManagerModule,這個負(fù)責(zé)控制Js層Dom到Native View的核心類;
創(chuàng)建CatalystInstance。CatalystInstance并不直接面向開發(fā)者,開發(fā)者通ReactInstanceManger間接操作CatalystInstance。CatalystInstance持有對ReactBridge的引用,主要通過ReactBridge這個JNI類去實現(xiàn)Java層與Js層的通信,ReactBridge由CatalystInstance的Constructor創(chuàng)建。同時初始化的時候調(diào)用了ReactQueueConfigurationSpec.createDefault創(chuàng)建了ReactNative通信的兩個線程 JsQueueThread&NativeModulesQueueThread;
調(diào)用reactContext.initializeWithInstance進(jìn)一步將創(chuàng)建完的CatalystInstance及線程等緩存在ReactContext中;
調(diào)用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創(chuàng)建。
/** * 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; }
調(diào)用catalystInstance.runJSBundle加載解析Jsbundle。假如在解析過程中出現(xiàn)Exception,統(tǒng)一交給NativeModuleCallExceptionHandler處理,建議開發(fā)者設(shè)置自己的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); } }
在創(chuàng)建完React Context后會執(zhí)行ReactContextInitAsyncTask的onPostExecute,從而調(diào)用setUpReactContext,會將ReactRootView做為Root View傳遞給UIManagerModule,此后Js通過UIManager創(chuàng)建的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后,進(jìn)一步調(diào)用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); },
看完上述內(nèi)容,你們掌握如何進(jìn)行ReactNative For Android 框架啟動核心路徑剖析的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!
新聞標(biāo)題:如何進(jìn)行ReactNativeForAndroid框架啟動核心路徑剖析
本文地址:http://jinyejixie.com/article24/pgggce.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供企業(yè)建站、商城網(wǎng)站、定制開發(fā)、網(wǎng)站設(shè)計公司、搜索引擎優(yōu)化、網(wǎng)站制作
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)