ReactNative 启动应用(2)

news/2024/11/3 21:42:24/

ReactNative 启动应用

简述

本节我们来看一下ReactNative在Android上启动Activity的流程,ReactNative在Android上也是一个Apk,它的实现全部都在应用层,所以它肯定也是符合我们Android应用的启动流程的,UI页面的载体也是一个Activity,然后启动JS引擎来加载index.js,执行javascript的代码,然后javascript通过Fabric渲染器和TurboModule执行到java代码,调用到最终的Native实现,本节我们就来看一下启动的这个过程。

Application启动

1.1 MainApplication.init
ReactNative的能力被包装为Package,Package里面包含一些Module,每个Module是一个子能力,后续介绍TurboModule的时候会介绍包加载的流程。

class MainApplication : Application(), ReactApplication {// 主要就是构建了DefaultReactNativeHost和ReactHostoverride val reactNativeHost: ReactNativeHost =object : DefaultReactNativeHost(this) {override fun getPackages(): List<ReactPackage> =// 这里加载了所有包PackageList(this).packages.apply {// Packages that cannot be autolinked yet can be added manually here, for example:// add(MyReactNativePackage())}override fun getJSMainModuleName(): String = "index"override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUGoverride val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLEDoverride val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED}// 详见1.2override val reactHost: ReactHostget() = getDefaultReactHost(applicationContext, reactNativeHost)override fun onCreate() {super.onCreate()SoLoader.init(this, false)if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {load()}}
}

1.2 getDefaultReactHost
调用ReactNativeHost.toReactHost构造ReactHost。

@OptIn(UnstableReactNativeAPI::class)
@JvmStatic
public fun getDefaultReactHost(context: Context,reactNativeHost: ReactNativeHost,
): ReactHost {require(reactNativeHost is DefaultReactNativeHost) {"You can call getDefaultReactHost only with instances of DefaultReactNativeHost"}//详见1.3return reactNativeHost.toReactHost(context)
}

1.3 toReactHost
调用了getDefaultReactHost

@UnstableReactNativeAPI
internal fun toReactHost(context: Context): ReactHost =// 详见1.4DefaultReactHost.getDefaultReactHost(context,packages,jsMainModuleName,bundleAssetName ?: "index",isHermesEnabled ?: true,useDeveloperSupport,)

1.4 getDefaultReactHost
初始化了JS引擎,加载了js代码,还构建了DefaultReactHostDelegate,最终会把这些信息封装到一个ReactHostImpl中,ReactHost建立了React和Activity一些交互的接口。

@OptIn(UnstableReactNativeAPI::class)
@JvmStatic
public fun getDefaultReactHost(context: Context,packageList: List<ReactPackage>,jsMainModulePath: String = "index",jsBundleAssetPath: String = "index",isHermesEnabled: Boolean = true,useDevSupport: Boolean = ReactBuildConfig.DEBUG,cxxReactPackageProviders: List<(ReactContext) -> CxxReactPackage> = emptyList(),
): ReactHost {// 构建ReactHostImplif (reactHost == null) {// 创建jsBundleLoader,这个是用来执行JS代码的val jsBundleLoader =JSBundleLoader.createAssetLoader(context, "assets://$jsBundleAssetPath", true)// 创建JS引擎val jsRuntimeFactory = if (isHermesEnabled) HermesInstance() else JSCInstance()val defaultTmmDelegateBuilder = DefaultTurboModuleManagerDelegate.Builder()cxxReactPackageProviders.forEach { defaultTmmDelegateBuilder.addCxxReactPackage(it) }// 构建DefaultReactHostDelegateval defaultReactHostDelegate =DefaultReactHostDelegate(jsMainModulePath = jsMainModulePath,jsBundleLoader = jsBundleLoader,reactPackages = packageList,jsRuntimeFactory = jsRuntimeFactory,turboModuleManagerDelegateBuilder = defaultTmmDelegateBuilder)val componentFactory = ComponentFactory()DefaultComponentsRegistry.register(componentFactory)// 创建ReactHostImpl,详见1.5reactHost =ReactHostImpl(context,defaultReactHostDelegate,componentFactory,true /* allowPackagerServerAccess */,useDevSupport,).apply {jsEngineResolutionAlgorithm =if (isHermesEnabled) {JSEngineResolutionAlgorithm.HERMES} else {JSEngineResolutionAlgorithm.JSC}}}return reactHost as ReactHost
}

