本文章向大家介绍ReactNative 详解(四) 源码之RN启动过程,主要包括ReactNative 详解(四) 源码之RN启动过程使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
本章主要来探讨一下,RN 的启动过程都做了什么?同时简单的介绍下在 Android 中是如何实现 ReactNative 的。进而引出解决一个重要的问题,ReactNative 的预加载。
ReactNative 系统框架概述
ReactNative 源码结构图如下:
其中几个主要内容:
- Libraries:JS层的实现,实现了JS组件的封装与JS队列的封装
- ReactAndroid:Android 源码实现
- ReactCommon:C++ 层实现,实现了对脚本引擎JSC的封装与通信桥ReactBridge,Android与iOS调用
- React:ReactNative源码的主要内容
ReactNative 主要工作就两部分:
第一部分实现:ReactNative 应用启动流程;ReactNative应用UI的绘制与渲染;ReactNative应用通信机制;ReactNative应用线程模型
第二部分:ReactNative运行时的异常以及异常的捕获与处理;SOLoader加载动态链接库;ReactNative触摸事件处理机制。
我们先从一个 Demo 工程来看 ReactNative 启动流程
启动流程
应用初始化
首先,我们打开这个普通工程的 android
目录,这里就是一个完整的 android 项目。
- 首先我们看
MainApplication.java
里面的 RN 的初始化操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public class extends Application implements ReactApplication { private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { public boolean getUseDeveloperSupport() { return BuildConfig.DEBUG; } protected List<ReactPackage> getPackages() { return Arrays.<ReactPackage>asList( new MainReactPackage() ); } protected String getJSMainModuleName() { return "index"; } }; public ReactNativeHost getReactNativeHost() { return mReactNativeHost; } public void onCreate() { super.onCreate(); SoLoader.init(this, false); } }
|
ReactApplication
可以看到我们在Application里实现了 ReactApplication接口,该接口要求创建一个ReactNativeHost对象。ReactNativeHost 对象,本身持有 ReactInstanceManager
对象。其对外暴露两个需要实现的方法:
1 2 3 4 5 6 7
| public abstract boolean getUseDeveloperSupport(); protected abstract List<ReactPackage> getPackages();
|
ReactNativeHost主要的工作就是创建 ReactInstanceManager,创建部分代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| public abstract class ReactNativeHost { protected ReactInstanceManager createReactInstanceManager() { ReactInstanceManagerBuilder builder = ReactInstanceManager.builder() .setApplication(mApplication) .setJSMainModulePath(getJSMainModuleName()) .setUseDeveloperSupport(getUseDeveloperSupport()) .setRedBoxHandler(getRedBoxHandler()) .setJavaScriptExecutorFactory(getJavaScriptExecutorFactory()) .setUIImplementationProvider(getUIImplementationProvider()) .setJSIModulesPackage(getJSIModulePackage()) .setInitialLifecycleState(LifecycleState.BEFORE_CREATE); for (ReactPackage reactPackage : getPackages()) { builder.addPackage(reactPackage); } String jsBundleFile = getJSBundleFile(); if (jsBundleFile != null) { builder.setJSBundleFile(jsBundleFile); } else { builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName())); } ReactInstanceManager reactInstanceManager = builder.build(); ReactMarker.logMarker(ReactMarkerConstants.BUILD_REACT_INSTANCE_MANAGER_END); return reactInstanceManager; } }
|
- 接下来看 MainActivity.java
1 2 3 4 5 6 7
| public class MainActivity extends ReactActivity { @Override protected String getMainComponentName() { return "TestDemo"; } }
|
这里我们的 MainActivity 继承自 ReactActivity
,ReactActivity作为JS页面的容器。最后我们的前端界面的内容,就是渲染到了这个容器上面。(ReactActivity 后面介绍)
应用启动流程
我们开始分析 ReactActivity
。ReactAcivity
本身继承自 Activity,并实现了其生命周期。本质上其什么也没有做,都是委托给了 ReactActivityDelegate
。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public abstract class ReactActivity extends Activity implements DefaultHardwareBackBtnHandler, PermissionAwareActivity { private final ReactActivityDelegate mDelegate; protected ReactActivity() { mDelegate = createReactActivityDelegate(); } protected @Nullable String getMainComponentName() { return null; } protected ReactActivityDelegate createReactActivityDelegate() { return new ReactActivityDelegate(this, getMainComponentName()); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mDelegate.onCreate(savedInstanceState); } }
|
其本身所有行为都交给了 ReactActivityDelegate
来处理,我们只需要关心 ReactActivityDelegate
即可。
ReactActivityDelegate
我们先来看,ReactActivity 的 onCreate
方法,本质上映射到了 ReactActivityDelegate
的 onCreate。
ReactActivityDelegate.onCreate(Bundle savedInstanceState)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public class ReactActivityDelegate { public ReactActivityDelegate(Activity activity, @Nullable String mainComponentName) { mActivity = activity; mMainComponentName = mainComponentName; mFragmentActivity = null; } protected void onCreate(Bundle savedInstanceState) { if (mMainComponentName != null) { loadApp(mMainComponentName); } } protected void loadApp(String appKey) { if (mReactRootView != null) { throw new IllegalStateException("Cannot loadApp while app is already running."); } mReactRootView = createRootView(); mReactRootView.startReactApplication( getReactNativeHost().getReactInstanceManager(), appKey, getLaunchOptions()); getPlainActivity().setContentView(mReactRootView); } }
|
创建阶段主要了做了如下几个事情:
- 创建ReactRootView作为应用的容器,它本质上是一个FrameLayout。
- 调用ReactRootView.startReactApplication()进一步执行应用启动流程。
- 调用Activity.setContentView() 将创建的 ReactRootView 作为 ReactActivity的content view。
所以呢,RN 其实被渲染到了一个 ReactRootView
上面,他可以被用在 Android 任何地方(比如 RN 视图和 原生视图组合)。接下来我们看启动过程 startReactApplication
。
ReactRootView.startReactApplication(ReactInstanceManager reactInstanceManager, String moduleName, @Nullable Bundle launchOptions)
简要代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public class ReactRootView extends SizeMonitoringFrameLayout implements RootView, MeasureSpecProvide { public void startReactApplication( ReactInstanceManager reactInstanceManager, String moduleName, @Nullable Bundle initialProperties, @Nullable String initialUITemplate) { try { UiThreadUtil.assertOnUiThread(); mReactInstanceManager = reactInstanceManager; mJSModuleName = moduleName; mAppProperties = initialProperties; mInitialUITemplate = initialUITemplate; if (!mReactInstanceManager.hasStartedCreatingInitialContext()) { mReactInstanceManager.createReactContextInBackground(); } attachToReactInstanceManager(); } finally { Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); } } }
|
参数信息:
- ReactInstanceManager reactInstanceManager:ReactInstanceManager 实例
- String moduleName:模块的名字,对应ReactActivity.getMainComponentName()与AppRegistry.registerComponent()
- Bundle launchOptions:Bundle 类型,可以在 startActivity 时候传递参数到 JS 层
(UiThreadUtil 主要包装了两个方法:UiThreadUtil.isOnUiThread(),UiThreadUtil.runOnUiThread(Runnable runnable))
主要还是调用了 ReactInstanceManager 上的 createReactContextInBackground
方法。
ReactInstanceManager.createReactContextInBackground()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| public class ReactInstanceManager { @ThreadConfined(UI) public void createReactContextInBackground() { Log.d(ReactConstants.TAG, "ReactInstanceManager.createReactContextInBackground()"); Assertions.assertCondition( !mHasStartedCreatingInitialContext, "createReactContextInBackground should only be called when creating the react " + "application for the first time. When reloading JS, e.g. from a new file, explicitly" + "use recreateReactContextInBackground"); mHasStartedCreatingInitialContext = true; recreateReactContextInBackgroundInner(); } @ThreadConfined(UI) private void recreateReactContextInBackgroundInner() { UiThreadUtil.assertOnUiThread(); if (mUseDeveloperSupport && mJSMainModulePath != null) { } recreateReactContextInBackgroundFromBundleLoader(); } @ThreadConfined(UI) private void recreateReactContextInBackgroundFromBundleLoader() { recreateReactContextInBackground(mJavaScriptExecutorFactory, mBundleLoader); } @ThreadConfined(UI) private void recreateReactContextInBackground( JavaScriptExecutorFactory jsExecutorFactory, JSBundleLoader jsBundleLoader) { UiThreadUtil.assertOnUiThread(); final ReactContextInitParams initParams = new ReactContextInitParams( jsExecutorFactory, jsBundleLoader); if (mCreateReactContextThread == null) { runCreateReactContextOnNewThread(initParams); } else { mPendingReactContextInitParams = initParams; } } public ReactContextInitParams( JavaScriptExecutorFactory jsExecutorFactory, JSBundleLoader jsBundleLoader) { mJsExecutorFactory = Assertions.assertNotNull(jsExecutorFactory); mJsBundleLoader = Assertions.assertNotNull(jsBundleLoader); } }
|
这里创建过程从上到下执行,最后调用到 runCreateReactContextOnNewThread
。该方法实际上就是新启了一个线程,来执行如下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| final ReactApplicationContext reactApplicationContext = createReactContext( initParams.getJsExecutorFactory().create(), initParams.getJsBundleLoader()); Runnable setupReactContextRunnable = new Runnable() { @Override public void run() { try { setupReactContext(reactApplicationContext); } catch (Exception e) { mDevSupportManager.handleException(e); } } }; reactApplicationContext.runOnNativeModulesQueueThread(setupReactContextRunnable); ``` 完成 ReactApplicationContext 的创建。我们看传入的两个参数: * JsExecutorFactory jsExecutor:当该类被加载时,它会自动去加载"reactnativejnifb.so"库,并会调用 Native 方 法 initHybrid() 初始化 C++层 RN 与 JSC通信的框架。 * JSBundleLoader jsBundleLoader:缓存了JSBundle的信息,封装了上层加载JSBundle的相关接口,CatalystInstance通过其间接调用ReactBridge 去加载J S文件,不同的场景会创建不同的加载器,具体可以查看类JSBundleLoader。 ##### ReactInstanceManager.createReactContext( JavaScriptExecutor jsExecutor, JSBundleLoader jsBundleLoader) 简要代码如下 ```java public class ReactInstanceManager { private ReactApplicationContext createReactContext( JavaScriptExecutor jsExecutor, JSBundleLoader jsBundleLoader) { final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext); NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null ? mNativeModuleCallExceptionHandler : mDevSupportManager; reactContext.setNativeModuleCallExceptionHandler(exceptionHandler); NativeModuleRegistry nativeModuleRegistry = processPackages(reactContext, mPackages, false); CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder() .setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault()) .setJSExecutor(jsExecutor) .setRegistry(nativeModuleRegistry) .setJSBundleLoader(jsBundleLoader) .setNativeModuleCallExceptionHandler(exceptionHandler); final CatalystInstance catalystInstance; try { catalystInstance = catalystInstanceBuilder.build(); } finally { Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_END); } if (mJSIModulePackage != null) { catalystInstance.addJSIModules(mJSIModulePackage .getJSIModules(reactContext, catalystInstance.getJavaScriptContextHolder())); } if (mBridgeIdleDebugListener != null) { catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener); } catalystInstance.runJSBundle(); reactContext.initializeWithInstance(catalystInstance); return reactContext; } }
|
- 主要创建 JavaModule 表,交给 CatalystInstance 管理
- 处理ReactPackage,将JavaModule与JavaScriptModule放进各自对应的注册表里。
- 通过上面jsExecutor、nativeModuleRegistry、jsModulesRegistry、jsBundleLoader、exceptionHandler等参数创建CatalystInstance实例。
- 关联 ReactContext 与 CatalystInstance,并将JS Bundle加载进来,等待ReactContextInitAsyncTask结束以后调用JS入口渲染页面。
- 最后调用 CatalystInstance.runJSBundle()去加载 JS Bundle
最终由 C++ 中的JSCExecutor.cpp 完成了 JS Bundle 的加载。
这里起到作用的就是 CatalystInstance
他由 CatalystInstanceImpl 构造而成。查看代码:
CatalystInstanceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| public class CatalystInstanceImpl implements CatalystInstance { private final HybridData mHybridData; private native static HybridData initHybrid(); private native void initializeBridge( ReactCallback callback, JavaScriptExecutor jsExecutor, MessageQueueThread jsQueue, MessageQueueThread moduleQueue, Collection<JavaModuleWrapper> javaModules, Collection<ModuleHolder> cxxModules); private CatalystInstanceImpl( final ReactQueueConfigurationSpec reactQueueConfigurationSpec, final JavaScriptExecutor jsExecutor, final NativeModuleRegistry nativeModuleRegistry, final JSBundleLoader jsBundleLoader, NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) { mHybridData = initHybrid(); mReactQueueConfiguration = ReactQueueConfigurationImpl.create( reactQueueConfigurationSpec, new NativeExceptionHandler()); mBridgeIdleListeners = new CopyOnWriteArrayList<>(); mNativeModuleRegistry = nativeModuleRegistry; mJSModuleRegistry = new JavaScriptModuleRegistry(); mJSBundleLoader = jsBundleLoader; mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler; mNativeModulesQueueThread = mReactQueueConfiguration.getNativeModulesQueueThread(); mTraceListener = new JSProfilerTraceListener(this); initializeBridge( new BridgeCallback(this), jsExecutor, mReactQueueConfiguration.getJSQueueThread(), mNativeModulesQueueThread, mNativeModuleRegistry.getJavaModules(this), mNativeModuleRegistry.getCxxModules()); mJavaScriptContextHolder = new JavaScriptContextHolder(getJavaScriptContext()); } }
|
- ReactCallback callback:CatalystInstanceImpl的静态内部类ReactCallback,负责接口回调。
- JavaScriptExecutor jsExecutor:JS执行器,将JS的调用传递给C++层。
- MessageQueueThread jsQueue.getJSQueueThread():JS线程,通过mReactQueueConfiguration.getJSQueueThread()获得,
- Collection javaModules:java modules,来源于mJavaRegistry.getJavaModules(this)。
- Collection cxxModules):c++ modules,来源于mJavaRegistry.getCxxModules()。
CatalystInstanceImpl 创建好,调用 runJSBundle 来加载js
CatalystInstanceImpl.runJSBundle()
这个代码最后会调用,初始化创建 ReactInstanceManager -> createReactContext 传入的 JSBundleLoader bundleLoader
上的
1 2
| mJSBundleLoader.loadScript(CatalystInstanceImpl.this);
|
bundleLoader 由 ReactInstanceManagerBuilder.setJSBundleFile(String jsBundleFile)
创建而来
1 2 3 4 5 6 7 8 9 10 11 12 13
| public ReactInstanceManagerBuilder setJSBundleFile(String jsBundleFile) { if (jsBundleFile.startsWith("assets://")) { mJSBundleAssetUrl = jsBundleFile; mJSBundleLoader = null; return this; } return setJSBundleLoader(JSBundleLoader.createFileLoader(jsBundleFile)); } public ReactInstanceManagerBuilder setJSBundleLoader(JSBundleLoader jsBundleLoader) { mJSBundleLoader = jsBundleLoader; mJSBundleAssetUrl = null; return this; }
|
JSBundleLoader 提供了多种加载方案,通过 ReactInstanceManagerBuilder.setJSBundleFile(String jsBundleFile)
调用的加载器如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public abstract class JSBundleLoader { public static JSBundleLoader createFileLoader(final String fileName) { return createFileLoader(fileName, fileName, false); } public static JSBundleLoader createFileLoader( final String fileName, final String assetUrl, final boolean loadSynchronously) { return new JSBundleLoader() { @Override public String loadScript(CatalystInstanceImpl instance) { instance.loadScriptFromFile(fileName, assetUrl, loadSynchronously); return fileName; } }; } public abstract String loadScript(CatalystInstanceImpl instance); }
|
可以看到,在这种加载器下,最后调用的是 CatalystInstanceImpl
上的 loadScriptFromFile
1 2 3 4 5 6 7 8
| public class CatalystInstanceImpl implements CatalystInstance { private native void jniLoadScriptFromFile(String fileName, String sourceURL, boolean loadSynchronously); void loadScriptFromFile(String fileName, String sourceURL, boolean loadSynchronously) { mSourceURL = sourceURL; jniLoadScriptFromFile(fileName, sourceURL, loadSynchronously); } }
|
CatalystInstanceImpl.java 最终还是调用C++层的 CatalystInstanceImpl.cpp去加载JS Bundle。
接下来就是 C++
部分了,不太会了呢。
ReactInstanceManager.setupReactContext(final ReactApplicationContext reactContext)
当 createContext
调用完毕后,C++ 会回调了 setupReactContextRunnable
线程,该线程调用的就是 setupReactContext
方法。代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| public class ReactInstanceManager { private void setupReactContext(final ReactApplicationContext reactContext) { Log.d(ReactConstants.TAG, "ReactInstanceManager.setupReactContext()"); ReactMarker.logMarker(PRE_SETUP_REACT_CONTEXT_END); ReactMarker.logMarker(SETUP_REACT_CONTEXT_START); Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "setupReactContext"); synchronized (mAttachedRootViews) { synchronized (mReactContextLock) { mCurrentReactContext = Assertions.assertNotNull(reactContext); } CatalystInstance catalystInstance = Assertions.assertNotNull(reactContext.getCatalystInstance()); catalystInstance.initialize(); mDevSupportManager.onNewReactContextCreated(reactContext); mMemoryPressureRouter.addMemoryPressureListener(catalystInstance); moveReactContextToCurrentLifecycleState(); ReactMarker.logMarker(ATTACH_MEASURED_ROOT_VIEWS_START); for (ReactRootView rootView : mAttachedRootViews) { attachRootViewToInstance(rootView); } ReactMarker.logMarker(ATTACH_MEASURED_ROOT_VIEWS_END); } ReactInstanceEventListener[] listeners = new ReactInstanceEventListener[mReactInstanceEventListeners.size()]; final ReactInstanceEventListener[] finalListeners = mReactInstanceEventListeners.toArray(listeners); UiThreadUtil.runOnUiThread( new Runnable() { @Override public void run() { for (ReactInstanceEventListener listener : finalListeners) { listener.onReactContextInitialized(reactContext); } } }); Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); ReactMarker.logMarker(SETUP_REACT_CONTEXT_END); reactContext.runOnJSQueueThread( new Runnable() { @Override public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); ReactMarker.logMarker(CHANGE_THREAD_PRIORITY, "js_default"); } }); reactContext.runOnNativeModulesQueueThread( new Runnable() { @Override public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); } }); } }
|
ReactInstanceManager.attachRootViewToInstance
方法,重置 ReactRootView
内容,然后将ReactRootView作为根布局,作为根布局进行绘制。随后调用 rootView.setRootViewTag(rootTag);
设置内相关内容,调用 rootView.runApplication()
启动 js。
rootView.runApplication
里面就是包装了一些启动参数 launchOptions 与 模块名 jsAppModuleName。然后最终调用了
catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams)
方法:
检查下这个代码:
1 2 3 4 5 6 7 8 9
| public interface AppRegistry extends JavaScriptModule { void runApplication(String appKey, WritableMap appParameters); void unmountApplicationComponentAtRootTag(int rootNodeTag); void startHeadlessTask(int taskId, String taskKey, WritableMap data); }
|
根据注释我们知道这有可能就是js层暴露给java的接口方法。?? (AppRegister.js)
原文:大专栏 ReactNative 详解(四) 源码之RN启动过程
原文地址:https://www.cnblogs.com/petewell/p/11607258.html