React Native新架构之Android端初始化源码分析
前言
注意,本文是基于React Native 0.83版本源码进行分析。文章基本属于保姆式分析,有详细的衔接流程,本专栏让你彻底理解新架构是怎么回事。
初始化流程
Application
根据Android原生初始化的顺序,我们应先从应用的Application开始。这里我们就打开工程源码提供的helloworld示例作为研究的开始:
源码react-native/private/helloworld/android/app/src/main/java/com/helloworld/MainApplication.kt
kotlin
class MainApplication : Application(), ReactApplication {
override val reactHost: ReactHost by
lazy(LazyThreadSafetyMode.NONE) {
getDefaultReactHost(
context = applicationContext,
packageList =
PackageList(this).packages.apply {
// 例如,目前还无法自动链接的软件包可以手动添加到这里:
// add(MyReactNativePackage())
},
)
}
override fun onCreate() {
super.onCreate()
loadReactNative(this)
}
}
一个ReactNative的Android原生工程,要求我们必须自定义一个派生自Application且实现了ReactApplication接口的子类。这里逻辑十分简单,我们先看一下ReactApplication接口有什么要求:
kotlin
/** 表示 React Native 应用程序实例的接口 */
public interface ReactApplication {
/** Get the default [ReactNativeHost] for this app. */
@Suppress("DEPRECATION")
@Deprecated(
"You should not use ReactNativeHost directly in the New Architecture. Use ReactHost instead.",
ReplaceWith("reactHost"),
)
public val reactNativeHost: ReactNativeHost
get() {
throw RuntimeException("You should not use ReactNativeHost directly in the New Architecture")
}
/**
* 获取此应用的默认的[ReactHost]。此方法将由 React Native 的新架构使用。
*/
public val reactHost: ReactHost?
get() = null
}
这里reactNativeHost是已经过时的旧架构需要的,我们主要研究新架构,忽略。也就是说ReactApplication接口的功能非常简单,只是需要实现类提供一个返回ReactHost实例的getter。
现在我们再来看MainApplication,其实非常简单,只做了两件事:
- 实现了一个返回
ReactHost实例的懒加载getter - 在
onCreate调用loadReactNative
这里getDefaultReactHost是一个静态方法,源码react-native/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactHost.kt
kotlin
/**
* 一个实用工具类,可简化开源新应用 [ReactHost] 的配置。
*
* [ReactHost] 是一个接口,负责处理 React Native 应用在无桥模式下的生命周期。
*/
public object DefaultReactHost {
private var reactHost: ReactHost? = null
/**
* 用于创建应用程序默认的 [ReactHost] 的实用函数。此方法由"New App"模板使用。
*
* @param context 用于创建 [ReactHost] 的 Android [Context]
* @param packageList 用于创建 [ReactHost] 的 [ReactPackage] 列表
* @param jsMainModulePath Metro 平台上应用程序主模块的路径。通常为 `index` or `index.<platform>`
* @param jsBundleAssetPath 相对于 assets 目录的 JS 包路径.
* 将以 `asset://...` URL 的形式呈现。通常为 `index.android.bundle`
*
* @param jsBundleFilePath 文件系统中 JS 包的路径.
* 将以 `file://...` URL 的形式呈现
*
* @param jsRuntimeFactory 用于执行 [ReactHost] 的 JS 引擎,默认为 Hermes
* @param useDevSupport 是否启用开发支持,默认为 ReactBuildConfig.DEBUG.
* @param cxxReactPackageProviders cxxreactpackage提供程序列表(用于注册 C++ Turbo 模块)
* @param exceptionHandler 宿主应用程序可用于响应 React Native 内部抛出的异常的回调函数
* @param bindingsInstaller 可用于安装绑定
*
* TODO(T186951312): Should this be @UnstableReactNativeAPI?
*/
@OptIn(UnstableReactNativeAPI::class)
@JvmStatic
public fun getDefaultReactHost(
context: Context,
packageList: List<ReactPackage>,
jsMainModulePath: String = "index",
jsBundleAssetPath: String = "index.android.bundle",
jsBundleFilePath: String? = null,
jsRuntimeFactory: JSRuntimeFactory? = null,
useDevSupport: Boolean = ReactBuildConfig.DEBUG,
cxxReactPackageProviders: List<(ReactContext) -> CxxReactPackage> = emptyList(),
exceptionHandler: (Exception) -> Unit = { throw it },
bindingsInstaller: BindingsInstaller? = null,
): ReactHost {
if (reactHost == null) {
val bundleLoader =
if (jsBundleFilePath != null) {
if (jsBundleFilePath.startsWith("assets://")) {
JSBundleLoader.createAssetLoader(context, jsBundleFilePath, true)
} else {
JSBundleLoader.createFileLoader(jsBundleFilePath)
}
} else {
JSBundleLoader.createAssetLoader(context, "assets://$jsBundleAssetPath", true)
}
val defaultTmmDelegateBuilder = DefaultTurboModuleManagerDelegate.Builder()
cxxReactPackageProviders.forEach { defaultTmmDelegateBuilder.addCxxReactPackage(it) }
val defaultReactHostDelegate =
DefaultReactHostDelegate(
jsMainModulePath = jsMainModulePath,
jsBundleLoader = bundleLoader,
reactPackages = packageList,
jsRuntimeFactory = jsRuntimeFactory ?: HermesInstance(),
bindingsInstaller = bindingsInstaller,
turboModuleManagerDelegateBuilder = defaultTmmDelegateBuilder,
exceptionHandler = exceptionHandler,
)
val componentFactory = ComponentFactory()
DefaultComponentsRegistry.register(componentFactory)
// TODO: T164788699 寻找访问 ReactHostImpl 以初始化 ReactHost 的替代方法
reactHost =
ReactHostImpl(
context,
defaultReactHostDelegate,
componentFactory,
true /* allowPackagerServerAccess */,
useDevSupport,
)
}
return reactHost as ReactHost
}
/**
* 用于 Brownfield 场景的清理函数,在销毁 React Native 实例后清除 ReactHost 的单例引用
*/
internal fun invalidate() {
reactHost = null
}
}
这里reactHost是一个静态单例引用,因此释放时需要调用invalidate方法。Brownfield 场景是指在现有应用中集成新技术,那么这里就是指原生 Android 应用中嵌入 RN 页面。
我们再来看loadReactNative方法,它的源码并不在工程中,而是在构建时自动生成,我们找到其代码模版react-native/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/GenerateEntryPointTask.kt
kotlin
companion object {
const val GENERATED_FILENAME = "com/facebook/react/ReactNativeApplicationEntryPoint.java"
// language=java
val generatedFileContentsTemplate =
"""
package com.facebook.react;
import android.app.Application;
import android.content.Context;
import android.content.res.Resources;
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
import com.facebook.react.common.annotations.internal.LegacyArchitectureLogger;
import com.facebook.react.views.view.WindowUtilKt;
import com.facebook.react.soloader.OpenSourceMergedSoMapping;
import com.facebook.soloader.SoLoader;
import java.io.IOException;
/**
* This class is the entry point for loading React Native using the configuration
* that the users specifies in their .gradle files.
*
* The `loadReactNative(this)` method invocation should be called inside the
* application onCreate otherwise the app won't load correctly.
*/
public class ReactNativeApplicationEntryPoint {
public static void loadReactNative(Context context) {
try {
SoLoader.init(context, OpenSourceMergedSoMapping.INSTANCE);
} catch (IOException error) {
throw new RuntimeException(error);
}
if ({{packageName}}.BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
DefaultNewArchitectureEntryPoint.load();
}
if ({{packageName}}.BuildConfig.IS_EDGE_TO_EDGE_ENABLED) {
WindowUtilKt.setEdgeToEdgeFeatureFlagOn();
}
}
}
"""
.trimIndent()
}
可以看到ReactNativeApplicationEntryPoint是加载 React Native 的入口点,它加载了一些用户在 .gradle文件中指定的配置。而loadReactNative(this) 方法必须在应用程序的 onCreate 方法中调用,否则应用程序将无法正确加载。其实此方法主要就是初始化了SoLoader,SoLoader是Facebook开源的一个工具库,主要用于安卓的动态库加载管理。后面再单独分析此库的源码。
小结
Application中主要做两件事:
- 调用
loadReactNative - 提供一个创建
ReactHost实例的getter
MainActivity
接下来我们来看react-native/private/helloworld/android/app/src/main/java/com/helloworld/MainActivity.kt
kotlin
class MainActivity : ReactActivity() {
/**
* 返回从 JavaScript 注册的主组件的名称。此名称用于安排组件的渲染。
*/
override fun getMainComponentName(): String = "HelloWorld"
/**
* 返回 [ReactActivityDelegate] 的实例。使用 [DefaultReactActivityDelegate],它允许您使用单个布尔标志 [fabricEnabled] 启用新架构。
*/
override fun createReactActivityDelegate(): ReactActivityDelegate =
DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)
}
这里主要是实现了ReactActivity中的两个方法,详细逻辑查看react-native/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java,这是一个Java类:
java
/** React Native 应用的基础Activity */
public abstract class ReactActivity extends AppCompatActivity
implements DefaultHardwareBackBtnHandler, PermissionAwareActivity {
private final ReactActivityDelegate mDelegate;
// ......
protected ReactActivity() {
mDelegate = createReactActivityDelegate();
}
/**
* Returns the name of the main component registered from JavaScript. This is used to schedule
* rendering of the component. e.g. "MoviesApp"
*/
protected @Nullable String getMainComponentName() {
return null;
}
/** Called at construction time, override if you have a custom delegate implementation. */
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegate(this, getMainComponentName());
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDelegate.onCreate(savedInstanceState);
if (AndroidVersion.isAtLeastTargetSdk36(this)) {
getOnBackPressedDispatcher().addCallback(this, mBackPressedCallback);
}
}
@Override
protected void onPause() {
super.onPause();
mDelegate.onPause();
}
@Override
protected void onResume() {
super.onResume();
mDelegate.onResume();
}
@Override
protected void onDestroy() {
super.onDestroy();
mDelegate.onDestroy();
}
public @Nullable ReactDelegate getReactDelegate() {
return mDelegate.getReactDelegate();
}
public ReactActivityDelegate getReactActivityDelegate() {
return mDelegate;
}
// ......
protected ReactHost getReactHost() {
return mDelegate.getReactHost();
}
protected final void loadApp(String appKey) {
mDelegate.loadApp(appKey);
}
}
可以看到,ReactActivity中主要是将一些回调代理给ReactActivityDelegate类,创建该类的createReactActivityDelegate方法可以由子类实现。这里正是在MainActivity中创建了ReactActivityDelegate实例。
接下来查看react-native/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactActivityDelegate.kt
kotlin
/**
* 一个实用工具类,可简化开源应用中 [ReactActivityDelegate] 的设置
* 具体来说,使用此类,您可以通过构造函数中的布尔标志轻松控制是否为 Activity 启用 Fabric
*
* @param fabricEnabled 是否应为此 Activity 的 RootView 启用 Fabric
*/
public open class DefaultReactActivityDelegate(
activity: ReactActivity,
mainComponentName: String,
private val fabricEnabled: Boolean = false,
) : ReactActivityDelegate(activity, mainComponentName) {
@Deprecated(
message =
"Creating DefaultReactActivityDelegate with both fabricEnabled and " +
"concurrentReactEnabled is deprecated. Please pass only one boolean value that will" +
" be used for both flags",
level = DeprecationLevel.WARNING,
replaceWith =
ReplaceWith("DefaultReactActivityDelegate(activity, mainComponentName, fabricEnabled)"),
)
public constructor(
activity: ReactActivity,
mainComponentName: String,
fabricEnabled: Boolean,
@Suppress("UNUSED_PARAMETER") concurrentReactEnabled: Boolean,
) : this(activity, mainComponentName, fabricEnabled)
override fun isFabricEnabled(): Boolean = fabricEnabled
}
该类并没是有那些代理方法的实现,因此继续研究父类react-native/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java
小结
MainActivity 中主要做一件事,重写createReactActivityDelegate方法创建ReactActivityDelegate实例。
ReactActivityDelegate
java
/**
* {@link ReactActivity} 的代理类。您可以继承此类以提供自定义实现
* 例如,如果您的 Application 类没有实现 {@link ReactApplication},则可以调用 {@link #getReactNativeHost()}
*/
@Nullsafe(Nullsafe.Mode.LOCAL)
public class ReactActivityDelegate {
private final @Nullable Activity mActivity;
private final @Nullable String mMainComponentName;
private @Nullable PermissionListener mPermissionListener;
private @Nullable Callback mPermissionsCallback;
private @Nullable ReactDelegate mReactDelegate;
/**
* 尽量使用 ReactActivity,因为它默认会调用所有 Activity 生命周期方法。
* 它还实现了 ReactDelegate 所需的 DefaultHardwareBackBtnHandler 接口。
*/
@Deprecated
public ReactActivityDelegate(@Nullable Activity activity, @Nullable String mainComponentName) {
mActivity = activity;
mMainComponentName = mainComponentName;
}
public ReactActivityDelegate(
@Nullable ReactActivity activity, @Nullable String mainComponentName) {
mActivity = activity;
mMainComponentName = mainComponentName;
}
/**
* 用于填充启动选项的公共 API,这些选项将作为"initialProperties"传递给渲染器。
*
* @return 返回 null 或键值对映射(以 Bundle 形式)
*/
protected @Nullable Bundle getLaunchOptions() {
return null;
}
protected @Nullable Bundle composeLaunchOptions() {
return getLaunchOptions();
}
/**
* 获取启用 Bridgeless 后此应用使用的 {@link ReactHost}
* 默认情况下,此方法假定 {@link Activity#getApplication()} 是 {@link ReactApplication} 的实例,
* 并调用 {@link ReactApplication#getReactHost()}。
* 如果您的应用类未实现 {@code ReactApplication} 接口,
* 或者您使用其他机制存储 {@code ReactHost}(例如,将其作为静态字段存储),请重写此方法。
*/
public @Nullable ReactHost getReactHost() {
return ((ReactApplication) getPlainActivity().getApplication()).getReactHost();
}
protected @Nullable ReactDelegate getReactDelegate() {
return mReactDelegate;
}
@Nullable
public String getMainComponentName() {
return mMainComponentName;
}
public void onCreate(@Nullable Bundle savedInstanceState) {
Systrace.traceSection(
Systrace.TRACE_TAG_REACT,
"ReactActivityDelegate.onCreate::init",
() -> {
String mainComponentName = getMainComponentName();
final Bundle launchOptions = composeLaunchOptions();
if (mActivity != null) {
Window window = mActivity.getWindow();
if (window != null) {
if (WindowUtilKt.isEdgeToEdgeFeatureFlagOn()) {
WindowUtilKt.enableEdgeToEdge(window);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && isWideColorGamutEnabled()) {
window.setColorMode(ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT);
}
}
}
if (ReactNativeNewArchitectureFeatureFlags.enableBridgelessArchitecture()) {
mReactDelegate =
new ReactDelegate(
getPlainActivity(), getReactHost(), mainComponentName, launchOptions);
} else {
// 省略......
}
if (mainComponentName != null) {
loadApp(mainComponentName);
}
});
}
protected void loadApp(@Nullable String appKey) {
Objects.requireNonNull(mReactDelegate).loadApp(Objects.requireNonNull(appKey));
getPlainActivity().setContentView(mReactDelegate.getReactRootView());
}
public void setReactSurface(ReactSurface reactSurface) {
Objects.requireNonNull(mReactDelegate).setReactSurface(reactSurface);
}
public void setReactRootView(ReactRootView reactRootView) {
Objects.requireNonNull(mReactDelegate).setReactRootView(reactRootView);
}
protected Context getContext() {
return Assertions.assertNotNull(mActivity);
}
protected Activity getPlainActivity() {
return ((Activity) getContext());
}
protected ReactActivity getReactActivity() {
return ((ReactActivity) getContext());
}
/**
* 从 ReactHost 或 ReactInstanceManager 获取当前的 {@link ReactContext}
*
* <p>不要存储对此的引用,如果 React 实例重新加载或销毁,此上下文将不再有效。</p>
*/
public @Nullable ReactContext getCurrentReactContext() {
return Objects.requireNonNull(mReactDelegate).getCurrentReactContext();
}
// 省略部分方法......
}
可以看到,在onCreate方法的实现中,新架构的核心逻辑就是创建了ReactDelegate实例对象,并调用了loadApp方法。
loadApp主要做了两件事,首先调用ReactDelegate类的loadApp方法,继续跟踪源码react-native/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactDelegate.kt
kotlin
/**
* 为指定的appKey启动 React Surface
*
* @param appKey 要加载到 Surface 中的应用app ID
*/
public fun loadApp(appKey: String) {
// 启用 Bridgeless 功能后,创建并启动surface
if (ReactNativeNewArchitectureFeatureFlags.enableBridgelessArchitecture()) {
val reactHost = reactHost
if (reactSurface == null && reactHost != null) {
reactSurface = reactHost.createSurface(activity, appKey, launchOptions)
}
reactSurface?.start()
} else {
// 省略旧架构......
}
}
这里新架构的核心逻辑是创建ReactSurface对象并调用start方法。我们后面再研究ReactSurface源码,先看一下ReactActivityDelegate的loadApp做的第二件事,就是设置Activity的ContentView:getPlainActivity().setContentView(mReactDelegate.getReactRootView())。
kotlin
// ReactDelegate.kt
public var reactRootView: ReactRootView?
get() {
return if (ReactNativeNewArchitectureFeatureFlags.enableBridgelessArchitecture()) {
if (reactSurface != null) {
reactSurface?.view as ReactRootView?
} else {
null
}
} else {
internalReactRootView
}
}
set(reactRootView) {
internalReactRootView = reactRootView
}
可以看到,新架构实际上就是返回了reactSurface中的view作为ReactRootView。现在调用流程都指向了ReactSurface,继续查看reactHost.createSurface方法的实现来找到ReactSurface的具体实现类,可以看到实现类是ReactSurfaceImpl:
kotlin
override fun createSurface(
context: Context,
moduleName: String,
initialProps: Bundle?,
): ReactSurface {
val surface = ReactSurfaceImpl(context, moduleName, initialProps)
val surfaceView = ReactSurfaceView(context, surface)
surfaceView.setShouldLogContentAppeared(true)
surface.attachView(surfaceView)
surface.attach(this)
return surface
}
小结
ReactActivityDelegate 中主要做了两件事:
-
调用
ReactDelegate的loadApp方法 -
设置Activity 的ContentView
ReactHostImpl
继续跟踪react-native/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactSurfaceImpl.kt
kotlin
override fun start(): TaskInterface<Void> {
if (surfaceViewRef.get() == null) {
return Task.forError(
IllegalStateException("Trying to call ReactSurface.start(), but view is not created.")
)
}
val host =
reactHost
?: return Task.forError(
IllegalStateException(
"Trying to call ReactSurface.start(), but no ReactHost is attached."
)
)
return host.startSurface(this)
}
实际实现是调用了reactHost的startSurface方法,并将当前ReactSurfaceImpl实例作为参数传入:
kotlin
// ReactHostImpl.kt
/**
* 开始在屏幕上渲染 React Native Surface
*
* @param surface 要渲染的 ReactSurface
* @return A Task that will complete when startSurface has been called.
*/
internal fun startSurface(surface: ReactSurfaceImpl): TaskInterface<Void> {
val method = "startSurface(surfaceId = ${surface.surfaceID})"
stateTracker.enterState(method, "Schedule")
attachSurface(surface)
return callAfterGetOrCreateReactInstance(method, bgExecutor) { reactInstance: ReactInstance ->
stateTracker.enterState(method, "Execute")
reactInstance.startSurface(surface)
}
}
可以看到,首先调用attachSurface方法将ReactSurfaceImpl实例添加到了attachedSurfaces中保存,这里attachedSurfaces是一个HashSet。接下来调用了callAfterGetOrCreateReactInstance方法,并传入了一个闭包,在这个闭包实现中又调用了reactInstance.startSurface(surface)。
callAfterGetOrCreateReactInstance方法返回了一个TaskInterface类型,这里 TaskInterface类似于JavaScript 的 Promise,查看bgExecutor的构建private val bgExecutor: Executor = Executors.newSingleThreadExecutor(),这里是有一个单独的后台线程,可见此处是进行了异步调用。继续查看callAfterGetOrCreateReactInstance方法实现:
kotlin
/** 如果 ReactInstance 尚不存在,则创建一个,并对其执行操作 */
private fun callAfterGetOrCreateReactInstance(
callingMethod: String,
executor: Executor = Task.IMMEDIATE_EXECUTOR,
runnable: (reactInstance: ReactInstance) -> Unit,
): Task<Void> =
getOrCreateReactInstance()
.onSuccess<Void>(
{ task ->
val reactInstance = task.getResult()
if (reactInstance == null) {
raiseSoftException(
"callAfterGetOrCreateReactInstance($callingMethod)",
"Execute: reactInstance is null. Dropping work.",
)
} else {
runnable(reactInstance)
}
null
},
executor,
)
接下来getOrCreateReactInstance的实现存在一系列方法调用:
kotlin
/**
* 创建 ReactInstance 的入口点
*
* 如果 ReactInstance 正在重新加载,则会返回 reload 任务。
* 如果 ReactInstance 正在销毁,则会等待销毁完成后再进行创建。
*/
private fun getOrCreateReactInstance(): Task<ReactInstance> =
Task.call({ waitThenCallGetOrCreateReactInstanceTask() }, bgExecutor)
@ThreadConfined("ReactHost")
private fun waitThenCallGetOrCreateReactInstanceTask(): Task<ReactInstance> =
waitThenCallGetOrCreateReactInstanceTaskWithRetries(0, 4)
@ThreadConfined("ReactHost")
private fun waitThenCallGetOrCreateReactInstanceTaskWithRetries(
tryNum: Int,
maxTries: Int,
): Task<ReactInstance> {
val method = "waitThenCallGetOrCreateReactInstanceTaskWithRetries"
// 如果正在 Reload,返回 reload task
reloadTask?.let { task ->
stateTracker.enterState(method, "React Native is reloading. Return reload task.")
return task
}
// 如果正在 Destroy,等待完成后重试
destroyTask?.let { task ->
val shouldTryAgain = tryNum < maxTries
if (shouldTryAgain) {
stateTracker.enterState(
method,
"React Native is tearing down.Wait for teardown to finish, before trying again (try count = $tryNum).",
)
// 等待 destroy 完成,然后递归重试
return task.onSuccessTask(
{ waitThenCallGetOrCreateReactInstanceTaskWithRetries(tryNum + 1, maxTries) },
bgExecutor,
)
}
// 达到最大重试次数,记录异常
raiseSoftException(
method,
"React Native is tearing down. Not wait for teardown to finish: reached max retries.",
)
}
// 没有阻塞,创建
return getOrCreateReactInstanceTask()
}
继续查看getOrCreateReactInstanceTask方法
kotlin
@ThreadConfined("ReactHost")
private fun getOrCreateReactInstanceTask(): Task<ReactInstance> {
val method = "getOrCreateReactInstanceTask()"
stateTracker.enterState(method)
// 使用 BridgelessAtomicRef 确保只创建一次
return createReactInstanceTaskRef.getOrCreate {
stateTracker.enterState(method, "Start")
// 确保 ReactHost 没有被 invalidate
Assertions.assertCondition(
!hostInvalidated,
"Cannot start a new ReactInstance on an invalidated ReactHost",
)
ReactMarker.logMarker(
ReactMarkerConstants.REACT_BRIDGELESS_LOADING_START,
BRIDGELESS_MARKER_INSTANCE_KEY,
)
val creationTask =
jsBundleLoader.onSuccess(
{ task ->
val bundleLoader = checkNotNull(task.getResult())
// 创建或获取 ReactContext
val reactContext =
bridgelessReactContextRef.getOrCreate {
stateTracker.enterState(method, "Creating BridgelessReactContext")
BridgelessReactContext(context, this)
}
reactContext.jsExceptionHandler = devSupportManager
// 创建 ReactInstance
stateTracker.enterState(method, "Creating ReactInstance")
val instance =
ReactInstance(
reactContext,
reactHostDelegate,
componentFactory,
devSupportManager,
{ e: Exception -> this.handleHostException(e) },
useDevSupport,
getOrCreateReactHostInspectorTarget(),
)
reactInstance = instance
// 设置内存压力监听
val memoryPressureListener = createMemoryPressureListener(instance)
this.memoryPressureListener = memoryPressureListener
memoryPressureRouter.addMemoryPressureListener(memoryPressureListener)
// 在 JS Bundle 执行的同时,并行地预先初始化 TurboModules
// 因为 TurboModuleManager 会处理任何并发访问
instance.initializeEagerTurboModules()
// 加载 JS Bundle
stateTracker.enterState(method, "Loading JS Bundle")
instance.loadJSBundle(bundleLoader)
stateTracker.enterState(
method,
"DevSupportManager.onNewReactContextCreated()",
)
devSupportManager.onNewReactContextCreated(reactContext)
reactContext.runOnJSQueueThread {
// 在 JS 线程上执行,以确保 JS 包已加载完毕。
// TODO T76081936 Move this if we switch to a sync RTE
ReactMarker.logMarker(
ReactMarkerConstants.REACT_BRIDGELESS_LOADING_END,
BRIDGELESS_MARKER_INSTANCE_KEY,
)
}
// 返回创建结果
CreationResult(instance, reactContext, reloadTask != null)
},
bgExecutor,
)
// 生命周期更新(在 UI 线程)
val lifecycleUpdateTask = task@{ task: Task<CreationResult> ->
if (task.isFaulted()) {
// handleHostException 可能会抛出异常,因此请将其移到任务调度程序之外。
uiExecutor.execute { handleHostException(checkNotNull(task.getError())) }
return@task
}
val result = checkNotNull(task.getResult())
val reactContext = result.context
val isReloading = result.isReloading
val isManagerResumed = reactLifecycleStateManager.lifecycleState == LifecycleState.RESUMED
/**
* ReactContext.onHostResume() 应该只在用户导航到第一个 React Native 页面时被调用。
*
* 初始化阶段:当用户导航到 React Native 页面时,应用程序会将 React 管理器置于 resumed 状态。
* 有两种初始化类型:
* (1) 如果 React Native 初始化发生在用户导航到 React Native 页面时,React 管理器会在初始化
* 开始时进入 resumed 状态,因此 ReactContext.onHostResume() 会在这里执行。
* (2) 如果 React Native 初始化发生在用户导航到 React Native 页面之前(即:React Native 被预加载),
* React 管理器在这里不会处于 resumed 状态。因此 ReactContext.onHostResume() 不会在这里执行。
* 但是,当用户导航到他们的第一个 React Native 页面时,应用程序会调用 ReactHost.onHostResume()。
* 那将会调用 ReactContext.onHostResume()。
*
* 重新加载阶段:如果管理器未处于 resumed 状态,则调用 ReactContext.onHostResume()。
* 如果 React Native 正在重新加载,可以合理地假设:
* (1) 我们过去一定导航到过某个 React Native 页面,或者
* (2) 我们当前一定在某个 React Native 页面上。
*/
if (isReloading && !isManagerResumed) {
reactLifecycleStateManager.moveToOnHostResume(reactContext, currentActivity)
} else {
/**
* 仅当已处于恢复状态时才调用 ReactContext.onHostResume(),
* 这与桥接 https://fburl.com/diffusion/2qhxmudv 一致。
*/
reactLifecycleStateManager.resumeReactContextIfHostResumed(reactContext, currentActivity)
}
stateTracker.enterState(method, "Executing ReactInstanceEventListeners")
for (listener in reactInstanceEventListeners) {
listener.onReactContextInitialized(reactContext)
}
}
creationTask.continueWith(lifecycleUpdateTask, uiExecutor)
// 提取 ReactInstance 并返回
creationTask.onSuccess({ task -> checkNotNull(task.getResult()).instance })
}
}
getOrCreateReactInstanceTask方法的结构看起来比较复杂,实际上是因为使用了Facebook的Bolts-Android 库,通过Task类来模仿JS的Promise来实现异步的任务链式编程。具体内容,可以查看本系列第二篇文章《React Native 之Android端Bolts库》。
这里我们只需从整体任务流来拆解,就能很好理解代码:
arduino
═══════════════════════════════════════════════════════
createReactInstanceTaskRef.getOrCreate单例保证,只会执行一次
═══════════════════════════════════════════════════════
┌─────────────────────────────────────────────────────┐
│ Task Chain: 异步任务链 │
├─────────────────────────────────────────────────────┤
│ jsBundleLoader (Task<JSBundleLoader>) │
│ ↓ .onSuccess │
│ creationTask (Task<CreationResult>) │
│ ├─ 创建 ReactContext │
│ ├─ 创建 ReactInstance │
│ ├─ 初始化 TurboModules (并行) │
│ └─ 加载 JS Bundle (并行) │
│ ↓ .continueWith │
│ lifecycleUpdateTask (Lambda) │
│ ├─ 错误处理 │
│ ├─ 更新生命周期 (UI 线程) │
│ └─ 通知监听器 │
│ ↓ .onSuccess │
│ Task<ReactInstance> │
│ └─ 提取 instance 字段 │
└─────────────────────────────────────────────────────┘
现在可以清晰的看到,主要有三个异步任务:jsBundleLoader加载、creationTask和lifecycleUpdateTask在串行执行。先看一下jsBundleLoader任务:
kotlin
private val jsBundleLoader: Task<JSBundleLoader>
get() {
stateTracker.enterState("getJSBundleLoader()")
if (devSupportManager.bundleFilePath != null) {
return try {
Task.forResult(
JSBundleLoader.createFileLoader(checkNotNull(devSupportManager.bundleFilePath))
)
} catch (e: Exception) {
Task.forError(e)
}
}
if (useDevSupport && allowPackagerServerAccess) {
return isMetroRunning.onSuccessTask(
{ task ->
val isMetroRunning = checkNotNull(task.getResult())
if (isMetroRunning) {
// 由于 Metro 正在运行,出现错误(method, "ReactContext is null. Reload
// 原因:$h 从服务器加载 JS 包
loadJSBundleFromMetro()
} else {
Task.forResult(reactHostDelegate.jsBundleLoader)
}
},
bgExecutor,
)
} else {
if (ReactBuildConfig.DEBUG) {
FLog.d(TAG, "Packager server access is disabled in this environment")
}
/**
* 生产模式下:回退到代理提供的 JS BundleLoader
*
* 注意:在 Task.call 中创建生产环境的 JSBundleLoader.
* 原因:如果 JSBundleLoader 创建过程中抛出异常,任务将会失败,我们将进入 ReactHost 的错误报告流程
*/
return try {
Task.forResult(reactHostDelegate.jsBundleLoader)
} catch (e: Exception) {
Task.forError(e)
}
}
}
这里的逻辑主要是处理JS Bundle包加载。
现在,我们可以绘制一个完整的任务执行流,如下:
makefile
T0: 方法被调用
├─ createReactInstanceTaskRef.getOrCreate { ... }
└─ 检查单例:未创建 → 执行 Lambda
T1: jsBundleLoader 开始加载
├─ 如果是开发模式:从 Metro 服务器下载
├─ 如果是生产模式:使用打包的 Bundle
└─ 返回 Task<JSBundleLoader>(未完成)
T2: jsBundleLoader 加载完成
└─ 触发 onSuccess 的 Lambda
T3: creationTask 开始执行(bgExecutor 线程)
├─ 创建 BridgelessReactContext
├─ 创建 ReactInstance
├─ 设置内存压力监听
├─ 并行初始化 Eager TurboModules ┐
│ ├─ 并行
├─ 加载 JS Bundle ┘
└─ 通知 DevSupportManager
T4: creationTask 完成
└─ 返回 CreationResult(instance, context, isReloading)
T5: continueWith 触发
└─ 切换到 uiExecutor(UI 线程)
T6: lifecycleUpdateTask 执行(UI 线程)
├─ 检查错误:无错误
├─ 更新生命周期:调用 onHostResume
└─ 通知监听器:所有 listeners
T7: lifecycleUpdateTask 完成
T8: onSuccess 提取 instance
└─ 从 CreationResult 中提取 ReactInstance
T9: 方法返回 Task<ReactInstance>(已完成)
小结
ReactHostImpl 中的startSurface方法主要做了三件事:
-
创建ReactInstance的实例
-
加载JS的bundle包
-
调用ReactInstance 的
startSurface方法
ReactInstance
接下来我们需要聚焦ReactInstance对象的创建,这里面是核心逻辑,先来看一下构造参数:
kotlin
val instance = ReactInstance(
reactContext, // BridgelessReactContext
reactHostDelegate, // DefaultReactHostDelegate
componentFactory, // ComponentFactory
devSupportManager, // DefaultDevSupportManagerFactory
{ e: Exception -> this.handleHostException(e) },
useDevSupport, // 是否是Debug模式启动
getOrCreateReactHostInspectorTarget(), // ReactHostInspectorTarget
)
注意,这里的reactHostDelegate实现类型是DefaultReactHostDelegate。下面我们仔细研究ReactInstance的初始化逻辑。
源码react-native/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.kt
kotlin
/**
* 用于替代 [com.facebook.react.bridge.CatalystInstance],负责创建和管理 React Native 实例。
*/
@ThreadSafe
@DoNotStrip
@FrameworkAPI
@UnstableReactNativeAPI
internal class ReactInstance(
private val context: BridgelessReactContext,
delegate: ReactHostDelegate,
componentFactory: ComponentFactory,
devSupportManager: DevSupportManager,
exceptionHandler: QueueThreadExceptionHandler,
useDevSupport: Boolean,
reactHostInspectorTarget: ReactHostInspectorTarget?,
) {
@Suppress("NoHungarianNotation") @DoNotStrip private val mHybridData: HybridData
private val turboModuleManager: TurboModuleManager
private val javaTimerManager: JavaTimerManager
private val viewManagerResolver: BridgelessViewManagerResolver
val reactQueueConfiguration: ReactQueueConfiguration
val fabricUIManager: FabricUIManager
val javaScriptContextHolder: JavaScriptContextHolder
init {
Systrace.beginSection(Systrace.TRACE_TAG_REACT, "ReactInstance.initialize")
/**
* 通过安装 JSI 绑定、初始化 Fabric + TurboModules 以及加载 JS bundle 来准备 ReactInstance
*/
val spec =
ReactQueueConfigurationSpec(
MessageQueueThreadSpec.newBackgroundThreadSpec("v_native"),
MessageQueueThreadSpec.newBackgroundThreadSpec("v_js"),
)
reactQueueConfiguration = ReactQueueConfigurationImpl.create(spec, exceptionHandler)
FLog.d(TAG, "Calling initializeMessageQueueThreads()")
context.initializeMessageQueueThreads(reactQueueConfiguration)
val jsMessageQueueThread = reactQueueConfiguration.getJSQueueThread()
val nativeModulesMessageQueueThread = reactQueueConfiguration.getNativeModulesQueueThread()
ReactChoreographer.initialize(AndroidChoreographerProvider.getInstance())
devSupportManager.startInspector()
val jsTimerExecutor = JSTimerExecutor()
javaTimerManager =
JavaTimerManager(
context,
jsTimerExecutor,
ReactChoreographer.getInstance(),
devSupportManager,
)
// 如果启用了性能分析,通知 JS
val isProfiling =
BuildConfig.ENABLE_PERFETTO ||
Systrace.isTracing(Systrace.TRACE_TAG_REACT) ||
getIsProfilingBuild()
mHybridData =
initHybrid(
delegate.jsRuntimeFactory,
jsMessageQueueThread,
nativeModulesMessageQueueThread,
javaTimerManager,
jsTimerExecutor,
ReactJsExceptionHandlerImpl(exceptionHandler),
delegate.bindingsInstaller,
isProfiling,
reactHostInspectorTarget,
)
javaScriptContextHolder = JavaScriptContextHolder(getJavaScriptContext())
// 设置 TurboModules
Systrace.beginSection(Systrace.TRACE_TAG_REACT, "ReactInstance.initialize#initTurboModules")
val reactPackages: MutableList<ReactPackage> = ArrayList<ReactPackage>()
reactPackages.add(
CoreReactPackage(context.devSupportManager, context.defaultHardwareBackBtnHandler)
)
if (useDevSupport) {
reactPackages.add(DebugCorePackage())
}
reactPackages.addAll(delegate.reactPackages)
val turboModuleManagerDelegate =
delegate.turboModuleManagerDelegateBuilder
.setPackages(reactPackages)
.setReactApplicationContext(context)
.build()
val unbufferedRuntimeExecutor = getUnbufferedRuntimeExecutor()
turboModuleManager =
TurboModuleManager( // 使用 unbuffered RuntimeExecutor 来安装绑定
unbufferedRuntimeExecutor,
turboModuleManagerDelegate,
getJSCallInvokerHolder(),
getNativeMethodCallInvokerHolder(),
)
Systrace.endSection(Systrace.TRACE_TAG_REACT)
// 设置 Fabric
Systrace.beginSection(Systrace.TRACE_TAG_REACT, "ReactInstance.initialize#initFabric")
viewManagerResolver = BridgelessViewManagerResolver(reactPackages, context)
// 为 JS 的 UIManager.hasViewManagerConfig() 初始化函数
// 使用 unbuffered RuntimeExecutor 来安装绑定
ComponentNameResolverBinding.install(
unbufferedRuntimeExecutor,
object : ComponentNameResolver {
override val componentNames: Array<String>
get() {
val viewManagerNames = viewManagerResolver.getViewManagerNames()
if (viewManagerNames.isEmpty()) {
FLog.e(TAG, "No ViewManager names found")
return arrayOf()
}
return viewManagerNames.toTypedArray<String>()
}
},
)
// 为 JS 的 UIManager.getViewManagerConfig() 初始化函数
// 这应该在 getTurboModuleManagerDelegate 之后执行,因为它依赖于 react packages 被初始化
// 这发生在 getTurboModuleManagerDelegate getter 内部
if (ReactNativeFeatureFlags.useNativeViewConfigsInBridgelessMode()) {
val customDirectEvents: MutableMap<String, Any> = HashMap()
UIConstantsProviderBinding.install(
// 使用 unbuffered RuntimeExecutor 来安装绑定
unbufferedRuntimeExecutor,
// 这里我们正在构建 UIManager.getConstants 调用的返回值
// 旧架构依赖于 constants 结构体包含:
// 1. 所有原生组件的预加载视图配置
// 2. genericBubblingEventTypes(通用冒泡事件类型)
// 3. genericDirectEventTypes(通用直接事件类型)
// 我们想要匹配这种行为
{ Arguments.makeNativeMap(UIManagerModuleConstantsHelper.defaultExportableEventTypes) },
ConstantsForViewManagerProvider { viewManagerName: String ->
val viewManager =
viewManagerResolver.getViewManager(viewManagerName)
?: return@ConstantsForViewManagerProvider null
getConstantsForViewManager(viewManager, customDirectEvents)
},
{
val viewManagers: List<ViewManager<*, *>> =
ArrayList(viewManagerResolver.eagerViewManagerMap.values)
val constants = createConstants(viewManagers, customDirectEvents)
val lazyViewManagers = viewManagerResolver.lazyViewManagerNames
if (!lazyViewManagers.isEmpty()) {
constants["ViewManagerNames"] = ArrayList(lazyViewManagers)
constants["LazyViewManagersEnabled"] = true
}
Arguments.makeNativeMap(constants)
},
)
}
val eventBeatManager = EventBeatManager()
fabricUIManager =
FabricUIManager(context, ViewManagerRegistry(viewManagerResolver), eventBeatManager)
// 在 Fabric 初始化之前需要完成的其他初始化
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(context)
val binding = FabricUIManagerBinding()
binding.register(
getBufferedRuntimeExecutor(),
getRuntimeScheduler(),
fabricUIManager,
eventBeatManager,
componentFactory,
)
// 初始化 FabricUIManager
fabricUIManager.initialize()
Systrace.endSection(Systrace.TRACE_TAG_REACT)
Systrace.endSection(Systrace.TRACE_TAG_REACT)
}
// 省略......
}
这段初始化代码,大概可以分为5个阶段:
-
线程创建
kotlinval spec = ReactQueueConfigurationSpec( MessageQueueThreadSpec.newBackgroundThreadSpec("v_native"), MessageQueueThreadSpec.newBackgroundThreadSpec("v_js"), ) reactQueueConfiguration = ReactQueueConfigurationImpl.create(spec, exceptionHandler) context.initializeMessageQueueThreads(reactQueueConfiguration)- 创建两个后台线程:v_native (Native模块线程)和 v_js(JS线程)
- 初始化消息队列
-
初始化基础设施
kotlinReactChoreographer.initialize(AndroidChoreographerProvider.getInstance()) devSupportManager.startInspector() val jsTimerExecutor = JSTimerExecutor() javaTimerManager = JavaTimerManager( context, jsTimerExecutor, ReactChoreographer.getInstance(), devSupportManager, )-
初始化 ReactChoreographer(用于同步动画和布局)
-
启动 Inspector(开发工具)
-
创建 JavaTimerManager(处理 JS 定时器)
-
-
初始化 Native 层 (HybridData)
kotlinmHybridData = initHybrid( delegate.jsRuntimeFactory, jsMessageQueueThread, nativeModulesMessageQueueThread, javaTimerManager, jsTimerExecutor, ReactJsExceptionHandlerImpl(exceptionHandler), delegate.bindingsInstaller, isProfiling, reactHostInspectorTarget, ) javaScriptContextHolder = JavaScriptContextHolder(getJavaScriptContext())-
调用 native 方法 initHybrid 创建 C++ 层的 ReactInstance 对象
-
获取 JavaScript 上下文句柄
-
-
初始化 TurboModule 系统
主要代码在
turboModuleManagerDelegate和turboModuleManager的创建前后。-
构建 ReactPackage 列表(包括核心包和开发包)
-
创建 TurboModuleManagerDelegate
-
初始化 TurboModuleManager,这是新架构中 Native 模块的管理器
-
-
初始化 Fabric UI 系统
- 创建 ViewManager 解析器:用于按需加载和管理所有的 ViewManager
- 安装 ComponentNameResolver 绑定:允许 JS 端调用 UIManager.hasViewManagerConfig() 方法
- 安装 UIConstantsProvider 绑定
- 创建并初始化 FabricUIManager
对于TurboModule 系统和Fabric UI 系统,我们在后面单独章节研究。现在来看一下initHybrid方法。此方法是一个native方法,由JNI层面C++实现。方法返回一个HybridData类型对象。这里HybridData来自由Facebook开源的另一个库 fbjni。此库的介绍:
Facebook JNI 辅助库旨在简化 Java 本地接口 (JNI) 的使用。这些辅助库的实现是为了方便在 Android 上集成跨平台移动代码,但其设计并不针对 Android 平台。它可以与任何支持 JNI 的 Java 虚拟机 (JVM) 一起使用。
简单说,JVM的JNI接口设计得非常冗长繁琐,直接使用JNI接口代码量会大大增加。fbjni库就是对JNI接口的封装,简化调用。
这里HybridData 就是fbjni提供的 JNI 桥接类
java
class HybridData {
static {
NativeLoader.loadLibrary("fbjni");
}
// 持有 C++ 对象的指针
private long mNativePointer; // C++ 对象的内存地址
// 省略......
}
参考以下架构图:
scss
┌─────────────────────────────────────────────────────────┐
│ React Native 架构 │
├─────────────────────────────────────────────────────────┤
│ │
│ Java/Kotlin 层 │
│ ┌──────────────────────────────┐ │
│ │ ReactInstance │ │
│ │ ├─ mHybridData ────────┐ │ │
│ │ ├─ turboModuleManager │ │ │
│ │ └─ fabricUIManager │ │ │
│ └─────────────────────────┘ │ │
│ │ │ │
│ │ (JNI/fbjni) │ │
│ ↓ │ │
│ ┌──────────────────────────┐ │ ← HybridData 桥接 │
│ │ HybridData │───┘ │
│ │ 持有 C++ 智能指针 │ │
│ └──────────────────────────┘ │
│ │ │
│ ↓ │
│ C++ 层 │
│ ┌──────────────────────────┐ │
│ │ ReactInstance (C++) │ │
│ │ ├─ JSI Runtime │ │
│ │ ├─ TurboModules │ │
│ │ └─ Fabric │ │
│ └──────────────────────────┘ │
│ │ │
│ │ (JSI) │
│ ↓ │
│ JavaScript 层 │
│ ┌──────────────────────────┐ │
│ │ React Components │ │
│ └──────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
主要作用就是桥接 C++ 对象:
- 持有 C++ 对象指针:保存对应的 C++ ReactInstance 对象的智能指针
- 生命周期管理:确保 C++ 对象与 Java 对象同步创建和销毁
- JNI 桥接:作为 Java 调用 C++ 方法的桥梁(通过 native 方法)
- 内存管理:防止 C++ 对象被过早释放,避免悬空指针
注意:
- 字段名必须是 "mHybridData"(fbjni 通过反射查找此名称)
- 通过native 方法
initHybrid()初始化,返回持有 C++ 对象的 HybridData - 调用 resetNative() 释放 C++ 资源(在 destroy() 中)
- 使用注解
@DoNotStrip防止 ProGuard 混淆/删除此字段
Java对象与C++对象的对应关系,可以查看源码 Hybrid.h#L310
关于C++侧的详细分析放到后面,这里先看看startSurface方法的调用:
kotlin
// ReactInstance.kt
/**
* 渲染一个 React Native surface.
*
* @param surface 要渲染的 [com.facebook.react.interfaces.fabric.ReactSurface] 对象
*/
@ThreadConfined("ReactHost")
fun startSurface(surface: ReactSurfaceImpl) {
FLog.d(TAG, "startSurface() is called with surface: ${surface.surfaceID}")
Systrace.beginSection(Systrace.TRACE_TAG_REACT, "ReactInstance.startSurface")
val view = surface.view
checkNotNull(view) {
"Starting surface without a view is not supported, use prerenderSurface instead."
}
/**
* 这是针对 646912b2590a6d5e760316cc064d1e27 的临时缓解措施,,
*
* <p>TODO T83828172 调查为什么 surface.getView() 的 ID 不等于 View.NO_ID
*/
if (view.id != View.NO_ID) {
ReactSoftExceptionLogger.logSoftException(
TAG,
IllegalViewOperationException(
"surfaceView's is NOT equal to View.NO_ID before calling startSurface."
),
)
view.id = View.NO_ID
}
if (surface.isRunning) {
// Surface 已经在运行(预渲染过),只需附加 View
fabricUIManager.attachRootView(surface.surfaceHandler, view)
} else {
fabricUIManager.startSurface(surface.surfaceHandler, surface.context, view)
}
Systrace.endSection(Systrace.TRACE_TAG_REACT)
}
这个方法的核心功能是:启动并渲染一个 React Native 界面(Surface)到 Android View 上。
但要注意,这里注解指明,该方法只能在ReactHost的后台线程上调用。具体来说,就是以下源码处:
kotlin
return callAfterGetOrCreateReactInstance(method, bgExecutor) { reactInstance: ReactInstance ->
stateTracker.enterState(method, "Execute")
reactInstance.startSurface(surface)
}
小结
ReactInstance 的创建做了很多事,概括一下,其实主要是四点:
-
创建后台工作线程
-
初始化 TurboModule 系统
-
初始化 Fabric UI 系统
-
启动并渲染React Native 界面
C++ 侧
如何找到ReactInstance 中native方法initHybrid对应的C++实现?
第一步,自然是定位到其加载的动态库:
kotlin
// ReactInstance.kt
init {
/**
* 加载 rninstance 动态库
*
* 该库包含:
* 1. ReactInstance 的所有 JNI native 方法实现(如 initHybrid、loadJSBundle 等)
* 2. C++ ReactInstance 类的实现
* 3. JSI Runtime 的初始化代码
* 4. TurboModule 和 Fabric 的 C++ 层实现
*
* 必须在类加载时调用,确保 native 方法可用
*/
SoLoader.loadLibrary("rninstance")
}
第二步,全局搜索定义了rninstance库名称的CMakeLists.txt文件:
react-native/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/CMakeLists.txt
cmake
file(GLOB_RECURSE bridgeless_jni_SRC CONFIGURE_DEPENDS *.cpp)
add_library(rninstance
OBJECT
${bridgeless_jni_SRC}
)
CMakeLists.txt是用于构建C++代码的配置文件,类似于Gradle脚本与Kotlin的关系。这里的两行代码,首先是递归查找当前路径下的所有.cpp文件,然后将文件列表保存到一个变量bridgeless_jni_SRC中,第二行则是将找到的.cpp源文件全部编译成一个rninstance 对象文件。这里了解C/C++开发的人一定会有疑惑,前面不是说在kotlin中加载动态库rninstance吗?现在构建脚本怎么是编译成对象文件,这可跟动态库不是一回事了?
其实我们继续搜索rninstance 关键字就会发现,在另外一个更外层配置文件react-native/packages/react-native/ReactAndroid/src/main/jni/CMakeLists.txt中,有如下内容:
cmake
# libreactnative.so 依赖项
#
# React Native Android 库(.aar 文件)应该仅通过此目标公开头文件。
# 这样做是为了减少最终库中包含的 .so 文件数量。
add_library(reactnative
SHARED
$<TARGET_OBJECTS:bridgeless>
$<TARGET_OBJECTS:bridgelessnativeviewconfig>
$<TARGET_OBJECTS:callinvokerholder>
$<TARGET_OBJECTS:fabricjni>
$<TARGET_OBJECTS:glog_init>
$<TARGET_OBJECTS:jni_lib_merge_glue>
$<TARGET_OBJECTS:jserrorhandler>
$<TARGET_OBJECTS:jsinspector>
# 省略......
$<TARGET_OBJECTS:rninstance>
# 省略......
$<TARGET_OBJECTS:runtimeexecutor>
$<TARGET_OBJECTS:turbomodulejsijni>
$<TARGET_OBJECTS:uimanagerjni>
$<TARGET_OBJECTS:yoga>
)
target_merge_so(reactnative)
可以看到,对象文件rninstance 最终被合并到了动态库reactnative 中。注释也告诉我们,这样做是为了减少动态库文件的数量。那么SoLoader.loadLibrary("rninstance")又是如何正确的找到reactnative 动态库的呢?这就需要来研究Facebaook另外一个开源库SoLoader的源码了。SoLoader.java#L891:
java
// SoLoader.java
public static void init(Context context, @Nullable ExternalSoMapping externalSoMapping) {
synchronized (SoLoader.class) {
SoLoader.externalSoMapping = externalSoMapping;
}
try {
init(context, 0);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
public static void init(Context context,int flags,@Nullable SoFileLoader soFileLoader,@Nullable Provider<ApplicationInfo> applicationInfoProvider)
throws IOException {
if (isInitialized()) {
LogUtil.w(TAG, "SoLoader already initialized");
return;
}
try {
isEnabled = initEnableConfig(context);
// 省略......
}
}
/**
* 确定是否启用 soloader
*/
private static boolean initEnableConfig(Context context) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return true;
}
if (SoLoader.externalSoMapping != null) {
// 此用例适用于使用开源 SoMerging 的 React Native 应用程序。
// 如果已提供 externalSoMapping,则无需检查 Manifest 文件中是否启用了 SoLoader。
return true;
}
// 省略......
}
public static boolean loadLibrary(String shortName) {
return isEnabled ? loadLibrary(shortName, 0) : NativeLoader.loadLibrary(shortName);
}
在本文开头分析入口代码自动生成的地方,loadReactNative()方法调用处,我们已经见过了SoLoader.init(context, OpenSourceMergedSoMapping.INSTANCE)初始化代码,再结合以上代码的官方注释,当初始化传入externalSoMapping对象时,默认启用SoLoader ,所以接下来就需要跟踪loadLibrary(shortName, 0)。由于本文主要分析React Native初始化,这里就不再详细分析SoLoader 的实现源码,后面在其他章节专门研究SoLoader 。先简要看一下loadLibrary实现:
java
/**
* 加载动态库,并初始化其中包含的任何 JNI 绑定。
*
* @param shortName 要查找的库的名称,不包含"lib"前缀或".so"后缀。
* @param loadFlags 用于控制加载行为的标志。可用的标志请参见 {@link SoSource} (LOAD_FLAG_XXX).
*/
public static boolean loadLibrary(String shortName, int loadFlags) throws UnsatisfiedLinkError {
// 省略......
return loadLibraryOnAndroid(shortName, loadFlags);
}
private static boolean loadLibraryOnAndroid(String shortName, int loadFlags) {
@Nullable Throwable failure = null;
String mergedLibName;
if (externalSoMapping != null) {
mergedLibName = externalSoMapping.mapLibName(shortName);
} else {
mergedLibName = MergedSoMapping.mapLibName(shortName);
}
String soName = mergedLibName != null ? mergedLibName : shortName;
ObserverHolder.onLoadLibraryStart(shortName, mergedLibName, loadFlags);
boolean wasLoaded = false;
try {
wasLoaded =
loadLibraryBySoName(
System.mapLibraryName(soName), shortName, mergedLibName, loadFlags, null);
return wasLoaded;
} catch (Throwable t) {
failure = t;
throw t;
} finally {
ObserverHolder.onLoadLibraryEnd(failure, wasLoaded);
}
}
这里可以看到,当传入了externalSoMapping时,使用的就是该实例提供的mapLibName方法查找映射的库名称。查看react-native/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/soloader/OpenSourceMergedSoMapping.kt:
kotlin
public override fun mapLibName(input: String): String =
when (input) {
"fabricjni",
"jsinspector",
"mapbufferjni",
"react_devsupportjni",
"react_featureflagsjni",
"react_newarchdefaults",
"reactnativeblob",
"reactnativejni",
"reactnativejni_common",
"rninstance",
"turbomodulejsijni",
"uimanagerjni",
"yoga" -> {
"reactnative"
}
"hermes_executor",
"hermesinstancejni",
"jsijniprofiler" -> {
"hermestooling"
}
else -> input
}
可见,当查找的是"rninstance"时,返回的实际上是动态库"reactnative"。在SoLoader 库中,最后经过一系列复杂的处理和调用,最终仍然是通过 System.load 来加载动态库。SoLoader 库年头算是比较久了,其中有大量代码用于处理各种兼容性问题,包括不同的安卓版本,不同的环境(安卓或JVM)等等,以致于其中代码处理十分复杂,成了一座屎山。所以具体的SoLoader加载调用链路我们这里就略过了。
第三步,既然定位到了rninstance 源码目录,直接查看头文件react-native/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.h和实现文件react-native/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.cpp:
cpp
// JReactInstance.h
class JReactInstance : public jni::HybridClass<JReactInstance> {
public:
constexpr static auto kJavaDescriptor = "Lcom/facebook/react/runtime/ReactInstance;";
static jni::local_ref<jhybriddata> initHybrid(
jni::alias_ref<jhybridobject> /*unused*/,
jni::alias_ref<JJSRuntimeFactory::javaobject> jsRuntimeFactory,
jni::alias_ref<JavaMessageQueueThread::javaobject> jsMessageQueueThread,
jni::alias_ref<JavaMessageQueueThread::javaobject> nativeMessageQueueThread,
jni::alias_ref<JJavaTimerManager::javaobject> javaTimerManager,
jni::alias_ref<JJSTimerExecutor::javaobject> jsTimerExecutor,
jni::alias_ref<JReactExceptionManager::javaobject> jReactExceptionManager,
jni::alias_ref<JBindingsInstaller::javaobject> jBindingsInstaller,
bool isProfiling,
jni::alias_ref<JReactHostInspectorTarget::javaobject> jReactHostInspectorTarget);
static void registerNatives();
void loadJSBundleFromAssets(jni::alias_ref<JAssetManager::javaobject> assetManager, const std::string &assetURL);
void loadJSBundleFromFile(const std::string &fileName, const std::string &sourceURL);
void callFunctionOnModule(const std::string &moduleName, const std::string &methodName, NativeArray *args);
// 省略......
};
注意,这里initHybrid是一个静态方法,关于上层Kotlin是如何关联到此方法的,这里面大有讲究。如果了解JNI调用,那么就知道,此处是不符合JNI的Native方法绑定规则,想必越是熟悉JNI开发,越是对此困惑。实际上这里面搞了相当多的骚操作,我们放到本章最后再来详细解谜,这里优先关注React Native的核心初始化流程。
首先JReactInstance继承自jni::HybridClass类,此类使用了 C++ 的 CRTP模式(Curiously Recurring Template Pattern,奇异递归模板模式)。所以这里子类将自己作为模板参数传了进去,这点后面很重要 。简单的理解,就相当于在父类指定了一个泛型,泛型的类型就是当前的子类。这里面使用了fbjni 提供的机制,前面也提到过,JReactInstance 实际上就相当于一个胶水层,将kotlin的ReactInstance 类和C++层的ReactInstance类关联起来,实现互相调用,互相通信。
cpp
// JReactInstance.cpp
jni::local_ref<JReactInstance::jhybriddata> JReactInstance::initHybrid(
jni::alias_ref<jhybridobject> /* unused */,
jni::alias_ref<JJSRuntimeFactory::javaobject> jsRuntimeFactory,
jni::alias_ref<JavaMessageQueueThread::javaobject> jsMessageQueueThread,
jni::alias_ref<JavaMessageQueueThread::javaobject> nativeMessageQueueThread,
jni::alias_ref<JJavaTimerManager::javaobject> javaTimerManager,
jni::alias_ref<JJSTimerExecutor::javaobject> jsTimerExecutor,
jni::alias_ref<JReactExceptionManager::javaobject> jReactExceptionManager,
jni::alias_ref<JBindingsInstaller::javaobject> jBindingsInstaller,
bool isProfiling,
jni::alias_ref<JReactHostInspectorTarget::javaobject>
jReactHostInspectorTarget) {
return makeCxxInstance(
jsRuntimeFactory,
jsMessageQueueThread,
nativeMessageQueueThread,
javaTimerManager,
jsTimerExecutor,
jReactExceptionManager,
jBindingsInstaller,
isProfiling,
jReactHostInspectorTarget);
}
在实现代码中,首先关注initHybrid方法,其作用就是使用fbjni提供的makeCxxInstance方法创建了一个JReactInstance实例:
cpp
template <typename... Args>
static local_ref<detail::HybridData> makeCxxInstance(Args&&... args) {
return makeHybridData(
std::unique_ptr<T>(new T(std::forward<Args>(args)...)));
}
这是一个模版方法,其核心就是创建了一个类型T 的实例对象。这里的T ,就是前面强调的CRTP模式传入的模版参数,所以这里就是在new一个 JReactInstance 实例。最后又调用makeHybridData方法包了一层返回给kotlin层持有,实际上就是包装成一个适合kotlin持有的智能指针。
接下来,重点就要关注JReactInstance的构造方法了:
cpp
JReactInstance::JReactInstance(
jni::alias_ref<JJSRuntimeFactory::javaobject> jsRuntimeFactory, // JS 引擎工厂(Hermes/JSC)
jni::alias_ref<JavaMessageQueueThread::javaobject> jsMessageQueueThread, // JS 线程
jni::alias_ref<JavaMessageQueueThread::javaobject> nativeMessageQueueThread, // Native 线程
jni::alias_ref<JJavaTimerManager::javaobject> javaTimerManager, // Java 定时器管理
jni::alias_ref<JJSTimerExecutor::javaobject> jsTimerExecutor, // JS 定时器执行器
jni::alias_ref<JReactExceptionManager::javaobject> jReactExceptionManager, // 异常管理
jni::alias_ref<JBindingsInstaller::javaobject> jBindingsInstaller, // 自定义绑定安装器
bool isProfiling, // 是否开启性能分析
jni::alias_ref<JReactHostInspectorTarget::javaobject> jReactHostInspectorTarget // 调试器
) noexcept {
// TODO(janzer): Lazily create runtime
// 创建消息队列线程包装器
auto sharedJSMessageQueueThread =
std::make_shared<JMessageQueueThread>(jsMessageQueueThread);
auto sharedNativeMessageQueueThread =
std::make_shared<JMessageQueueThread>(nativeMessageQueueThread);
// 创建定时器管理器 (for JS timers)
auto timerRegistry =
std::make_unique<JavaTimerRegistry>(jni::make_global(javaTimerManager));
auto timerManager = std::make_shared<TimerManager>(std::move(timerRegistry));
jsTimerExecutor->cthis()->setTimerManager(timerManager);
// 设置 JS 错误处理回调
jReactExceptionManager_ = jni::make_global(jReactExceptionManager);
auto onJsError =
[weakJReactExceptionManager = jni::make_weak(jReactExceptionManager)](
jsi::Runtime& runtime,
const JsErrorHandler::ProcessedError& error) mutable noexcept {
if (auto jReactExceptionManager =
weakJReactExceptionManager.lockLocal()) {
jReactExceptionManager->reportJsException(runtime, error);
}
};
jBindingsInstaller_ = jni::make_global(jBindingsInstaller);
// 创建核心 ReactInstance
instance_ = std::make_unique<ReactInstance>(
jsRuntimeFactory->cthis()->createJSRuntime(sharedJSMessageQueueThread),
sharedJSMessageQueueThread,
timerManager,
std::move(onJsError),
jReactHostInspectorTarget
? jReactHostInspectorTarget->cthis()->getInspectorTarget()
: nullptr);
// 设置定时器的 RuntimeExecutor
auto bufferedRuntimeExecutor = instance_->getBufferedRuntimeExecutor();
timerManager->setRuntimeExecutor(bufferedRuntimeExecutor);
// 初始化 JS Runtime
ReactInstance::JSRuntimeFlags options = {.isProfiling = isProfiling};
// TODO T194671568 Consider moving runtime init to the JS thread.
instance_->initializeRuntime(options, [this](jsi::Runtime& runtime) {
// 绑定 Android 日志系统
react::Logger androidLogger =
static_cast<void (*)(const std::string&, unsigned int)>(
&reactAndroidLoggingHook);
react::bindNativeLogger(runtime, androidLogger);
if (jBindingsInstaller_ != nullptr) {
auto appBindingInstaller =
jBindingsInstaller_->cthis()->getBindingsInstallFunc();
if (appBindingInstaller != nullptr) {
appBindingInstaller(runtime);
}
}
});
auto unbufferedRuntimeExecutor = instance_->getUnbufferedRuntimeExecutor();
// 创建调用器(用于 TurboModules)
auto jsInvoker = std::make_unique<RuntimeSchedulerCallInvoker>(
instance_->getRuntimeScheduler());
jsCallInvokerHolder_ = jni::make_global(
CallInvokerHolder::newObjectCxxArgs(std::move(jsInvoker)));
auto nativeMethodCallInvoker =
std::make_unique<BridgelessNativeMethodCallInvoker>(
sharedNativeMessageQueueThread);
nativeMethodCallInvokerHolder_ = jni::make_global(
NativeMethodCallInvokerHolder::newObjectCxxArgs(
std::move(nativeMethodCallInvoker)));
// 创建 RuntimeExecutor 包装器
// 将它存储在这里是为了确保 Java 引用不会被销毁。
unbufferedRuntimeExecutor_ = jni::make_global(
JRuntimeExecutor::newObjectCxxArgs(unbufferedRuntimeExecutor));
bufferedRuntimeExecutor_ = jni::make_global(
JRuntimeExecutor::newObjectCxxArgs(bufferedRuntimeExecutor));
runtimeScheduler_ = jni::make_global(
JRuntimeScheduler::newObjectCxxArgs(instance_->getRuntimeScheduler()));
}
以上流程非常清晰,我已经添加了一部分注释。接下来重点看一下ReactInstance 构造部分,react-native/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp
cpp
ReactInstance::ReactInstance(
std::unique_ptr<JSRuntime> runtime,
std::shared_ptr<MessageQueueThread> jsMessageQueueThread,
std::shared_ptr<TimerManager> timerManager,
JsErrorHandler::OnJsError onJsError,
jsinspector_modern::HostTarget* parentInspectorTarget)
: runtime_(std::move(runtime)),
jsMessageQueueThread_(std::move(jsMessageQueueThread)),
timerManager_(std::move(timerManager)),
jsErrorHandler_(std::make_shared<JsErrorHandler>(std::move(onJsError))),
parentInspectorTarget_(parentInspectorTarget) {
// 创建 RuntimeExecutor(核心逻辑)
RuntimeExecutor runtimeExecutor =
[weakRuntime = std::weak_ptr(runtime_),
weakTimerManager = std::weak_ptr(timerManager_),
weakJsThread = std::weak_ptr(jsMessageQueueThread_),
jsErrorHandler = jsErrorHandler_](auto callback) {
// 检查 runtime 是否还存活
if (weakRuntime.expired()) {
return;
}
if (auto jsThread = weakJsThread.lock()) {
// 在 JS 线程上执行
jsThread->runOnQueue([jsErrorHandler,
weakRuntime,
weakTimerManager,
callback = std::move(callback)]() {
auto runtime = weakRuntime.lock();
if (!runtime) {
return;
}
jsi::Runtime& jsiRuntime = runtime->getRuntime();
TraceSection s("ReactInstance::_runtimeExecutor[Callback]");
try {
ShadowNode::setUseRuntimeShadowNodeReferenceUpdateOnThread(true);
callback(jsiRuntime); // 执行回调
} catch (jsi::JSError& originalError) {
jsErrorHandler->handleError(jsiRuntime, originalError, true);
} catch (std::exception& ex) {
// 捕获 C++ 异常,转换为 JS 错误
jsi::JSError error(
jsiRuntime, std::string("Non-js exception: ") + ex.what());
jsErrorHandler->handleError(jsiRuntime, error, true);
}
});
}
};
// 有调试器的情况
if (parentInspectorTarget_ != nullptr) {
auto executor = parentInspectorTarget_->executorFromThis();
auto bufferedRuntimeExecutorThatWaitsForInspectorSetup =
std::make_shared<BufferedRuntimeExecutor>(runtimeExecutor);
auto runtimeExecutorThatExecutesAfterInspectorSetup =
[bufferedRuntimeExecutorThatWaitsForInspectorSetup](
std::function<void(jsi::Runtime & runtime)>&& callback) {
bufferedRuntimeExecutorThatWaitsForInspectorSetup->execute(
std::move(callback));
};
// 创建 RuntimeScheduler
runtimeScheduler_ = createRuntimeScheduler(
runtimeExecutorThatExecutesAfterInspectorSetup,
[jsErrorHandler = jsErrorHandler_](
jsi::Runtime& runtime, jsi::JSError& error) {
jsErrorHandler->handleError(runtime, error, true);
});
auto runtimeExecutorThatGoesThroughRuntimeScheduler =
[runtimeScheduler = runtimeScheduler_.get()](
std::function<void(jsi::Runtime & runtime)>&& callback) {
runtimeScheduler->scheduleWork(std::move(callback));
};
// 这段代码可能从任意线程执行,因此我们需要确保在正确的线程中设置调试器逻辑。
// 如果当前已经在正确的线程中,回调会立即执行
executor([this,
runtimeExecutorThatGoesThroughRuntimeScheduler,
bufferedRuntimeExecutorThatWaitsForInspectorSetup](
jsinspector_modern::HostTarget& hostTarget) {
// 通过 page target executor 调度的回调通常不保证一定会执行
// (例如:如果 page target 被销毁了)
// 但在这个场景下,回调是保证会执行的,因为 page target 不可能在实例完成初始化之前被销毁:
// * 在 iOS 上,这是因为我们同步地进行初始化设置
// * 在 Android 上,这是因为我们会显式等待实例创建任务完成后,才开始销毁流程
inspectorTarget_ = &hostTarget.registerInstance(*this);
runtimeInspectorTarget_ = &inspectorTarget_->registerRuntime(
runtime_->getRuntimeTargetDelegate(),
runtimeExecutorThatGoesThroughRuntimeScheduler);
bufferedRuntimeExecutorThatWaitsForInspectorSetup->flush();
});
} else {
// 没有调试器的情况
runtimeScheduler_ = createRuntimeScheduler(
runtimeExecutor,
[jsErrorHandler = jsErrorHandler_](
jsi::Runtime& runtime, jsi::JSError& error) {
jsErrorHandler->handleError(runtime, error, true);
});
}
// 在 JS Bundle 加载完成前,缓冲所有的 JS 调用,加载完成后再执行
bufferedRuntimeExecutor_ = std::make_shared<BufferedRuntimeExecutor>(
[runtimeScheduler = runtimeScheduler_.get()](
std::function<void(jsi::Runtime & runtime)>&& callback) {
runtimeScheduler->scheduleWork(std::move(callback));
});
}
这其中,RuntimeExecutor非常重要,这里可以看到是一个lambda函数,相当于一个闭包。它的主要作用是:
-
接收一个 callback(需要在 JS 线程执行的函数)
-
将 callback 调度到 JS 线程
-
执行时自动处理异常
小结
最后,总结一下C++侧的初始化流程:
css
┌─────────────────────────────────────────────────────────────────────────────┐
│ JReactInstance 创建流程 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Java: ReactInstance.kt │
│ │ │
│ ▼ │
│ initHybrid(jsRuntimeFactory, jsThread, nativeThread, ...) │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ JReactInstance 构造函数 │ │
│ ├─────────────────────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ ① 包装 Java 线程 → C++ MessageQueueThread │ │
│ │ │ │
│ │ ② 创建 TimerManager ← JavaTimerRegistry ← JavaTimerManager │ │
│ │ │ │
│ │ ③ 创建 onJsError 回调 (JS 错误 → Java 红屏) │ │
│ │ │ │
│ │ ④ 创建 ReactInstance ───────────────────────┐ │ │
│ │ │ │ │ │
│ │ ▼ ▼ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ ReactInstance 构造函数 │ │ │
│ │ ├──────────────────────────────────────────────────────────────┤ │ │
│ │ │ │ │ │
│ │ │ (a) 保存 runtime_, jsMessageQueueThread_, timerManager_ │ │ │
│ │ │ │ │ │
│ │ │ (b) 创建 RuntimeExecutor (核心调度器) │ │ │
│ │ │ - 捕获弱引用防止循环引用 │ │ │
│ │ │ - 将任务调度到 JS 线程 │ │ │
│ │ │ - 自动捕获异常并处理 │ │ │
│ │ │ │ │ │
│ │ │ (c) 创建 RuntimeScheduler │ │ │
│ │ │ - 任务优先级调度 │ │ │
│ │ │ - 与 React 调度器集成 │ │ │
│ │ │ │ │ │
│ │ │ (d) 创建 BufferedRuntimeExecutor │ │ │
│ │ │ - 缓冲 Bundle 加载前的调用 │ │ │
│ │ │ │ │ │
│ │ │ (e) 设置调试器 (可选) │ │ │
│ │ │ │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ⑤ timerManager->setRuntimeExecutor() │ │
│ │ │ │
│ │ ⑥ initializeRuntime() │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 绑定 console.log, setTimeout, 全局变量... │ │
│ │ │ │
│ │ ⑦ 创建 CallInvokerHolder (TurboModules 用) │ │
│ │ │ │
│ │ ⑧ 包装 RuntimeExecutor/RuntimeScheduler 为 Java 对象 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
关于JNI函数的注册
前面我们留了一个坑,Kotlin类ReactInstance 中的Native方法initHybrid是如何与C++类JReactInstance 中的initHybrid关联起来的?
根据JNI的规则,总共有两种方式。具体的可以查看谷歌方法提供的JNI 文档。这里好像两种都不像,但我们可以从这个文档知道,当JNI加载动态库时,动态库中可以定义一个全局函数JNI_OnLoad,我们可以在这个全局函数中注册Java/Kotlin的Native方法,将上层的Native方法与具体的C++实现关联起来。而且这个JNI_OnLoad是自动调用的。
我们找到rninstance 库的源码目录,里面刚好包含了一个react-native/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/OnLoad.cpp文件,里面就定义了一个全局函数JNI_OnLoad。到这里似乎一切都没有问题,但这里有一个最大的问题------rninstance 库并没有被独立编译成一个动态库,它是和众多模块一起被最终合并到reactnative 动态库中。所以问题就来了,其他那些被合并的模块中也定义了全局函数JNI_OnLoad,这会直接导致全局函数命名冲突。一个动态库,只能有一个JNI_OnLoad,否则就导致符号冲突了。那么reactnative 动态库中定义的众多JNI_OnLoad函数是怎么回事?
我们先研究一下rninstance 构建脚本react-native/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/CMakeLists.txt:
cmake
add_library(rninstance
OBJECT
${bridgeless_jni_SRC}
)
# 省略......
target_merge_so(rninstance)
发现这里调用了一个cmake函数target_merge_so,全局搜索找到该函数实现react-native/packages/react-native/ReactAndroid/src/main/jni/first-party/jni-lib-merge/SoMerging-utils.cmake:
scss
# 如果需要将请求的库与 libreactnative.so 合并,则应调用此函数,例如:
# target_merge_so(react_newarchdefaults)
#
# 此函数将强制包含 jni_lib_merge.h 头文件,该文件负责将 JNI_OnLoad 函数重定义为 JNI_OnLoad_Weak。
function(target_merge_so target_name)
if(NOT ANDROID)
return()
endif()
target_compile_options(${target_name}
PRIVATE
-DORIGINAL_SONAME=\"lib${target_name}.so\"
-include ${REACT_ANDROID_DIR}/src/main/jni/first-party/jni-lib-merge/jni_lib_merge.h
)
target_link_options(${target_name} PRIVATE -Wl,--defsym=JNI_OnLoad=JNI_OnLoad_Weak)
endfunction()
我们从此函数的注释中得到了惊喜!这里通过编译器的一些参数,在rninstance 编译时强制包含了一个头文件jni_lib_merge.h,这个头文件的作用是将原来的JNI_OnLoad函数给重命名了。
cpp
#define JNI_OnLoad \
/* Need to make JNI_OnLoad weak, which requires splitting it into */ \
/* a declaration and definition, which is a little risky. */ \
/* Hopefully they didn't use 'extern "C"'. */ \
JNI_OnLoad_Weak(JavaVM *vm, void *reserved) __attribute__((weak)); \
\
/* We rename the declared JNI_OnLoad to this so we can call it */ \
/* from either our weak JNI_OnLoad or our merge-friendly init function. */ \
static jint pre_merge_original_JNI_OnLoad(JavaVM *vm, void *reserved); \
\
/* Merge-friendly wrapper for the original JNI_OnLoad, called by JNI. */ \
/* Return non-zero to indicate failure. */ \
static inline jint pre_merge_jni_library_wrapper_for_JNI_OnLoad(JNIEnv *env, jclass clazz) \
{ \
(void)clazz; \
JNI_MERGE_PRINT("In JNI_OnLoad wrapper for %s", ORIGINAL_SONAME); \
/* Note: relying on SoLoader's synchronization for thread-safety. */ \
static char already_loaded = 0; \
if (already_loaded) { \
return 0; \
} \
already_loaded = 1; \
JavaVM *vm; \
jint ret = JNI_MERGE_GET_JAVA_VM(env, vm); \
if (ret < 0) { \
/* Exception already thrown. */ \
return -1; \
} \
JNI_MERGE_PRINT("Calling original JNI_OnLoad for %s", ORIGINAL_SONAME); \
ret = pre_merge_original_JNI_OnLoad(vm, NULL); \
if (!(ret == JNI_VERSION_1_2 || ret == JNI_VERSION_1_4 || ret == JNI_VERSION_1_6)) { \
return -1; \
} \
return 0; \
} \
\
jint JNI_OnLoad_Weak(JavaVM *vm, void *reserved) \
{ \
/* This path will be taken if we're not merged. */ \
/* Just call the original JNI_OnLoad. */ \
JNI_MERGE_PRINT("Calling original (unmerged) JNI_OnLoad for %s", ORIGINAL_SONAME); \
return pre_merge_original_JNI_OnLoad(vm, reserved); \
} \
\
/* Register our name and wrapper in the proper section. */ \
static struct pre_merge_jni_library pre_merge_jni_library_register_object ATTRIBUTE_RETAIN \
__attribute__((__section__("pre_merge_jni_libraries"))) __attribute__((no_sanitize("address"))) \
__attribute__((__used__)) = { \
.name = ORIGINAL_SONAME, \
.onload_func = pre_merge_jni_library_wrapper_for_JNI_OnLoad, \
}; \
\
/* Re-start the JNI_OnLoad prototype to capture the body. */ \
static jint pre_merge_original_JNI_OnLoad
在这个头文件中定义了一个宏JNI_OnLoad 。我们知道C/C++中宏的作用就是在编译前的预处理阶段搞字符串替换。很显然,这里就是将OnLoad.cpp中的JNI_OnLoad函数定义给字符串替换了。替换的内容就是它下面一大串本文。我们要注意这段宏的最后一句static jint pre_merge_original_JNI_OnLoad,它既没有分号也没有括号,显然是替换后与一部分原始内容进行字符串拼接。
我们先看一下OnLoad.cpp的原始内容:
cpp
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* /*unused*/) {
return facebook::jni::initialize(vm, [] {
facebook::react::JReactMarker::setLogPerfMarkerIfNeeded();
facebook::react::JReactInstance::registerNatives();
facebook::react::JJSTimerExecutor::registerNatives();
facebook::react::JReactHostInspectorTarget::registerNatives();
});
}
接下来,我们大致复现一下宏展开之后,完成字符串替换拼接后的样子:
cpp
JNIEXPORT jint JNICALL
// ===== 宏展开开始 =====
JNI_OnLoad_Weak(JavaVM *vm, void *reserved) __attribute__((weak));
static jint pre_merge_original_JNI_OnLoad(JavaVM *vm, void *reserved);
static inline jint pre_merge_jni_library_wrapper_for_JNI_OnLoad(JNIEnv *env, jclass clazz) {
// ... 包装函数实现 ...
}
jint JNI_OnLoad_Weak(JavaVM *vm, void *reserved) {
return pre_merge_original_JNI_OnLoad(vm, reserved);
}
static struct pre_merge_jni_library pre_merge_jni_library_register_object
__attribute__((__section__("pre_merge_jni_libraries"))) = {
.name = "librninstance.so",
.onload_func = pre_merge_jni_library_wrapper_for_JNI_OnLoad,
};
static jint pre_merge_original_JNI_OnLoad // 宏的最后一行,没有分号
// ===== 宏展开结束 =====
(JavaVM* vm, void* /*unused*/) { // 与原始代码的参数列表进行拼接
return facebook::jni::initialize(vm, [] {
facebook::react::JReactMarker::setLogPerfMarkerIfNeeded();
facebook::react::JReactInstance::registerNatives();
facebook::react::JJSTimerExecutor::registerNatives();
facebook::react::JReactHostInspectorTarget::registerNatives();
});
}
这里的宏替换十分巧妙!看到了吗,不仅在函数之前注入了大量内容,最后更是直接将原始函数JNI_OnLoad给重命名为pre_merge_original_JNI_OnLoad,这是一个static修饰的私有函数,并不导出到全局,不存在命名冲突。这里还有一个地方需要注意,就是JNI_OnLoad_Weak(JavaVM *vm, void *reserved) __attribute__((weak))的声明,这里又玩了一个黑科技,将一个函数声明为弱符号。我们知道这个宏是通用的,那么其他模块也会引入,这又会导致全局函数命名冲突,因为每个模块都定义了全局函数JNI_OnLoad_Weak。弱符号声明是一个编译器扩展特性,微软的MSVC编译器就不支持,所以这并不是语言规范,可以说完全是一个黑科技。弱符号的特点就是当存在多个相同的弱符号时不会存在符号冲突,而如果存在一个相同的强符号时,那么强符号自动覆盖弱符号。并且默认的函数定义都是强符号。
所以,这里各个模块的JNI_OnLoad_Weak 定义都是弱符号,在reactnative 库的某个地方,肯定存在一个JNI_OnLoad_Weak 强符号,最终编译时覆盖这些弱符号。这个强符号实现就在react-native/packages/react-native/ReactAndroid/src/main/jni/first-party/jni-lib-merge/jni_lib_merge.c中。
接下来看一下pre_merge_original_JNI_OnLoad函数的实现,具体是facebook::react::JReactInstance::registerNatives()这行,显然这是用于注册Native方法的,我们找到函数的实现:
cpp
// JReactInstance.cpp
void JReactInstance::registerNatives() {
registerHybrid({
makeNativeMethod("initHybrid", JReactInstance::initHybrid),
makeNativeMethod(
"loadJSBundleFromAssets", JReactInstance::loadJSBundleFromAssets),
makeNativeMethod(
"loadJSBundleFromFile", JReactInstance::loadJSBundleFromFile),
makeNativeMethod(
"getJSCallInvokerHolder", JReactInstance::getJSCallInvokerHolder),
makeNativeMethod(
"getNativeMethodCallInvokerHolder",
JReactInstance::getNativeMethodCallInvokerHolder),
makeNativeMethod(
"callFunctionOnModule", JReactInstance::callFunctionOnModule),
// 省略......
});
}
现在我们终于找到了JNI Native方法注册的地方,可以看到,上层Kotlin类的 "initHybrid"方法调用,指向了JReactInstance::initHybrid,其他的Native方法也都在此处注册。
现在基本调用流程我们都完成了闭环。但是,如果我们继续深入探究就会发现,在reactnative 库中根本搜不到JNI_OnLoad 方法的声明。这让我一开始也感觉到十分困惑,但我直觉这肯定又是搞了什么黑科技。再次仔细查看SoMerging-utils.cmake构建脚本,果然有所发现:
cmake
# 省略......
target_link_options(${target_name} PRIVATE -Wl,--defsym=JNI_OnLoad=JNI_OnLoad_Weak)
这里又通过编译器参数--defsym=JNI_OnLoad=JNI_OnLoad_Weak 搞了一个符号别名,直接将 JNI_OnLoad 符号定义为 JNI_OnLoad_Weak 的别名,这就相当于JNI_OnLoad_Weak函数等同于JNI_OnLoad。
好了,就到处为止。这里面其实还有黑科技,譬如主库reactnative 是如何遍历调用各模块私有的pre_merge_original_JNI_OnLoad函数的?__section__("pre_merge_jni_libraries")又是什么?这些可以自己探究。