1.5 ReactHostImpl
构建了一个DevSupportManager。

public ReactHostImpl(Context context,ReactHostDelegate delegate,ComponentFactory componentFactory,Executor bgExecutor,Executor uiExecutor,boolean allowPackagerServerAccess,boolean useDevSupport) {mContext = context;mReactHostDelegate = delegate;mComponentFactory = componentFactory;mBGExecutor = bgExecutor;mUIExecutor = uiExecutor;mQueueThreadExceptionHandler = ReactHostImpl.this::handleHostException;mMemoryPressureRouter = new MemoryPressureRouter(context);mAllowPackagerServerAccess = allowPackagerServerAccess;mUseDevSupport = useDevSupport;// debug版本和release版本,分别构建不同的SupportManagerif (mUseDevSupport) {mDevSupportManager =new BridgelessDevSupportManager(ReactHostImpl.this, mContext, mReactHostDelegate.getJsMainModulePath());} else {mDevSupportManager = new ReleaseDevSupportManager();}
}

Application中主要是初始化了一些React的对象,主要就是一个ReactHostImpl,ReactHostImpl里面持有了JS代码和引擎,以及要加载的包信息等等。

Activity启动

2.1 ReactActivity.onCreate
调了ReactActivityDelegate的onCreate

protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 详见2.2mDelegate.onCreate(savedInstanceState);
}

2.2 ReactActivityDelegate.onCreate
构建ReactDelegate,会根据flag构建ReactDelegate传入不同的参数,我们跟新架构的路径。
ReactDelegate会持有ReactHost或者ReactNativeHost,新架构是前者,老架构师后者。

public void onCreate(Bundle savedInstanceState) {String mainComponentName = getMainComponentName();final Bundle launchOptions = composeLaunchOptions();if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && isWideColorGamutEnabled()) {mActivity.getWindow().setColorMode(ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT);}if (ReactFeatureFlags.enableBridgelessArchitecture) {// 详见2.3mReactDelegate =new ReactDelegate(getPlainActivity(), getReactHost(), mainComponentName, launchOptions);} else {mReactDelegate =new ReactDelegate(getPlainActivity(),getReactNativeHost(),mainComponentName,launchOptions,isFabricEnabled()) {@Overrideprotected ReactRootView createRootView() {ReactRootView rootView = ReactActivityDelegate.this.createRootView();if (rootView == null) {rootView = super.createRootView();}return rootView;}};}if (mainComponentName != null) {// 详见2.3loadApp(mainComponentName);}
}

2.3 ReactActivityDelegate.loadApp
构建根View,配置给Activity。

protected void loadApp(String appKey) {// 详见2.4mReactDelegate.loadApp(appKey);getPlainActivity().setContentView(mReactDelegate.getReactRootView());
}

2.4 ReactDelegate.loadApp
这里一样有两套逻辑,新架构一套老架构一套,我们还是看新架构的流程。
这里构建了一个ReactSurfaceImpl,ReactSurfaceImpl里面会持有一个ReactSurfaceView,ReactSurfaceView是一个FrameLayout的子类,作为根View。
然后调用了mReactSurface.start来初始化。

public void loadApp(String appKey) {// With Bridgeless enabled, create and start the surfaceif (ReactFeatureFlags.enableBridgelessArchitecture) {if (mReactSurface == null) {// 构建了一个ReactSurfaceImpl和ReactSurfaceView,ReactSurfaceImpl持有ReactSurfaceView  // ReactSurfaceView本质就是一个FrameLayoutmReactSurface = mReactHost.createSurface(mActivity, appKey, mLaunchOptions);mActivity.setContentView(mReactSurface.getView());}// 启动ReactSurfaceImpl,详见2.5mReactSurface.start();} else {if (mReactRootView != null) {throw new IllegalStateException("Cannot loadApp while app is already running.");}mReactRootView = createRootView();mReactRootView.startReactApplication(getReactNativeHost().getReactInstanceManager(), appKey, mLaunchOptions);}
}

2.5 ReactSurfaceImpl.start
这里的Task是ReactNative自己写的一套逻辑,封装了异步调用的能力,功能类似于RXJava,就不详细看了,只要知道这是干啥用的就可以。
大致就是在mBGExecutor线程池里面调用getOrCreateStartTask返回的Task,然后等任务完成后回调getResult。
逻辑在getOrCreateStartTask里。

public TaskInterface<Void> start() {// 详见2.6return Task.call(this::getOrCreateStartTask, mBGExecutor).continueWithTask(Task::getResult);
}

