啟動流程
我們新建一個RN的項目,在原生代碼中會生成 MainActivity 和 MainApplication 兩個 Java 類。顧名思義, MainAcitivity 就是我們的 Native 的入口了,我們先來看下 MainApplication 都做了哪些操作
public class MainApplication extends Application implements reactApplication { //ReactNativeHost:持有ReactInstanceManager實例,做一些初始化操作。 private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { @Override public boolean getUseDeveloperSupport() { return BuildConfig.DEBUG; } @Override protected List<ReactPackage> getPackages() { return Arrays.<ReactPackage>asList( new MainReactPackage() ); } }; @Override public ReactNativeHost getReactNativeHost() { return mReactNativeHost; } @Override public void onCreate() { super.onCreate(); //SoLoader:加載C++底層庫,准備解析JS。 SoLoader.init(this, /* native exopackage */ false); } } }
我們再來看下 MainActivity 的代碼
public class MainActivity extends ReactActivity { @Override protected String getMainComponentName() { return "demo"; } }
可以看到其實是繼承了 ReactActivity 類,只是重寫了 getMainComponentName 方法,有沒有看出來,其方法的返回值和我們在 JS 端的值是一樣的。如果不一致會怎么樣,你可以自己試一下。
ReactActivity
我們來看下 ReactActivity 的方法的 onCreate 方法
public abstract class ReactActivity extends Activity implements DefaultHardwareBackBtnHandler, PermissionAwareActivity { private final ReactActivityDelegate mDelegate; ...省略代碼 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mDelegate.onCreate(savedInstanceState); } }
ReactActivity 全權委托給 ReactActivityDelegate 來處理
ReactActivityDelegate
public class ReactActivityDelegate { protected void onCreate(Bundle savedInstanceState) { // 彈框權限判斷 boolean needsOverlayPermission = false; if (getReactNativeHost().getUseDeveloperSupport() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // Get permission to show redbox in dev builds. if (!Settings.canDrawOverlays(getContext())) { needsOverlayPermission = true; Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getContext().getPackageName())); FLog.w(Reactconstants.TAG, REDBOX_PERMISSION_MESSAGE); Toast.makeText(getContext(), REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show(); ((Activity) getContext()).startActivityForResult(serviceIntent, REQUEST_OVERLAY_PERMISSION_CODE); } } // 加載組建邏輯 mMainComponentName為getMainComponentName返回的值 if (mMainComponentName != null && !needsOverlayPermission) { loadApp(mMainComponentName); } // 雙擊判斷工具類 mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer(); } protected void loadApp(String appKey) { //空判斷 if (mReactRootView != null) { throw new IllegalStateException("Cannot loadApp while app is already running."); } // 創建 RN容器根視圖 mReactRootView = createRootView(); mReactRootView.startReactApplication( getReactNativeHost().getReactInstanceManager(), appKey, getLaunchOptions()); //將rootview添加入activity getPlainActivity().setContentView(mReactRootView); } }
loadApp 做了三件事:創建 RootView 、創建 ReactApplication 、創建 ReactInstanceManager
ReactRootView
ReactRootView是一個自定義的View,其父類是FrameLayout。因此,可以把RN看成是一個特殊的 “自定義View”。
我們來看下 startReactApplication 方法
public void startReactApplication( ReactInstanceManager reactInstanceManager, String moduleName, @Nullable Bundle initialProperties) { ...省略代碼 try { //在UI線程中進行 UiThreadUtil.assertOnUiThread(); Assertions.assertCondition( mReactInstanceManager == null, "This root view has already been attached to a catalyst instance manager"); // 賦值 mReactInstanceManager = reactInstanceManager; mJSModuleName = moduleName; mAppProperties = initialProperties; // 判斷ReactContext是否初始化,沒有就異步進行初始化 if (!mReactInstanceManager.hasStartedCreatingInitialContext()) { mReactInstanceManager.createReactContextInBackground(); } //寬高計算完成后添加布局監聽 attachToReactInstanceManager(); } finally { Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); } }
startReactApplication 中的三個參數
形參 | 描述 |
---|---|
reactInstanceManager | ReactInstanceManager 類型,創建和管理 CatalyInstance 的實例 |
moduleName | 就是之前的組件名 |
initialProperties | 是 Native 向JS傳遞的數據,以后可能由POJO代替,默認是 null,需要的話要重寫 createReactActivityDelegate ,並重寫其中 getLaunchOptions 方法 |
startReactApplication 中調用了 ReactInstanceManager 的 createReactContextInBackground 方法。
ReactInstanceManager
public void createReactContextInBackground() { //首次執行 mHasStartedCreatingInitialContext = true; recreateReactContextInBackgroundInner(); }
該方法只會在 application 中執行一次,JS重載時,會走 recreateReactContextInBackground, 這兩個方法最終都會調用 recreateReactContextInBackgroundInner 方法
@ThreadConfined(UI)
private void recreateReactContextInBackgroundInner() { // 確保在UI線程中執行 UiThreadUtil.assertOnUiThread(); if (mUseDeveloperSupport && mJSMainModuleName != null && !Systrace.isTracing(TRACE_TAG_REACT_APPS | TRACE_TAG_REACT_JSC_CALLS)) { // 調試模式,加載服務器bundle return; } // 加載本地bundle recreateReactContextInBackgroundFromBundleLoader(); } @ThreadConfined(UI) private void recreateReactContextInBackgroundFromBundleLoader() { recreateReactContextInBackground( new JSCJavaScriptExecutor.Factory(mJSCConfig.getConfigMap()), mBundleLoader); }
形參 | 描述 |
---|---|
jsExecutorFactory | C++和JS雙向通信的中轉站 |
jsBundleLoader | bundle 加載器,根據 ReactNativeHost 中的配置決定從哪里加載 bundle 文件 |
private void recreateReactContextInBackground( JavaScriptExecutor.Factory jsExecutorFactory, JSBundleLoader jsBundleLoader) { UiThreadUtil.assertOnUiThread(); //創建ReactContextInitParams對象 final ReactContextInitParams initParams = new ReactContextInitParams( jsExecutorFactory, jsBundleLoader); if (mCreateReactContextThread == null) { // 新增線程初始化ReactContext runCreateReactContextOnNewThread(initParams); } else { mPendingReactContextInitParams = initParams; } }
runCreateReactContextOnNewThread 中有一個核心方法 createReactContext 來創建 ReactContext
private ReactApplicationContext createReactContext( JavaScriptExecutor jsExecutor, JSBundleLoader jsBundleLoader) { // 包裝ApplicationContext final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext); //創建JavaModule注冊表Builder,用來創建JavaModule注冊表,JavaModule注冊表將所有的JavaModule注冊到CatalystInstance中。 NativeModuleRegistryBuilder nativeModuleRegistryBuilder = new NativeModuleRegistryBuilder( reactContext, this, mLazyNativeModulesEnabled); // 創建JavaScriptModule注冊表Builder JavaScriptModuleRegistry.Builder jsModulesBuilder = new JavaScriptModuleRegistry.Builder(); if (mUseDeveloperSupport) { // 調試模式下,將錯誤交給DevSupportManager處理 reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager); } ...省略代碼 try { //創建CoreModulesPackage,其中封裝了RN Framework核心功能,通信、調試等。 CoreModulesPackage coreModulesPackage = new CoreModulesPackage( this, mBackBtnHandler, mUIImplementationProvider, mLazyViewManagersEnabled); //把各自的Module添加到對應的注冊表中 processPackage(coreModulesPackage, nativeModuleRegistryBuilder, jsModulesBuilder); } finally { Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); } // 將我們Application中的ReactPackage循環處理,加入對應的注冊表中。 for (ReactPackage reactPackage : mPackages) { ...省略代碼 try { processPackage(reactPackage, nativeModuleRegistryBuilder, jsModulesBuilder); } finally { Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); } } ...省略代碼 //生成Java注冊表,將Java可調用的API暴露給JS NativeModuleRegistry nativeModuleRegistry; try { nativeModuleRegistry = nativeModuleRegistryBuilder.build(); } finally { Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_END); } NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null ? mNativeModuleCallExceptionHandler : mDevSupportManager; //構建CatalystInstanceImpl實例 CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder() .setReactQueueConfigurationSpec(mUseSeparateUIBackgroundThread ? ReactQueueConfigurationSpec.createWithSeparateUIBackgroundThread() : ReactQueueConfigurationSpec.createDefault()) //JS執行通信類 .setJSExecutor(jsExecutor) //Java模塊注冊表 .setRegistry(nativeModuleRegistry) // JS注冊表 .setJSModuleRegistry(jsModulesBuilder.build()) // Bundle加載工具類 .setJSBundleLoader(jsBundleLoader) // 異常處理器 .setNativeModuleCallExceptionHandler(exceptionHandler); // 省略代碼 final CatalystInstance catalystInstance; try { catalystInstance = catalystInstanceBuilder.build(); } finally { //省略代碼 } if (mBridgeIdleDebugListener != null) { catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener); } if (Systrace.isTracing(TRACE_TAG_REACT_APPS | TRACE_TAG_REACT_JSC_CALLS)) { //調用CatalystInstanceImpl的Native方法把Java Registry轉換為Json,再由C++層傳送到JS層。 catalystInstance.setGlobalvariable("__RCTProfileIsProfiling", "true"); } //關聯ReacContext與CatalystInstance reactContext.initializeWithInstance(catalystInstance); //通過CatalystInstance開始加載JS Bundle catalystInstance.runJSBundle(); return reactContext; }
這段代碼比較長,它主要做了這幾件事:
- 創建 JavaModule 注冊表和 JavaScriptModule 注冊表,交給 CatalystInstance 管理。
- 處理 ReactPackage ,將各自的 Module 放入對應的注冊表中。
- 通過上面的各個參數創建 CatalystInstance 實例。
CatalystInstance 關聯 ReactContext ,開始加載 JS Bundle
CatalystInstance
我們來看下 CatalystInstance 的實現類 CatalystInstanceImpl 的構造方法
private CatalystInstanceImpl( final ReactQueueConfigurationSpec reactQueueConfigurationSpec, final JavaScriptExecutor jsExecutor, final NativeModuleRegistry registry, final JavaScriptModuleRegistry jsModuleRegistry, final JSBundleLoader jsBundleLoader, NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) { //用來創建JNI相關方法,並返回mHybridData mHybridData = initHybrid(); // Android UI線程、JS線程、NativeMOdulesQueue線程 mReactQueueConfiguration = ReactQueueConfigurationImpl.create( reactQueueConfigurationSpec, new NativeExceptionHandler()); // 省略代碼 //調用 C++ 層代碼進行初始化Bridge initializeBridge( new BridgeCallback(this), jsExecutor, mReactQueueConfiguration.getJSQueueThread(), mNativeModulesQueueThread, mUIBackgroundQueueThread, mJavaregistry.getJavaModules(this), mJavaRegistry.getCxxModules()); }
private native void initializeBridge(
ReactCallback callback,
JavaScriptExecutor jsExecutor,
MessageQueueThread jsQueue,
MessageQueueThread moduleQueue,
MessageQueueThread uiBackgroundQueue,
Collection<JavaModuleWrapper> javaModules, Collection<ModuleHolder> cxxModules);
形參 | 描述 |
---|---|
ReactCallback | CatalystInstanceImpl的靜態內部類 ReactCallback ,負責接口回調 |
JavaScriptExecutor | JS執行器,將JS的調用傳給C++層 |
MessageQueueThread | JS 線程 |
MessageQueueThread moduleQueue | Java 線程 |
MessageQueueThread uiBackgroundQueue | UI 背景線程 |
javaModules | java module |
cxxModules | c++ module |
createReactContext 方法中用 catalystInstance.runJSBundle() 來加載 JS bundle
@Override public void runJSBundle() { ... mJSBundleLoader.loadScript(CatalystInstanceImpl.this); ... }
JSBundleLoader
CatalystInstanceImpl.runJSBundle() 會調用 JSBundleLoader 去加載 JS Bundle ,由於不同的情況可能會有不同的 JSBundleLoader ,我們假設其中一種
public abstract class JSBundleLoader { /** * This loader is recommended one for release version of your app. In that case local JS executor * should be used. JS bundle will be read from assets in native code to save on passing large * strings from java to native memory. */ public static JSBundleLoader createAssetLoader( final Context context, final String assetUrl, final boolean loadSynchronously) { return new JSBundleLoader() { @Override public String loadScript(CatalystInstanceImpl instance) { instance.loadScriptFromAssets(context.getAssets(), assetUrl, loadSynchronously); return assetUrl; } }; }
可以看到它會繼續調用 CatalystInstance 中的 loadScriptFromAssets 方法
public class CatalystInstanceImpl { /* package */ void loadScriptFromAssets(AssetManager assetManager, String assetURL) { mSourceURL = assetURL; jniLoadScriptFromAssets(assetManager, assetURL); } private native void jniLoadScriptFromAssets(AssetManager assetManager, String assetURL); }
最終呢,還是會調用 CatalystInstanceImpl.cpp 去加載 JS Bundle ,我們去 C++ 層看一下實現
我們先看下源碼的結構圖
CatalystInstanceImpl.cpp
在ReactAndroid的Jni中,我們看下相關代碼:
void CatalystInstanceImpl::jniLoadScriptFromAssets(
jni::alias_ref<JAssetManager::javaobject> assetManager,
const std::string& assetURL,
bool loadSynchronously) {
const int kAssetsLength = 9; // strlen("assets://"); // 獲取soure js Bundle的路徑名 auto sourceURL = assetURL.substr(kAssetsLength); // 獲取AssetManager auto manager = extractAssetManager(assetManager); // 讀取JS Bundle里的內容 auto script = loadScriptFromAssets(manager, sourceURL); // unbundle命令打包判斷 if (JniJSModulesUnbundle::isUnbundle(manager, sourceURL)) { instance_->loadUnbundle( folly::make_unique<JniJSModulesUnbundle>(manager, sourceURL), std::move(script), sourceURL, loadSynchronously); return; } else { //bundle命令打包走次流程,instance_是Instan.h中類的實例 instance_->loadScriptFromString(std::move(script), sourceURL, loadSynchronously); } }
Instance.cpp
void Instance::loadScriptFromString(std::unique_ptr<const JSBigString> string, std::string sourceURL, bool loadSynchronously) { SystraceSection s("reactbridge_xplat_loadScriptFromString", "sourceURL", sourceURL); if (loadSynchronously) { loadApplicationSync(nullptr, std::move(string), std::move(sourceURL)); } else { loadApplication(nullptr, std::move(string), std::move(sourceURL)); } } void Instance::loadApplicationSync( std::unique_ptr<JSModulesUnbundle> unbundle, std::unique_ptr<const JSBigString> string, std::string sourceURL) { std::unique_lock<std::mutex> lock(m_syncMutex); m_syncCV.wait(lock, [this] { return m_syncReady; }); SystraceSection s("reactbridge_xplat_loadApplicationSync", "sourceURL", sourceURL); //nativeToJsBridge_也是在Instance::initializeBridget()方法里初始化的,具體實現在NativeToJsBridge.cpp里。 nativeToJsBridge_->loadApplicationSync(std::move(unbundle), std::move(string), std::move(sourceURL)); }
資源搜索網站大全 http://www.szhdn.com
NativeToJsBridge.cpp
void NativeToJsBridge::loadApplication(
std::unique_ptr<JSModulesUnbundle> unbundle,
std::unique_ptr<const JSBigString> startupScript, std::string startupScriptSourceURL) { //獲取一個MessageQueueThread,探后在線程中執行一個Task。 runOnExecutorQueue( m_mainExecutorToken, [unbundleWrap=folly::makeMoveWrapper(std::move(unbundle)), startupScript=folly::makeMoveWrapper(std::move(startupScript)), startupScriptSourceURL=std::move(startupScriptSourceURL)] (JSExecutor* executor) mutable { auto unbundle = unbundleWrap.move(); if (unbundle) { executor->setJSModulesUnbundle(std::move(unbundle)); } //executor從runOnExecutorQueue()返回的map中取得,與OnLoad中的JSCJavaScriptExecutorHolder對應,也與 //Java中的JSCJavaScriptExecutor對應。它的實例在JSExecutor.cpp中實現。 executor->loadApplicationScript(std::move(*startupScript), std::move(startupScriptSourceURL)); }); }
unbundle命令,使用方式和bundle命令完全相同。unbundle命令是在bundle命令的基礎上增加了一項功能,除了生成整合JS文件index.android.bundle外,還會
生成各個單獨的未整合JS文件(但會被優化),全部放在js-modules目錄下,同時會生成一個名為UNBUNDLE的標識文件,一並放在其中。UNBUNDLE標識文件的前4個字節
固定為0xFB0BD1E5,用於加載前的校驗。
- 該函數進一步調用 JSExecutor.cpp 的 loadApplicationScript() 方法。
- 到了這個方法,就是去真正加載JS文件了。
JSCExecutor.cpp
void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> script, std::string sourceURL) { ... //使用Webkit JSC去解釋執行JS evaluateSourceCode(m_context, bcSourceCode, jsSourceURL); flush(); }
void JSCExecutor::flush() {
...
//綁定bridge,核心就是通過getGlobalObject()將JS與C++通過Webkit jSC實現綁定 bindBridge(); //返回給callNativeModules callNativeModules(m_flushedQueueJS->callAsFunction({})); ... }
void JSCExecutor::callNativeModules(Value&& value) {
...
//把JS層相關通信數據轉換為JSON格式 auto calls = value.toJSONString(); //m_delegate為JsToNativeBridge對象。 m_delegate->callNativeModules(*this, folly::parseJson(calls), true); ... }
- m_flushedQueueJS 支線的是 MessageQueue.js 的 flushedQueue() 方法,此時JS已經被加載到隊列中,等待Java層來驅動它。
- JS Bundle 加載並解析完成后,我們回到Java代碼中看看后續的流程
- 我們在之前的 runCreateReactContextOnNewThread 方法中,在 creatReactContext 之后還有一句核心的代碼
setupReactContext(reactApplicationContext);
這就是加載 JS Bundle 之后執行的代碼
public class ReactInstanceManager { private void setupReactContext(ReactApplicationContext reactContext) { ... // Native Java module初始化 catalystInstance.initialize(); //重置ReactContext mDevSupportManager.onNewReactContextCreated(reactContext); //內存狀態回調設置 mMemoryPressureRouter.addMemoryPressureListener(catalystInstance); // 復位生命周期 moveReactContextToCurrentLifecycleState(); ReactMarker.logMarker(ATTACH_MEASURED_ROOT_VIEWS_START); synchronized (mAttachedRootViews) { //mAttachedRootViews保存的是ReactRootView for (ReactRootView rootView : mAttachedRootViews) { attachRootViewToInstance(rootView, catalystInstance); } } ... } } private void attachMeasuredRootViewToInstance ( final ReactRootView rootView, CatalystInstance catalystInstance) { ... //將ReactRootView作為根布局 UIManagerModule uiManagerModule = catalystInstance.getNativeModule(UIManagerModule.class); int rootTag = uiManagerModule.addMeasuredRootView(rootView); //設置相關 rootView.setRootViewTag(rootTag); rootView.runApplication(); ... }
/* package */ void runApplication() { ... CatalystInstance catalystInstance = reactContext.getCatalystInstance(); WritableNativeMap appParams = new WritableNativeMap(); appParams.putDouble("rootTag", getRootViewTag()); @Nullable Bundle appProperties = getAppProperties(); if (appProperties != null) { appParams.putMap("initialProps", Arguments.fromBundle(appProperties)); } String jsAppModuleName = getJSModuleName(); //啟動流程入口:由Java層調用啟動 catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams); ... }
可以看到,最終調用的是 catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams) , AppRegistry.class 是JS層暴露給Java層的接口方法。它的真正實現在 AppRegistry.js 里, AppRegistry.js 是運行所有 RN 應用的 JS 層入口,我們來看看它的實現:在 Libraries/ReactNative 中的 AppRegistry.js
AppRegistry.js
runApplication(appKey: string, appParameters: any): void { const msg = 'Running application "' + appKey + '" with appParams: ' + JSON.stringify(appParameters) + '. ' + '__DEV__ === ' + String(__DEV__) + ', development-level warning are ' + (__DEV__ ? 'ON' : 'OFF') + ', performance optimizations are ' + (__DEV__ ? 'OFF' : 'ON'); infoLog(msg); BugReporting.addSource('AppRegistry.runApplication' + runCount++, () => msg); invariant( runnables[appKey] && runnables[appKey].run, 'Application ' + appKey + ' has not been registered.\n\n' + 'Hint: This error often happens when you\'re running the packager ' + '(local dev server) from a wrong folder. For example you have ' + 'multiple apps and the packager is still running for the app you ' + 'were working on before.\nIf this is the case, simply kill the old ' + 'packager instance (e.g. close the packager terminal window) ' + 'and start the packager in the correct app folder (e.g. cd into app ' + 'folder and run \'npm start\').\n\n' + 'This error can also happen due to a require() error during ' + 'initialization or failure to call AppRegistry.registerComponent.\n\n' ); SceneTracker.setActiveScene({name: appKey}); runnables[appKey].run(appParameters); }
到這里就會去調用JS進行渲染,在通過 UIManagerModule 將JS組件轉換成Android組件,最終顯示在 ReactRootView 上。
最后總結一下,就是先在應用終端啟動並創建上下文對象,啟動 JS Runtime ,進行布局,將JS端的代碼通過C++層, UIManagerMoodule 轉化成 Android 組件,再進行渲染,最后將渲染的View添加到 ReactRootView 上,最終呈現在用戶面前。