2.6 ReactSurfaceImpl.getOrCreateStartTask
主要就是通过waitThenCallGetOrCreateReactInstanceTask来构建ReactInstance的Task

private Task<Void> getOrCreateStartTask() {final String method = "getOrCreateStartTask()";if (mStartTask == null) {log(method, "Schedule");mStartTask =// 调用waitThenCallGetOrCreateReactInstanceTask来构建ReactInstance,首次就会构建,后续使用缓存,详见2.7waitThenCallGetOrCreateReactInstanceTask().continueWithTask((task) -> {if (task.isFaulted()) {mReactHostDelegate.handleInstanceException(task.getError());// 如果出现异常则调用DestoryTask。return getOrCreateDestroyTask("getOrCreateStartTask() failure: " + task.getError().getMessage(),task.getError()).continueWithTask(destroyTask -> Task.forError(task.getError())).makeVoid();}return task.makeVoid();},mBGExecutor);}return mStartTask;
}

2.7 ReactSurfaceImpl.waitThenCallGetOrCreateReactInstanceTaskWithRetries
直接调用了waitThenCallGetOrCreateReactInstanceTaskWithRetries重载函数

@ThreadConfined("ReactHost")
private Task<ReactInstance> waitThenCallGetOrCreateReactInstanceTask() {// 调用重载函数,传入参数为重试次数,详见2.8return waitThenCallGetOrCreateReactInstanceTaskWithRetries(0, 4);
}

2.8 ReactSurfaceImpl.waitThenCallGetOrCreateReactInstanceTaskWithRetries
这里最终调用了getOrCreateReactInstanceTask来创建 创建ReactInstance的Task,也就是说其实最终是通过getOrCreateReactInstanceTask返回的Task里面的任务来创建ReactTask,其他的代码都是负责处理辅助逻辑,如失败重试,失败销毁,切换线程执行等。

@ThreadConfined("ReactHost")
private Task<ReactInstance> waitThenCallGetOrCreateReactInstanceTaskWithRetries(int tryNum, int maxTries) {final String method = "waitThenCallGetOrCreateReactInstanceTaskWithRetries";if (mReloadTask != null) {log(method, "React Native is reloading. Return reload task.");return mReloadTask;}if (mDestroyTask != null) {// 如果失败后重试次数没到最大重试次数,还可以重试boolean shouldTryAgain = tryNum < maxTries;if (shouldTryAgain) {log(method,"React Native is tearing down."+ "Wait for teardown to finish, before trying again (try count = "+ tryNum+ ").");return mDestroyTask.onSuccessTask((task) -> waitThenCallGetOrCreateReactInstanceTaskWithRetries(tryNum + 1, maxTries),mBGExecutor);}raiseSoftException(method,"React Native is tearing down. Not wait for teardown to finish: reached max retries.");}// 调用getOrCreateReactInstanceTask来创建ReactInstance,详见2.9return getOrCreateReactInstanceTask();
}

2.9 ReactSurfaceImpl.getOrCreateReactInstanceTask
ReactInstance代表着React的一个实例,里面持有了FabricUIManager,TurboModuleManager等,且提供了loadJSBundle执行JS代码,是React在Java侧最重要的管理类了。
这里最终调用了instance.loadJSBundle来执行JS代码。

@ThreadConfined("ReactHost")
private Task<ReactInstance> getOrCreateReactInstanceTask() {final String method = "getOrCreateReactInstanceTask()";log(method);return mCreateReactInstanceTaskRef.getOrCreate(() -> {log(method, "Start");ReactMarker.logMarker(ReactMarkerConstants.REACT_BRIDGELESS_LOADING_START, BRIDGELESS_MARKER_INSTANCE_KEY);// 获取JsBundleLoder,最终是通过这个来执行JS代码的,这个JsBundleLoader就是我们在Application初始化ReactHost的时候创建的那个。return getJsBundleLoader().onSuccess(task -> {// 前面Task完成后会进onSuccess,task.getResult可以拿到前面方法的返回值,就是JsBundleLoaderfinal JSBundleLoader bundleLoader = task.getResult();final BridgelessReactContext reactContext = getOrCreateReactContext();final DevSupportManager devSupportManager = getDevSupportManager();reactContext.setJSExceptionHandler(devSupportManager);log(method, "Creating ReactInstance");// 构建ReactInstance,这里初始化了很多内容,比较重要的有FabricUIManager,TurboModuleManagerfinal ReactInstance instance =new ReactInstance(reactContext,mReactHostDelegate,mComponentFactory,devSupportManager,mQueueThreadExceptionHandler,mUseDevSupport,getOrCreateReactHostInspectorTarget());mReactInstance = instance;instance.initializeEagerTurboModules();MemoryPressureListener memoryPressureListener =createMemoryPressureListener(instance);mMemoryPressureListener = memoryPressureListener;mMemoryPressureRouter.addMemoryPressureListener(memoryPressureListener);log(method, "Loading JS Bundle");// 执行JS代码,详见2.10instance.loadJSBundle(bundleLoader);// ...return new Result();},mBGExecutor).onSuccess(task -> {// ...流转生命周期return reactInstance;},mUIExecutor);});
}

2.10 ReactInstance.loadJSBundle
这里逻辑挺绕的,这里会调用JSBundleLoader的loadScript,JSBundleLoader是在1.4通过createAssetLoader创建的,是一个匿名内部类,里面就是调用了JSBundleLoaderDelegate.loadScriptFromAssets,JSBundleLoaderDelegate是loadscript的一个入参,是这里的匿名内部类,随意就是执行了这里的loadScriptFromAssets。

public void loadJSBundle(JSBundleLoader bundleLoader) {// Load the JS bundleSystrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "ReactInstance.loadJSBundle");bundleLoader.loadScript(new JSBundleLoaderDelegate() {@Overridepublic void loadScriptFromFile(String fileName, String sourceURL, boolean loadSynchronously) {mBridgelessReactContext.setSourceURL(sourceURL);loadJSBundleFromFile(fileName, sourceURL);}@Overridepublic void loadSplitBundleFromFile(String fileName, String sourceURL) {loadJSBundleFromFile(fileName, sourceURL);}@Overridepublic void loadScriptFromAssets(//执行的是这,我们在编译后会将js打包到assets目录下,这里就是执行入口。AssetManager assetManager, String assetURL, boolean loadSynchronously) {mBridgelessReactContext.setSourceURL(assetURL);// 详见2.11loadJSBundleFromAssets(assetManager, assetURL);}@Overridepublic void setSourceURLs(String deviceURL, String remoteURL) {mBridgelessReactContext.setSourceURL(deviceURL);}});Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}public static JSBundleLoader createAssetLoader(final Context context, final String assetUrl, final boolean loadSynchronously) {return new JSBundleLoader() {@Overridepublic String loadScript(JSBundleLoaderDelegate delegate) {delegate.loadScriptFromAssets(context.getAssets(), assetUrl, loadSynchronously);return assetUrl;}};
}

2.11 ReactInstance.loadJSBundleFromAssets
这个是native方法,映射到C++层是JReactInstance::loadJSBundleFromAssets。

void JReactInstance::loadJSBundleFromAssets(jni::alias_ref<JAssetManager::javaobject> assetManager,const std::string& assetURL) {const int kAssetsLength = 9; // strlen("assets://");auto sourceURL = assetURL.substr(kAssetsLength);auto manager = extractAssetManager(assetManager);// 加载asset内的文件,将结果存在scriptauto script = loadScriptFromAssets(manager, sourceURL);// 最终调用的C++层的ReactInstance::loadScriptinstance_->loadScript(std::move(script), sourceURL);
}

2.12 ReactInstance::loadScript
这里调用了JSCRuntime.evaluateJavaScript,这里就到了JSI到逻辑了,JSI是使用JS引擎提供的api封装的轻量级框架,用于提供Ojbect-C和JS或者C++和JS通信,我们这里就不继续深入了,后面会有专门一节介绍JSI。
这里调用JSI接口执行JS代码,就到了index.js了,后续index.js代码是怎么最终通知Native构建View以及显示的逻辑我们在Fabric渲染器章节再来介绍。

void ReactInstance::loadScript(std::unique_ptr<const JSBigString> script,const std::string& sourceURL) {
auto buffer = std::make_shared<BigStringBuffer>(std::move(script));
std::string scriptName = simpleBasename(sourceURL);runtimeScheduler_->scheduleWork([this,scriptName,sourceURL,buffer = std::move(buffer),weakBufferedRuntimeExecuter = std::weak_ptr<BufferedRuntimeExecutor>(bufferedRuntimeExecutor_)](jsi::Runtime& runtime) {try {SystraceSection s("ReactInstance::loadScript");bool hasLogger(ReactMarker::logTaggedMarkerBridgelessImpl);if (hasLogger) {ReactMarker::logTaggedMarkerBridgeless(ReactMarker::RUN_JS_BUNDLE_START, scriptName.c_str());}// 调用JSCRuntime.evaluateJavaScript,这就到了JSI层了,我们这里暂时不继续看了,后面会有一节专门讲JSIruntime.evaluateJavaScript(buffer, sourceURL);if (hasLogger) {ReactMarker::logTaggedMarkerBridgeless(ReactMarker::RUN_JS_BUNDLE_STOP, scriptName.c_str());ReactMarker::logMarkerBridgeless(ReactMarker::INIT_REACT_RUNTIME_STOP);ReactMarker::logMarkerBridgeless(ReactMarker::APP_STARTUP_STOP);}if (auto strongBufferedRuntimeExecuter =weakBufferedRuntimeExecuter.lock()) {strongBufferedRuntimeExecuter->flush();}} catch (jsi::JSError& error) {jsErrorHandler_->handleFatalError(error);}});
}

小结

通过上面的介绍,我们知道了ReactNative的Android app是怎么启动并且最终执行到JS到index.js代码,中间我们跳过了一些逻辑,比如加载TruboModule包,且后面index.js执行后又是怎么通知到Native侧,让应用更新对应的View布局,以及JSI是怎么通过JS引擎提供的接口实现JS和C++层通信,这些我们会在后面的章节来介绍。


http://www.ppmy.cn/news/1543462.html

相关文章

appium文本输入的多种形式

目录 一、send_keys方法 二、press_keycode方法 三、subprocess方法直接通过adb命令输入 一、send_keys方法 这个是最常用的方法&#xff0c;不过通常使用时要使用聚焦&#xff0c;也就是先点击后等待&#xff1a; element wait.until(EC.presence_of_element_located((By…

Redis 淘汰策略 问题

前言 相关系列 《Redis & 目录》《Redis & 淘汰策略 & 源码》《Redis & 淘汰策略 & 总结》《Redis & 淘汰策略 & 问题》 参考文献 《Redis过期策略和淘汰策略》 在内存耗尽的情况访问Redis会发生什么&#xff1f; 如果是读指令会正常返回…

初识字节码文件--Java

1&#xff0c;问题&#xff1a;请问以下代码&#xff0c;每行创建了几个对象&#xff1f; public static void main(String[] args) {String a "你好";String b new String("你好");String c "你" "好";} 如何查看字节码 方法一…

Python中的`update`方法详解及示例

Python中的update方法详解及示例 1. update方法简介2. update方法的应用场景3. 代码示例示例代码代码解释运行结果 4. 总结 在Python编程中&#xff0c;update方法是一个非常实用的工具&#xff0c;尤其是在处理集合&#xff08;Set&#xff09;数据类型时。本文将详细介绍upda…

数据库的三范式是什么?

数据库的三范式&#xff08;3NF&#xff09;是数据库规范化过程中的三个不同层次&#xff0c;旨在减少数据冗余和提高数据完整性。以下是三个范式的详细说明&#xff1a; 1. **第一范式&#xff08;1NF&#xff09;**&#xff1a; - **原子性**&#xff1a;确保数据库表的每…

深入分析-MySQL中的死锁问题排查与解决(二)

深入分析&#xff1a;MySQL中的死锁问题排查与解决 在高并发的数据库环境中&#xff0c;死锁是一个不可避免的问题。它发生在两个或多个事务互相等待对方持有的资源&#xff0c;导致所有相关事务无法继续执行。本文将通过一个实际案例&#xff0c;详细分析死锁的成因、排查过程…

深入探讨 Go 语言的函数基本概念

在现代编程中&#xff0c;函数是构建可重用和模块化代码的基本单元。Go 语言以其简洁和高效的特性而受到广泛欢迎&#xff0c;而函数在其中扮演着至关重要的角色。本文将深入探讨 Go 语言中函数的基本概念&#xff0c;包括函数的定义、参数、返回值、作用域以及高阶函数等内容。…

python爬虫基础篇:文本操作和二进制存储

文本操作 读取方式r readw writea appendb btye 合并方式 text.txt文件写入 lll aaa hhh wywywywywywy 读取文件方式&#xff1a;open&#xff08;“文件名”&#xff0c;读取方式&#xff0c;编码方式&#xff09; # ("读取文件名字"&#xff0c;读取方式&#xff0…