ReactNative新架构之Android端TurboModule机制完全解析
前言
注意,本文是基于React Native 0.83版本源码进行分析。
在《React Native新架构之Android端初始化源码分析》一文已经剖析了启动流程,但上次略过了TurboModule系统,现在就详细分析一下TurboModule系统。
TurboModule 初始化
我们先回顾一下源码react-native/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.kt
kotlin
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 {
// 省略......
// 设置 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)
// 创建 TurboModuleManagerDelegate
val turboModuleManagerDelegate =
delegate.turboModuleManagerDelegateBuilder
.setPackages(reactPackages)
.setReactApplicationContext(context)
.build()
val unbufferedRuntimeExecutor = getUnbufferedRuntimeExecutor()
// 创建 TurboModuleManager
turboModuleManager =
TurboModuleManager( // 使用 unbuffered RuntimeExecutor 来安装绑定
unbufferedRuntimeExecutor,
turboModuleManagerDelegate,
getJSCallInvokerHolder(),
getNativeMethodCallInvokerHolder(),
)
Systrace.endSection(Systrace.TRACE_TAG_REACT)
// 省略......
}
// 省略......
}
以上代码,在ReactInstance对象构造时立即在 init {} 中创建 TurboModuleManager。但需要注意一点,这里注入的CoreReactPackage平台相关的一些内部模块,而reactPackages.addAll(delegate.reactPackages)添加的则是在MainApplication中注册的本地Turbo Module实现:
kotlin
class MainApplication : Application(), ReactApplication {
override val reactHost: ReactHost by
lazy(LazyThreadSafetyMode.NONE) {
getDefaultReactHost(
context = applicationContext,
packageList =
PackageList(this).packages.apply {
// 例如,目前还无法自动链接的软件包可以手动添加到这里:
// add(MyReactNativePackage())
},
)
}
// 省略......
}
这里需要重点留意的是PackageList(this).packages这行代码,PackageList是工具自动生成的代码,主要依靠Gradle 插件机制来收集和链接三方TurboModule模块,其中主要是Gradle脚本代码,为了不中断代码分析的思路,所以这部分的详细分析我放到本文的最后。
TurboModuleManager的初始化
源码react-native/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/turbomodule/core/TurboModuleManager.kt
kotlin
/**
* 这是 TurboModules 的主类和入口点。注意,这是一个混合类,
* 它有一个 C++ 对应类。此类安装 JSI 绑定。它还实现了获取 Java 模块的方法,该方法由 C++ 对应类调用。
*/
@OptIn(FrameworkAPI::class)
public class TurboModuleManager(
runtimeExecutor: RuntimeExecutor,
private val delegate: TurboModuleManagerDelegate?,
jsCallInvokerHolder: CallInvokerHolder,
nativeMethodCallInvokerHolder: NativeMethodCallInvokerHolder,
) : TurboModuleRegistry {
public override val eagerInitModuleNames: List<String>
private val turboModuleProvider: ModuleProvider
private val legacyModuleProvider: ModuleProvider
// 模块清理锁(防止在清理时创建新模块)
private val moduleCleanupLock = Object()
@GuardedBy("moduleCleanupLock")
private var moduleCleanupStarted = false
// 模块缓存:moduleName -> ModuleHolder
@GuardedBy("moduleCleanupLock")
private val moduleHolders = mutableMapOf<String, ModuleHolder>()
// 1. 创建 C++ HybridData(JNI 桥接)
@DoNotStrip
private val mHybridData: HybridData = initHybrid(
runtimeExecutor,
jsCallInvokerHolder as CallInvokerHolderImpl,
nativeMethodCallInvokerHolder as NativeMethodCallInvokerHolderImpl,
delegate,
)
init {
// 2. 安装 JSI Bindings 到 JavaScript Runtime
installJSIBindings(shouldEnableLegacyModuleInterop())
// 3. 获取需要预加载的模块列表
eagerInitModuleNames = delegate?.getEagerInitModuleNames() ?: emptyList()
// 4. 创建 TurboModule 提供者
val nullProvider = ModuleProvider { _: String -> null }
turboModuleProvider = if (delegate == null) nullProvider
else ModuleProvider { moduleName: String ->
delegate.getModule(moduleName) as NativeModule?
}
// 5. 创建 Legacy Module 提供者(兼容旧架构)
// 省略......
}
companion object {
private const val TAG = "TurboModuleManager"
init {
// 加载 C++ 库
SoLoader.loadLibrary("turbomodulejsijni")
}
}
}
可以看到,TurboModuleManager实现了TurboModuleRegistry接口,我们可以先看一下该接口了解大致的功能:
kotlin
/**
* 用于创建和检索 NativeModule 的接口。
*
* 为什么这个接口要以 "Turbo" 作为前缀,即使它同时支持 Legacy NativeModule 和 TurboModule?
* 因为已经存在一个 NativeModuleRegistry(旧架构的一部分)。
* 一旦删除了那个类,我们应该相应地重命名此接口。
*/
public interface TurboModuleRegistry {
/**
* 返回名为 `moduleName` 的 NativeModule 实例。
* 如果 `moduleName` 对应的 TurboModule 尚未实例化,则实例化它。
* 如果没有注册名为 `moduleName` 的 TurboModule,则返回 null。
*/
public fun getModule(moduleName: String): NativeModule?
/** 获取所有已实例化的 NativeModule。*/
public val modules: Collection<NativeModule>
/** 检查名为 `moduleName` 的 NativeModule 是否已被实例化。*/
public fun hasModule(moduleName: String): Boolean
/**
* 返回所有应该被预先初始化的 NativeModule 的名称列表。
* 通过对每个名称调用 getModule,应用程序可以预先初始化这些 NativeModule。
*/
public val eagerInitModuleNames: List<String>
/**
* 在 ReactHost 关闭过程中调用。
* 此方法在 React Native 停止之前被调用。
*/
public fun invalidate()
}
接下来仔细分析TurboModuleManager的初始化。initHybrid和installJSIBindings都是Native方法,我们稍后分析,先看看delegate?.getEagerInitModuleNames()返回的预加载模块是什么。这里的delegate是外部传入的TurboModuleManagerDelegate实例,在ReactInstance初始化中创建:
kotlin
val turboModuleManagerDelegate = delegate.turboModuleManagerDelegateBuilder
.setPackages(reactPackages)
.setReactApplicationContext(context)
.build()
我们知道ReactInstance中的delegate是DefaultReactHostDelegate实例,而DefaultReactHostDelegate中的turboModuleManagerDelegateBuilder亦是外部传入:
kotlin
// DefaultReactHost.kt
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,
)
再看react-native/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultTurboModuleManagerDelegate.kt
kotlin
override fun build(
context: ReactApplicationContext,
packages: List<ReactPackage>,
): DefaultTurboModuleManagerDelegate =
DefaultTurboModuleManagerDelegate(
context,
packages,
cxxReactPackageProviders.flatMap { provider -> provider(context) },
)
那么delegate?.getEagerInitModuleNames()调用中的delegate实际上就是DefaultTurboModuleManagerDelegate。但这里要注意一下,delegate.turboModuleManagerDelegateBuilder.setPackages(reactPackages).setReactApplicationContext(context).build()实际上调用的是父类中的不带参数的build方法react-native/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactPackageTurboModuleManagerDelegate.kt:
kotlin
public fun build(): ReactPackageTurboModuleManagerDelegate {
val nonNullContext =
requireNotNull(context) {
"The ReactApplicationContext must be provided to create ReactPackageTurboModuleManagerDelegate"
}
val nonNullPackages =
requireNotNull(packages) {
"A set of ReactPackages must be provided to create ReactPackageTurboModuleManagerDelegate"
}
return build(nonNullContext, nonNullPackages)
}
由父类的build再调用其子类实现的带两个参数的build方法。所以DefaultTurboModuleManagerDelegate构造时传入的packages实际上就是我们前面在ReactInstance中分析的reactPackages。
但DefaultTurboModuleManagerDelegate实际上没实现getEagerInitModuleNames,我们来看父类的实现:
kotlin
// ReactPackageTurboModuleManagerDelegate.kt
protected constructor(
reactApplicationContext: ReactApplicationContext,
packages: List<ReactPackage>,
hybridData: HybridData,
) : super(hybridData) {
initialize(reactApplicationContext, packages)
}
override fun getEagerInitModuleNames(): List<String> = buildList {
for (moduleProvider in moduleProviders) {
for (moduleInfo in packageModuleInfos[moduleProvider]?.values ?: emptyList()) {
if (moduleInfo.isTurboModule && moduleInfo.needsEagerInit) {
add(moduleInfo.name)
}
}
}
}
private fun initialize(
reactApplicationContext: ReactApplicationContext,
packages: List<ReactPackage>,
) {
val applicationContext: ReactApplicationContext = reactApplicationContext
for (reactPackage in packages) {
/**
* BaseReactPackage(新架构,推荐方式)
*
* BaseReactPackage 是专为新架构设计的抽象类,特点:
* - 支持懒加载:模块只在需要时才创建(通过 getModule(name))
* - 提供模块元信息:通过 getReactModuleInfoProvider() 获取预定义的模块信息
* - 性能优化:避免启动时创建所有模块,减少内存占用和启动时间
*/
if (reactPackage is BaseReactPackage) {
val moduleProvider = ModuleProvider { moduleName: String ->
reactPackage.getModule(moduleName, applicationContext)
}
moduleProviders.add(moduleProvider)
packageModuleInfos[moduleProvider] =
reactPackage.getReactModuleInfoProvider().getReactModuleInfos()
continue
}
// 省略过时的旧架构代码......
}
}
通过对以上代码分析,流程就很清晰了。它在ReactPackageTurboModuleManagerDelegate的构造方法中调用initialize方法进行初始化。
核心职责是:
- 遍历所有 ReactPackage,为每个包创建 ModuleProvider(模块提供者),用于按需创建 NativeModule
- 收集并缓存所有模块的元信息(ReactModuleInfo),包括模块名、类型、是否 TurboModule 等
初始化完成后,那么接下来的getEagerInitModuleNames调用就很好理解,主要就是返回所有标记为 needsEagerInit = true 的 TurboModule 模块名列表,这些模块会在 ReactInstance 初始化后立即创建,而不是等到首次使用时才创建。
之所以需要预加载,是因为某些模块必须在应用启动时立即初始化,从而避免首次使用时创建模块导致的延迟和卡顿,同时确保关键基础设施模块在 JS bundle 加载前就准备好。
C++ 侧的初始化
现在我们来分析TurboModuleManager中的Native方法initHybrid。首先该类加载的动态库是SoLoader.loadLibrary("turbomodulejsijni"),所以对应的JNI实现,肯定位于turbomodulejsijni库中,其次上层Kotlin类和Native层C++类是一种映射关系,所以C++中也应该有一个对应类叫TurboModuleManager,这也是我们在启动流程里面分析过的,使用fbjni 的原因。根据这两个条件,就能准确定位到此处initHybrid的具体实现(注意,全局搜索initHybrid会发现有很多同名方法的)。
头文件源码react-native/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/TurboModuleManager.h
c++
static jni::local_ref<jhybriddata> initHybrid(
jni::alias_ref<jhybridobject> /* unused */,
jni::alias_ref<JRuntimeExecutor::javaobject> runtimeExecutor,
jni::alias_ref<CallInvokerHolder::javaobject> jsCallInvokerHolder,
jni::alias_ref<NativeMethodCallInvokerHolder::javaobject> nativeMethodCallInvokerHolder,
jni::alias_ref<TurboModuleManagerDelegate::javaobject> delegate);
CPP源码react-native/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/TurboModuleManager.cpp
c++
TurboModuleManager::TurboModuleManager(
RuntimeExecutor runtimeExecutor,
std::shared_ptr<CallInvoker> jsCallInvoker,
std::shared_ptr<NativeMethodCallInvoker> nativeMethodCallInvoker,
jni::alias_ref<TurboModuleManagerDelegate::javaobject> delegate)
: runtimeExecutor_(std::move(runtimeExecutor)),
jsCallInvoker_(std::move(jsCallInvoker)),
nativeMethodCallInvoker_(std::move(nativeMethodCallInvoker)),
delegate_(jni::make_global(delegate)) {}
jni::local_ref<TurboModuleManager::jhybriddata> TurboModuleManager::initHybrid(
jni::alias_ref<jhybridobject> /* unused */,
jni::alias_ref<JRuntimeExecutor::javaobject> runtimeExecutor,
jni::alias_ref<CallInvokerHolder::javaobject> jsCallInvokerHolder,
jni::alias_ref<NativeMethodCallInvokerHolder::javaobject>
nativeMethodCallInvokerHolder,
jni::alias_ref<TurboModuleManagerDelegate::javaobject> delegate) {
return makeCxxInstance(
runtimeExecutor->cthis()->get(),
jsCallInvokerHolder->cthis()->getCallInvoker(),
nativeMethodCallInvokerHolder->cthis()->getNativeMethodCallInvoker(),
delegate);
}
void TurboModuleManager::registerNatives() {
registerHybrid({
makeNativeMethod("initHybrid", TurboModuleManager::initHybrid),
makeNativeMethod(
"installJSIBindings", TurboModuleManager::installJSIBindings),
});
}
这里大量使用了fbjni 提供的机制,这对于熟悉和不熟悉JVM JNI机制的人都会造成一定程度混乱。首先我们理一理调用的流程,根据JNI的机制,当在Java类中加载动态库时,其动态库中的JNI_OnLoad就会被调用。这里有一份谷歌官方文档,详细介绍了JNI_OnLoad方法的使用,包括如何注册Native方法名等,JNI 文档。现在看到react-native/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/OnLoad.cpp
cpp
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* /*unused*/) {
return facebook::jni::initialize(vm, [] {
// TODO: dvacca ramanpreet unify this with the way
// "ComponentDescriptorFactory" is defined in Fabric
facebook::react::TurboModuleManager::registerNatives();
facebook::jni::registerNatives(
"com/facebook/react/internal/turbomodule/core/TurboModulePerfLogger",
{makeNativeMethod("jniEnableCppLogging", jniEnableCppLogging)});
});
}
现在流程就很清晰了,当kotlin中SoLoader.loadLibrary("turbomodulejsijni")加载时,JNI_OnLoad被调用,TurboModuleManager中的静态函数registerNatives被调用,注册了一个Native方法initHybrid,并将其与TurboModuleManager的静态方法initHybrid进行映射。
现在,当Kotlin中的Native方法initHybrid调用时,就会调用TurboModuleManager的静态函数initHybrid。接下来,initHybrid的实现中,调用了fbjni 提供的makeCxxInstance函数。其内部实现如下:
c++
static local_ref<detail::HybridData> makeHybridData(
std::unique_ptr<T> cxxPart) {
auto hybridData = detail::HybridData::create();
setNativePointer(hybridData, std::move(cxxPart));
return hybridData;
}
template <typename... Args>
static local_ref<detail::HybridData> makeCxxInstance(Args&&... args) {
return makeHybridData(
std::unique_ptr<T>(new T(std::forward<Args>(args)...)));
}
这是两个模版函数。其主要的功能就是做了三件事:
- 根据模版类型创建 C++ 对象
- 创建
HybridData包装器来包装 C++ 对象 - 返回 JNI 本地引用。
local_ref就是一个智能指针。
到这里C++的initHybrid就很清楚了,主要就是创建了一个C++的TurboModuleManager对象,并使用了 HybridData 包装返回。
该方法的几个参数:
| 参数 | 类型 | 作用 |
|---|---|---|
| runtimeExecutor | JRuntimeExecutor |
在 JS 线程上执行代码的执行器 |
| jsCallInvokerHolder | CallInvokerHolder |
持有从 C++ 调用 JS 的调用器 |
| nativeMethodCallInvokerHolder | NativeMethodCallInvokerHolder |
持有从 JS 调用 Native 的调用器 |
| delegate | TurboModuleManagerDelegate |
提供 TurboModule 实例的委托对象 |
接下来在Kotlin中调用的installJSIBindings方法,自然也是对应到C++类中的静态函数:
c++
// TurboModuleManager.cpp
void TurboModuleManager::installJSIBindings(
jni::alias_ref<jhybridobject> javaPart, // ← Java 端 TurboModuleManager 对象引用
bool shouldCreateLegacyModules) { // ← 是否创建旧架构模块的标志
auto cxxPart = javaPart->cthis(); // ← 获取 C++ 端对象指针
if (cxxPart == nullptr || !cxxPart->jsCallInvoker_) {
return; // 连接到 Chrome 调试器时,运行时不存在。
}
cxxPart->runtimeExecutor_([javaPart = jni::make_global(javaPart),
shouldCreateLegacyModules](jsi::Runtime& runtime) {
// 注意,此 lambda中已经线程切换了, 是在 JS 线程上执行的
TurboModuleBinding::install( // ← 在 JS Runtime 中安装绑定
runtime,
createTurboModuleProvider(javaPart, &runtime),
shouldCreateLegacyModules ? createLegacyModuleProvider(javaPart)
: nullptr);
});
}
TurboModuleProviderFunctionType TurboModuleManager::createTurboModuleProvider(
jni::alias_ref<jhybridobject> javaPart,
jsi::Runtime* runtime) {
return [runtime, weakJavaPart = jni::make_weak(javaPart)](
const std::string& name) -> std::shared_ptr<TurboModule> {
auto javaPart = weakJavaPart.lockLocal(); // ← 尝试获取强引用
if (!javaPart) {
return nullptr; // ← Java 对象已被回收
}
auto cxxPart = javaPart->cthis();
if (cxxPart == nullptr) {
return nullptr; // ← C++ 对象已被销毁
}
return cxxPart->getTurboModule(javaPart, name, *runtime); // ← 获取模块实例
};
}
可以看到,createTurboModuleProvider方法返回的是一个闭包,也就是C++中的lambda函数。此闭包的返回值是TurboModule类型,可见,如何查找调用TurboModule的核心逻辑,肯定就在cxxPart->getTurboModule(javaPart, name, *runtime)这行,这里的getTurboModule方法,就是当前C++ TurboModuleManager对象中的方法。此处我们暂时略过,放到下一节TurboModule 调用流程详细分析。
现在我们应该看看TurboModuleBinding::install函数做了什么。
源码react-native/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.cpp
c++
void TurboModuleBinding::install(
jsi::Runtime& runtime,
TurboModuleProviderFunctionType&& moduleProvider,
TurboModuleProviderFunctionType&& legacyModuleProvider,
std::shared_ptr<LongLivedObjectCollection> longLivedObjectCollection) {
// TODO(T208105802): We can get this information from the native side!
auto isBridgeless = runtime.global().hasProperty(runtime, "RN$Bridgeless");
if (!isBridgeless) {
runtime.global().setProperty(
runtime,
"__turboModuleProxy",
// 旧架构省略......
);
return;
}
// 新架构(Bridgeless 模式),安装 nativeModuleProxy HostObject
defineReadOnlyGlobal(
runtime,
"nativeModuleProxy", // ← 全局对象名
jsi::Object::createFromHostObject(
runtime,
std::make_shared<BridgelessNativeModuleProxy>(
runtime,
std::move(moduleProvider),
std::move(legacyModuleProvider),
longLivedObjectCollection)));
}
此处的defineReadOnlyGlobal函数就涉及到JSI接口调用了。JSI之于JS 引擎,就相当于JNI之于JVM。JSI就是打通上层JS代码与底层C++互相调用的引擎接口框架。对于JSI接口的详细分析,会再单独的篇章介绍。这里只需要知道,此方法在JS运行时定义了一个只读全局属性,此方法等价于以下JS代码:
javascript
Object.defineProperty(global, propName, {
value: value,
writable: false, // ← 不可写(只读)
enumerable: false, // ← 不可枚举(隐藏)
configurable: false // ← 不可配置(不可删除/修改描述符)
});
那么这里,就是在JS全局定义了一个只读全局属性nativeModuleProxy,此变量的内部类型是BridgelessNativeModuleProxy。也就是说,上层JS使用nativeModuleProxy时,其实就是在调用底层C++的BridgelessNativeModuleProxy对象。
总的来说,TurboModuleBinding::install的作用就是创建了一个BridgelessNativeModuleProxy对象,其持有moduleProvider闭包,然后注册给上层JavaScript使用。此方法有个地方需要留意,在旧架构时,其定义的全局对象是__turboModuleProxy。
TurboModule 调用流程
先回顾一下官方给出的编写TurboModule的规范:
ts
import type {TurboModule} from 'react-native';
import {TurboModuleRegistry} from 'react-native';
export interface Spec extends TurboModule {
setItem(value: string, key: string): void;
getItem(key: string): string | null;
}
export default TurboModuleRegistry.getEnforcing<Spec>(
'NativeLocalStorage',
) as Spec;
第一步是编写一个继承自TurboModule的接口,描述我们想要定义的方法。最后是通过export default导出了一个Spec类型的实现对象。外部使用此模块的代码,只需要导入这个对象就可以直接调用我们接口声明的这些方法。现在的关键是分析一下getEnforcing方法做了些什么。
查看源码react-native/packages/react-native/Libraries/TurboModule/TurboModuleRegistry.js:
js
import type {TurboModule} from './RCTExport';
import invariant from 'invariant';
const NativeModules = require('../BatchedBridge/NativeModules').default;
const turboModuleProxy = global.__turboModuleProxy;
function requireModule<T: TurboModule>(name: string): ?T {
if (turboModuleProxy != null) {
const module: ?T = turboModuleProxy(name);
if (module != null) {
return module;
}
}
const legacyModule: ?T = NativeModules[name];
if (legacyModule != null) {
return legacyModule;
}
return null;
}
export function get<T: TurboModule>(name: string): ?T {
return requireModule<T>(name);
}
export function getEnforcing<T: TurboModule>(name: string): T {
const module = requireModule<T>(name);
invariant(
module != null,
`TurboModuleRegistry.getEnforcing(...): '${name}' could not be found. ` +
'Verify that a module by this name is registered in the native binary.',
);
return module;
}
getEnforcing方法实际上是调用的requireModule来查找模块,requireModule方法实现需要注意,其中turboModuleProxy是旧架构的机制。这点我们在前面TurboModuleBinding::install方法分析时就知道了。但此处的实现代码仍然具有误导性,既然turboModuleProxy在新架构不存在,那么就应该执行const legacyModule: ?T = NativeModules[name]这行来查找模块,但其变量命名看,是加载旧架构模块。实际上此处是兼容代码,同时兼容新旧架构。想要洞悉其中玄机,我们需要阅读react-native/packages/react-native/Libraries/BatchedBridge/NativeModules.js的实现源码:
js
let NativeModules: {[moduleName: string]: any, ...} = {};
if (global.nativeModuleProxy) {
NativeModules = global.nativeModuleProxy;
} else {
// 旧架构代码省略......
}
export default NativeModules;
可以看到,这里NativeModules变量的声明相当于一个三目表达式,当global.nativeModuleProxy属性存在时,直接返回nativeModuleProxy对象,否则加载旧架构的模块。
到此就豁然开朗了,TurboModuleRegistry.getEnforcing<Spec>( 'NativeLocalStorage')调用实际上就等价于global.nativeModuleProxy['NativeLocalStorage']
我们已经知道nativeModuleProxy实际上就是C++类BridgelessNativeModuleProxy的实例,其也定义在TurboModuleBinding.cpp中:
c++
// TurboModuleBinding.cpp
class BridgelessNativeModuleProxy : public jsi::HostObject {
TurboModuleBinding turboBinding_;
std::unique_ptr<TurboModuleBinding> legacyBinding_;
public:
BridgelessNativeModuleProxy(
jsi::Runtime& runtime,
TurboModuleProviderFunctionType&& moduleProvider,
TurboModuleProviderFunctionType&& legacyModuleProvider,
std::shared_ptr<LongLivedObjectCollection> longLivedObjectCollection)
: turboBinding_(
runtime,
std::move(moduleProvider),
longLivedObjectCollection),
legacyBinding_(
legacyModuleProvider ? std::make_unique<TurboModuleBinding>(
runtime,
std::move(legacyModuleProvider),
longLivedObjectCollection)
: nullptr) {}
jsi::Value get(jsi::Runtime& runtime, const jsi::PropNameID& name) override {
/**
* BatchedBridge/NativeModules.js 包含这一行:
*
* module.exports = global.nativeModuleProxy
*
* 这意味着 NativeModuleProxy 作为模块从 'NativeModules.js' 导出。
* 每当某些 JavaScript 需要 'NativeModule.js' 时,
* Metro 检查此模块的 __esModule 属性以查看模块是否为 ES6 模块。
*
* 我们从此属性访问返回 false,这样我们可以在稍后发生的实际
* NativeModule require 上失败,这更具可操作性。
*/
std::string moduleName = name.utf8(runtime);
if (moduleName == "__esModule") {
return {false}; // 表示不是 ES6 模块
}
// 优先尝试从 TurboModule 系统获取模块
auto turboModule = turboBinding_.getModule(runtime, moduleName);
if (turboModule.isObject()) {
return turboModule;
}
// 回退到 Legacy Module 系统(兼容旧架构)
if (legacyBinding_) {
auto legacyModule = legacyBinding_->getModule(runtime, moduleName);
if (legacyModule.isObject()) {
return legacyModule;
}
}
// 模块不存在,返回 null
return jsi::Value::null();
}
// 省略......
};
这里继承自JSI接口提供的jsi::HostObject类型,这表示此C++类是可以直接被上层JS代码使用的对象类型。根据JSI的要求,如果一个C++类希望被JS直接调用,那么就要继承jsi::HostObject。
此类首先创建了一个TurboModuleBinding对象,当上层调用global.nativeModuleProxy['NativeLocalStorage']时,就会调用其get方法,作为新架构,接下来就会调用TurboModuleBinding的getModule来查找模块。
c++
// TurboModuleBinding.cpp
jsi::Value TurboModuleBinding::getModule(
jsi::Runtime& runtime,
const std::string& moduleName) const {
std::shared_ptr<TurboModule> module;
{
TraceSection s("TurboModuleBinding::moduleProvider", "module", moduleName);
module = moduleProvider_(moduleName); // ← 调用moduleProvider获取模块
}
if (module) {
TurboModuleWithJSIBindings::installJSIBindings(module, runtime);
// jsRepresentation 是什么?TurboModule 属性的缓存
// 此后,始终将缓存(即:jsRepresentation)返回给 JavaScript
//
// 如果在 TurboModule 上找到 jsRepresentation,则返回它。
//
// 注意:TurboModule 在 TurboModuleManager 中按名称缓存。因此,
// jsRepresentation 也由 TurboModuleManager 按名称缓存
auto& weakJsRepresentation = module->jsRepresentation_;
if (weakJsRepresentation) {
auto jsRepresentation = weakJsRepresentation->lock(runtime);
if (!jsRepresentation.isUndefined()) {
return jsRepresentation; // ← 返回缓存的 JS 对象
}
}
// 状态:在 TurboModule 上未找到 jsRepresentation
// 创建一个全新的 jsRepresentation,并将其附加到 TurboModule
jsi::Object jsRepresentation(runtime);
weakJsRepresentation =
std::make_unique<jsi::WeakObject>(runtime, jsRepresentation);
// 在属性访问时延迟填充 jsRepresentation。
//
// 这是如何工作的?
// 1. 最初 jsRepresentation 是空的:{}
// 2. 如果在 jsRepresentation 上的属性查找失败,JS 运行时将
// 搜索 jsRepresentation 的原型:jsi::Object(TurboModule)。
// 3. TurboModule::get(runtime, propKey) 执行。这会创建
// 属性,将其缓存在 jsRepresentation 上,然后将其返回给JavaScript
auto hostObject =
jsi::Object::createFromHostObject(runtime, std::move(module));
jsRepresentation.setProperty(runtime, "__proto__", std::move(hostObject));
return jsRepresentation;
} else {
return jsi::Value::null();
}
}
这里moduleProvider_正是我们前面略过的内容,在TurboModuleManager::createTurboModuleProvider方法中创建的一个闭包。此时传入模块名调用此闭包,闭包中主要还是调用TurboModuleManager::getTurboModule方法:
c++
// TurboModuleManager.cpp
std::shared_ptr<TurboModule> TurboModuleManager::getTurboModule(
jni::alias_ref<jhybridobject> javaPart,
const std::string& name,
jsi::Runtime& runtime) {
const char* moduleName = name.c_str();
TurboModulePerfLogger::moduleJSRequireBeginningStart(moduleName);
// 1. 检查 C++ 缓存
auto turboModuleLookup = turboModuleCache_.find(name);
if (turboModuleLookup != turboModuleCache_.end()) {
TurboModulePerfLogger::moduleJSRequireBeginningCacheHit(moduleName);
TurboModulePerfLogger::moduleJSRequireBeginningEnd(moduleName);
return turboModuleLookup->second; // ← 缓存命中,直接返回
}
TurboModulePerfLogger::moduleJSRequireBeginningEnd(moduleName);
// 2. 尝试获取 C++ TurboModule(通过 delegate)
auto cxxDelegate = delegate_->cthis();
auto cxxModule = cxxDelegate->getTurboModule(name, jsCallInvoker_);
if (cxxModule) {
turboModuleCache_.insert({name, cxxModule}); // 加入C++缓存
return cxxModule;
}
// 3. 尝试获取全局注册的 C++ TurboModule
auto& cxxTurboModuleMapProvider = globalExportedCxxTurboModuleMap();
auto it = cxxTurboModuleMapProvider.find(name);
if (it != cxxTurboModuleMapProvider.end()) {
auto turboModule = it->second(jsCallInvoker_);
turboModuleCache_.insert({name, turboModule});
return turboModule;
}
// 4. 调用 Java 层获取 Java TurboModule
static auto getTurboJavaModule = javaPart->getClass()
->getMethod<jni::alias_ref<JTurboModule>(const std::string&)>(
"getTurboJavaModule");
auto moduleInstance = getTurboJavaModule(javaPart.get(), name);
if (moduleInstance) {
TurboModulePerfLogger::moduleJSRequireEndingStart(moduleName);
// 5. 创建 JavaTurboModule 包装
JavaTurboModule::InitParams params = {
.moduleName = name,
.instance = moduleInstance,
.jsInvoker = jsCallInvoker_,
.nativeMethodCallInvoker = nativeMethodCallInvoker_};
auto turboModule = cxxDelegate->getTurboModule(name, params);
// 6. 处理 JSI Bindings(如果有)
if (moduleInstance->isInstanceOf(JTurboModuleWithJSIBindings::javaClassStatic())) {
static auto getBindingsInstaller =
JTurboModuleWithJSIBindings::javaClassStatic()
->getMethod<BindingsInstallerHolder::javaobject()>(
"getBindingsInstaller");
auto installer = getBindingsInstaller(moduleInstance);
if (installer) {
installer->cthis()->installBindings(runtime, jsCallInvoker_);
}
}
turboModuleCache_.insert({name, turboModule});
TurboModulePerfLogger::moduleJSRequireEndingEnd(moduleName);
return turboModule;
}
// 省略Legacy Module ......
return nullptr;
}
查找优先级:
- C++ 缓存 :
turboModuleCache_ - C++ TurboModule:通过 delegate
- 全局 C++ TurboModule :
globalExportedCxxTurboModuleMap() - Java TurboModule :首先通过 JNI 调用
getTurboJavaModule(),接着调用C++层的getTurboModule - Legacy C++ Module:兼容旧架构
C++ TurboModule
需要注意,React Native的TurboModule是分为两种的,一种是纯C++ TurboModule,另一种是基于原生的TurboModule。原生的TurboModule与平台相关,在Android平台使用Java/Kotlin 语言开发,在iOS上主要是ObjC/Swift开发。而纯C++ TurboModule 就是C++开发,开发一次就可以同时兼容两大平台。因此建议将一些通用的代码可以用纯C++开发,减少两份代码的维护成本。
此处逻辑很清晰,优先查找第三方注册的纯C++ TurboModule,其次找全局注册的C++ TurboModule,最后找Java开发的TurboModule(安卓平台Kotlin最终也是编译成Java字节码,等价于Java)。
先来分析一下C++ TurboModule的查找逻辑,这里的cxxDelegate是TurboModuleManagerDelegate类型,其具体实现类是DefaultTurboModuleManagerDelegate。
源码react-native/packages/react-native/ReactAndroid/src/main/jni/react/newarchdefaults/DefaultTurboModuleManagerDelegate.cpp
c++
std::shared_ptr<TurboModule> DefaultTurboModuleManagerDelegate::getTurboModule(
const std::string& name,
const std::shared_ptr<CallInvoker>& jsInvoker) {
// 1. 遍历所有注册的 CxxReactPackage
for (const auto& cxxReactPackage : cxxReactPackages_) {
auto cppPart = cxxReactPackage->cthis();
if (cppPart != nullptr) {
auto module = cppPart->getModule(name, jsInvoker);
if (module) {
return module;
}
}
}
// 2. 查找全局ModuleProvider
auto moduleProvider = DefaultTurboModuleManagerDelegate::cxxModuleProvider;
if (moduleProvider) {
auto module = moduleProvider(name, jsInvoker);
if (module) {
return module;
}
}
// 3. 查找默认系统模块
return DefaultTurboModules::getTurboModule(name, jsInvoker);
}
可以看到也是三级查找,首先遍历cxxReactPackages_,此值实际上是RN框架在初始化时传入的,具体可以回顾一下安卓初始化流程分析一文,实际上应该是空列表,因为一般也是在应用的Application处注册,如下cxxReactPackageProviders:
kotlin
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 {
//......
}
DefaultTurboModules::getTurboModule是查找的是RN框架内部定义的C++ TurboModule,重点需要关注一下DefaultTurboModuleManagerDelegate::cxxModuleProvider,这里是查找第三方的C++ TurboModule。
c++
// DefaultTurboModuleManagerDelegate.cpp
std::function<std::shared_ptr<TurboModule>(
const std::string&,
const std::shared_ptr<CallInvoker>&)>
DefaultTurboModuleManagerDelegate::cxxModuleProvider{nullptr};
可以看到cxxModuleProvider实际上是一个全局变量,只不过是一个函数类型的变量。那么此变量是在哪里赋值的呢?通过全局搜索,只有一处进行了设置,源码react-native/packages/react-native/ReactAndroid/cmake-utils/default-app-setup/OnLoad.cpp:
c++
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
return facebook::jni::initialize(vm, [] {
facebook::react::DefaultTurboModuleManagerDelegate::cxxModuleProvider =
&facebook::react::cxxModuleProvider;
facebook::react::DefaultTurboModuleManagerDelegate::javaModuleProvider =
&facebook::react::javaModuleProvider;
facebook::react::DefaultComponentsRegistry::
registerComponentDescriptorsFromEntryPoint =
&facebook::react::registerComponents;
});
}
std::shared_ptr<TurboModule> cxxModuleProvider(
const std::string& name,
const std::shared_ptr<CallInvoker>& jsInvoker) {
// Here you can provide your CXX Turbo Modules coming from
// either your application or from external libraries. The approach to follow
// is similar to the following (for a module called `NativeCxxModuleExample`):
//
// if (name == NativeCxxModuleExample::kModuleName) {
// return std::make_shared<NativeCxxModuleExample>(jsInvoker);
// }
// And we fallback to the CXX module providers autolinked
return autolinking_cxxModuleProvider(name, jsInvoker);
return nullptr;
}
这里其实就是在libappmodules.so加载时进行了初始化,最终是调用的autolinking_cxxModuleProvider来自动完成三方库的C++ TurboModule注册。毫无疑问,肯定又是基于codegen工具进行了自动代码生成,具体代码生成逻辑,放到后面再研究。但是查找逻辑我们已经非常清晰,最终就是创建了类似NativeCxxModuleExample这样的纯C++三方TurboModule对象。
Java TurboModule
接下来再看另一条线Java TurboModule的查找,可以分为两步,首先是Java层的调用,将返回的TurboModule实例封装到InitParams结构中作为参数,再次调用C++层的getTurboModule:
c++
// TurboModuleManager.cpp
static auto getTurboJavaModule = javaPart->getClass()->
getMethod<jni::alias_ref<JTurboModule>(const std::string&)>("getTurboJavaModule");
// Java层的调用
auto moduleInstance = getTurboJavaModule(javaPart.get(), name);
if (moduleInstance) {
TurboModulePerfLogger::moduleJSRequireEndingStart(moduleName);
JavaTurboModule::InitParams params = {
.moduleName = name,
.instance = moduleInstance,
.jsInvoker = jsCallInvoker_,
.nativeMethodCallInvoker = nativeMethodCallInvoker_};
// C++层的调用
auto turboModule = cxxDelegate->getTurboModule(name, params);
// 省略......
return turboModule;
}
Java层是通过JNI反射并调用kotlin类TurboModuleManager中的getTurboJavaModule方法:
kotlin
// TurboModuleManager.kt
@Suppress("unused")
@DoNotStrip
private fun getTurboJavaModule(moduleName: String): TurboModule? {
/*
* 这个 API 从 global.__turboModuleProxy 调用
* 只有当 native 模块是 turbo 模块时才调用 getModule
*/
if (!isTurboModule(moduleName)) {
return null
}
val module = getModule(moduleName)
return if (module !is CxxModuleWrapper && module is TurboModule) module else null
}
/**
* 返回与提供的 moduleName 对应的 NativeModule 实例。
*
* 此方法:
* - 如果模块尚不存在,则创建并初始化该模块
* - 在 TurboModuleManager 被销毁后返回 null
*/
override fun getModule(moduleName: String): NativeModule? {
val moduleHolder: ModuleHolder?
synchronized(moduleCleanupLock) {
if (moduleCleanupStarted) { // 检查标志,判断 TurboModuleManager 是否正在清理(调用了 invalidate())
/*
* 清理开始后总是返回 null,这样 getNativeModule(moduleName) 返回 null
*/
FLog.e(
TAG,
"getModule(): Tried to get module \"%s\", but TurboModuleManager was tearing down (legacy: %b, turbo: %b)",
moduleName,
isLegacyModule(moduleName),
isTurboModule(moduleName),
)
return null // 回 null,拒绝创建新模块
}
/*
* TODO(T64619790): 我们是否应该提前填充 moduleHolders,以避免必须控制对它的并发访问?
*/
if (!moduleHolders.containsKey(moduleName)) {
moduleHolders[moduleName] = ModuleHolder()
}
moduleHolder = moduleHolders[moduleName]
}
if (moduleHolder == null) {
FLog.e(TAG, "getModule(): Tried to get module \"%s\", but moduleHolder was null", moduleName)
return null
}
TurboModulePerfLogger.moduleCreateStart(moduleName, moduleHolder.moduleId)
// 获取或创建模块
val module = getOrCreateModule(moduleName, moduleHolder, true)
if (module != null) {
TurboModulePerfLogger.moduleCreateEnd(moduleName, moduleHolder.moduleId)
} else {
TurboModulePerfLogger.moduleCreateFail(moduleName, moduleHolder.moduleId)
}
return module
}
/**
* 给定一个 ModuleHolder 和 TurboModule 的 moduleName,返回 TurboModule 实例。
*
* 使用 ModuleHolder 来确保如果 n 个线程竞争创建 TurboModule x,那么只有
* 第一个线程创建 x。其他 n - 1 个线程会等待,直到 x 被创建并初始化完成。
*/
private fun getOrCreateModule(
moduleName: String,
moduleHolder: ModuleHolder,
shouldPerfLog: Boolean,
): NativeModule? {
var shouldCreateModule = false
synchronized(moduleHolder) {
if (moduleHolder.isDoneCreatingModule) {
if (shouldPerfLog) {
TurboModulePerfLogger.moduleCreateCacheHit(moduleName, moduleHolder.moduleId)
}
return moduleHolder.module
}
if (!moduleHolder.isCreatingModule) {
// 只有一个线程能到达这里。
shouldCreateModule = true
moduleHolder.startCreatingModule()
}
}
if (shouldCreateModule) {
TurboModulePerfLogger.moduleCreateConstructStart(moduleName, moduleHolder.moduleId)
var nativeModule = turboModuleProvider.getModule(moduleName)
if (nativeModule == null) {
// 回退到 Legacy
nativeModule = legacyModuleProvider.getModule(moduleName)
}
TurboModulePerfLogger.moduleCreateConstructEnd(moduleName, moduleHolder.moduleId)
TurboModulePerfLogger.moduleCreateSetUpStart(moduleName, moduleHolder.moduleId)
if (nativeModule != null) {
synchronized(moduleHolder) { moduleHolder.module = nativeModule }
/*
* TurboModuleManager 在 ReactApplicationContext 设置完成后初始化
* NativeModules 应该在 ReactApplicationContext 设置完成后初始化
* 因此,我们现在应该初始化 TurboModule
*/
nativeModule.initialize()
} else {
FLog.e(
TAG,
"getOrCreateModule(): Unable to create module \"%s\" (legacy: %b, turbo: %b)",
moduleName,
isLegacyModule(moduleName),
isTurboModule(moduleName),
)
}
// 标记创建完成并唤醒等待线程
TurboModulePerfLogger.moduleCreateSetUpEnd(moduleName, moduleHolder.moduleId)
synchronized(moduleHolder) {
moduleHolder.endCreatingModule()
(moduleHolder as Object).notifyAll()
}
return nativeModule
}
synchronized(moduleHolder) {
var wasInterrupted = false
while (moduleHolder.isCreatingModule) {
try {
// 等待直到 TurboModule 被创建和初始化
(moduleHolder as Object).wait()
} catch (e: InterruptedException) {
wasInterrupted = true
}
}
if (wasInterrupted) {
/*
* TurboModules 理想情况下应该快速创建和初始化。因此,
* 我们等到 TurboModule 完成初始化后再重新中断当前线程。
*/
Thread.currentThread().interrupt()
}
return moduleHolder.module
}
}
代码不难理解,我们概括一下流程:
markdown
┌──────────────────────────────────────────────────────────┐
│ 第一层:getTurboJavaModule │
├──────────────────────────────────────────────────────────┤
│ 核心步骤: │
│ 1. 检查是否是 TurboModule(类型验证) │
│ 2. 调用 getModule 获取模块 │
│ 3. 过滤返回:只返回 Java TurboModule │
└──────────────────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────────────┐
│ 第二层:getModule │
├──────────────────────────────────────────────────────────┤
│ 核心步骤: │
│ 1. 检查清理状态(拒绝创建) │
│ 2. 获取或创建 ModuleHolder(模块持有者) │
│ 3. 调用 getOrCreateModule 执行实际创建 │
│ 4. 记录性能日志 │
└──────────────────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────────────┐
│ 第三层:getOrCreateModule │
├──────────────────────────────────────────────────────────┤
│ 核心步骤: │
│ 1. 竞争检测:检查缓存或标记创建 │
│ 2. 创建逻辑(胜出线程): │
│ - 调用提供者创建模块实例 │
│ - 初始化模块 │
│ - 唤醒等待线程 │
│ 3. 等待逻辑(其他线程): │
│ - 等待创建完成 │
│ - 返回缓存的模块 │
└──────────────────────────────────────────────────────────┘
↓
返回 TurboModule 实例
最后,再来看一下turboModuleProvider.getModule方法的实现。在前面TurboModuleManager初始化一节我们已经知道了turboModuleProvider实际上是个闭包,内部其实就是调用的TurboModuleManagerDelegate类型的getModule方法返回Module。其具体实现则是在其子类ReactPackageTurboModuleManagerDelegate中:
kotlin
// ReactPackageTurboModuleManagerDelegate.kt
override fun getModule(moduleName: String): TurboModule? {
var resolvedModule: NativeModule? = null
for (moduleProvider in moduleProviders) {
val moduleInfo: ReactModuleInfo? = packageModuleInfos[moduleProvider]?.get(moduleName)
if (
moduleInfo?.isTurboModule == true &&
(resolvedModule == null || moduleInfo.canOverrideExistingModule)
) {
val module = moduleProvider.getModule(moduleName)
if (module != null) {
resolvedModule = module
}
}
}
// 跳过与 TurboModule 不兼容的模块
val isLegacyModule = resolvedModule !is TurboModule
if (isLegacyModule) {
return null
}
return resolvedModule as TurboModule
}
现在再看C++层的调用:
c++
// DefaultTurboModuleManagerDelegate.cpp
std::shared_ptr<TurboModule> DefaultTurboModuleManagerDelegate::getTurboModule(
const std::string& name,
const JavaTurboModule::InitParams& params) {
auto moduleProvider = DefaultTurboModuleManagerDelegate::javaModuleProvider;
if (moduleProvider) {
if (auto resolvedModule = moduleProvider(name, params)) {
return resolvedModule;
}
}
return nullptr;
}
javaModuleProvider是一个全局变量,跟前面分析C++ TurboModule的cxxModuleProvider一样,都在同一个地方赋值:
c++
// OnLoad.cpp
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
return facebook::jni::initialize(vm, [] {
facebook::react::DefaultTurboModuleManagerDelegate::cxxModuleProvider =
&facebook::react::cxxModuleProvider;
facebook::react::DefaultTurboModuleManagerDelegate::javaModuleProvider =
&facebook::react::javaModuleProvider;
// 省略......
});
}
std::shared_ptr<TurboModule> javaModuleProvider(
const std::string& name,
const JavaTurboModule::InitParams& params) {
// 提供自己的模块提供者。 遵循的方法类似于以下示例(对于名为 `samplelibrary`的库):
//
// auto module = samplelibrary_ModuleProvider(name, params);
// if (module != nullptr) {
// return module;
// }
// return rncore_ModuleProvider(name, params);
// 链接应用程序本地模块(如果可用)
#ifdef REACT_NATIVE_APP_MODULE_PROVIDER
auto module = REACT_NATIVE_APP_MODULE_PROVIDER(name, params);
if (module != nullptr) {
return module;
}
#endif
// 首先尝试查找 React Native 核心模块
if (auto module = FBReactNativeSpec_ModuleProvider(name, params)) {
return module;
}
// 回退到自动链接的模块提供者
if (auto module = autolinking_ModuleProvider(name, params)) {
return module;
}
return nullptr;
}
这里三方库依然是走的自动链接函数autolinking_ModuleProvider。显然这也是通过Codegen自动生成的代码,我们放到后面再一起研究。
基本到这里,结合我们前面对moduleProviders初始化的分析,整套安卓的TurboModule初始化及其调用流程都已经通了。剩下的,就是React Native是如何扫描并自动将项目依赖的第三方TurboModule进行收集注册。
TurboModule 注册
Autolinking 机制剖析
Autolinking 机制在安卓平台是基于Gradle 插件系统来实现自动处理的。
Android Gradle 构建机制
简要介绍一下Android Gradle 构建机制原理: Gradle 构建生命周期
scss
┌─────────────────────────────────────────────────────────────────┐
│ 1. Initialization 阶段 │
│ - 确定参与构建的项目 │
│ - 为每个项目创建 Project 实例 │
├─────────────────────────────────────────────────────────────────┤
│ 2. Settings 阶段 ← ReactSettingsPlugin 在这里执行 │
│ - 解析 settings.gradle(.kts) │
│ - 应用 Settings Plugin │
│ - 执行 autolinkLibrariesFromCommand() │
│ - 生成 autolinking.json │
│ - 动态添加子项目 (settings.include) │
├─────────────────────────────────────────────────────────────────┤
│ 3. Configuration 阶段 ← ReactPlugin 在这里执行 │
│ - 解析所有 build.gradle(.kts) │
│ - 应用 Project Plugin │
│ - 注册任务 (configureAutolinking) │
│ - 创建任务图(Task Graph) │
├─────────────────────────────────────────────────────────────────┤
│ 4. Execution 阶段 ← GeneratePackageListTask 在这里执行 │
│ - 执行任务 │
│ - 生成构建产物 │
└─────────────────────────────────────────────────────────────────┘
Settings Plugin 的特点:
-
项目结构控制:可以动态决定哪些项目参与构建
-
早期介入:在任何 Project Plugin 执行前就完成工作
-
全局视角:能够访问整个构建的 Settings 对象
-
依赖发现:适合做自动依赖发现和链接
ReactSettingsPlugin
ReactSettingsPlugin 是 Autolinking 机制的核心入口点,它通过 Gradle Settings Plugin 机制在项目配置的最早阶段介入,完成三方库的自动发现和链接。
ReactSettingsPlugin 架构层次:
scss
┌─────────────────────────────────────────────────────────────────┐
│ Gradle Settings Plugin 层 │
├─────────────────────────────────────────────────────────────────┤
│ ReactSettingsPlugin (插件入口) │
│ ↓ │
│ ReactSettingsExtension (功能实现) │
│ ↓ │
│ autolinkLibrariesFromCommand() (核心方法) │
└─────────────────────────────────────────────────────────────────┘
插件注册
源码react-native/packages/gradle-plugin/settings-plugin/build.gradle.kts
groovy
gradlePlugin {
plugins {
create("react.settings") {
id = "com.facebook.react.settings" // ← 插件 ID
implementationClass = "com.facebook.react.ReactSettingsPlugin" // ← 实现类
}
}
}
插件核心实现
源码react-native/packages/gradle-plugin/settings-plugin/src/main/kotlin/com/facebook/react/ReactSettingsPlugin.kt
kotlin
class ReactSettingsPlugin : Plugin<Settings> {
override fun apply(settings: Settings) {
// 关键:注册 ReactSettingsExtension 扩展
settings.extensions.create(
"reactSettings", // ← 扩展名称
ReactSettingsExtension::class.java, // ← 扩展实现类
settings // ← 注入 Settings 实例
)
}
}
- Settings Plugin:在 Gradle Settings 阶段执行,早于所有 Project Plugin
- Extension 注册 :创建
reactSettings扩展,供settings.gradle调用 - 依赖注入 :将
Settings实例注入到ReactSettingsExtension中
继续查看实现类react-native/packages/gradle-plugin/settings-plugin/src/main/kotlin/com/facebook/react/ReactSettingsExtension.kt
kotlin
abstract class ReactSettingsExtension @Inject constructor(val settings: Settings) {
// 输出文件路径
private val outputFile =
settings.layout.rootDirectory.file("build/generated/autolinking/autolinking.json").asFile
// 缓存目录
private val outputFolder =
settings.layout.rootDirectory.file("build/generated/autolinking/").asFile
private val defaultConfigCommand: List<String> =
windowsAwareCommandLine(listOf("npx", "@react-native-community/", "config")).map {
it.toString()
}
/**
* 使用外部命令作为权威数据,从而实现自动链接库的工具函数。
*
* 此方法应在 `settings.gradle` 文件中调用,它会确保 Gradle 项目加载所有发现的库。
*
* @param command 要执行的命令,用于获取自动链接配置。默认为
* `npx @react-native-community/cli config`。
* @param workingDirectory 执行命令的工作目录。
* @param lockFiles 要检查变化的锁文件列表(如果锁文件没有变化,则不会执行命令)。
*/
@JvmOverloads
public fun autolinkLibrariesFromCommand(
command: List<String> = defaultConfigCommand,
workingDirectory: File? = settings.layout.rootDirectory.dir("../").asFile,
lockFiles: FileCollection =
settings.layout.rootDirectory
.dir("../")
.files("yarn.lock", "package-lock.json", "package.json", "react-native.config.js"),
) {
// 第一步:创建输出目录
outputFile.parentFile.mkdirs()
// 第二步:创建命令执行配置
val updateConfig =
object : GenerateConfig {
override fun command(): List<String> = command
override fun execute(): Int {
// 执行 React Native CLI 命令
val execResult = settings.providers.exec { exec ->
exec.commandLine(command) // ← 执行 npx @react-native-community/cli config
exec.workingDir = workingDirectory // ← 设置工作目录
}
// 将 CLI 输出写入 autolinking.json
outputFile.writeText(execResult.standardOutput.asText.get())
return execResult.result.get().exitValue
}
}
// 第三步:检查缓存并更新配置
checkAndUpdateCache(updateConfig, outputFile, outputFolder, lockFiles)
// 第四步:链接发现的库
linkLibraries(getLibrariesToAutolink(outputFile))
}
// 省略......
}
检查缓存并更新配置主要是通过SHA计算和检查锁文件实现。这里我们重点研究一下第四步链接发现的库:
kotlin
// 提取可链接的库
internal fun getLibrariesToAutolink(buildFile: File): Map<String, File> {
val model = JsonUtils.fromAutolinkingConfigJson(buildFile) // ← 解析 JSON 配置
return model
?.dependencies
?.values
// 过滤:只处理有 Android 配置的依赖
?.filter { it.platforms?.android?.sourceDir != null }
// 过滤:跳过纯 C++ 依赖(没有 .gradle 文件)
?.filterNot { it.platforms?.android?.isPureCxxDependency == true }
// 映射:项目名 -> 源码目录
?.associate { deps ->
":${deps.nameCleansed}" to File(deps.platforms?.android?.sourceDir)
} ?: emptyMap()
}
private fun linkLibraries(input: Map<String, File>) {
input.forEach { (path, projectDir) ->
settings.include(path) // ← 包含子项目到 Gradle 构建
settings.project(path).projectDir = projectDir // ← 设置项目目录
}
}
可以看到,链接库时用到了之前注入的Settings实例,将依赖的那些三方库一个个添加到宿主工程中。
插件的引入
我们以React Native源码工程提供的helloworld 项目为例,看一下这个gradle插件是怎么被引入的,源码react-native/private/helloworld/android/settings.gradle
groovy
pluginManagement {
includeBuild("../../../packages/gradle-plugin")
}
plugins {
id("com.facebook.react.settings") // ← 应用 ReactSettingsPlugin
}
extensions.configure(com.facebook.react.ReactSettingsExtension) { ex ->
ex.autolinkLibrariesFromCommand() // ← 调用 autolinking 方法
}
完整调用流程图
css
┌─────────────────────────────────────────────────────────────────┐
│ 阶段 1: Gradle Settings 解析 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ settings.gradle 解析时: │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ 1. 应用 ReactSettingsPlugin │ │
│ │ ↓ │ │
│ │ 2. 注册 ReactSettingsExtension │ │
│ │ ↓ │ │
│ │ 3. 调用 autolinkLibrariesFromCommand() │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ 阶段 2: 缓存检查和 CLI 执行 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ checkAndUpdateCache(): │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ 1. isCacheDirty() 检查缓存是否失效 │ │
│ │ - 检查 autolinking.json 是否存在 │ │
│ │ - 检查锁文件 SHA 是否变化 │ │
│ │ - 检查配置模型是否有效 │ │
│ │ │ │
│ │ 2. 如果缓存失效,执行 updateConfig.execute() │ │
│ │ - 执行 npx @react-native-community/cli config │ │
│ │ - 将输出写入 autolinking.json │ │
│ │ - 更新锁文件 SHA 缓存 │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ 阶段 3: 库发现和链接 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ linkLibraries(getLibrariesToAutolink()): │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ 1. JsonUtils.fromAutolinkingConfigJson() 解析 JSON │ │
│ │ - 清理 CLI 输出中的调试信息 │ │
│ │ - 反序列化为 ModelAutolinkingConfigJson │ │
│ │ │ │
│ │ 2. getLibrariesToAutolink() 提取可链接库 │ │
│ │ - 过滤有 Android 配置的依赖 │ │
│ │ - 跳过纯 C++ 依赖 │ │
│ │ - 映射项目名到源码目录 │ │
│ │ │ │
│ │ 3. linkLibraries() 实际链接 │ │
│ │ - settings.include(path) 包含子项目 │ │
│ │ - settings.project(path).projectDir = dir 设置目录 │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
ReactPlugin
与 ReactSettingsPlugin 不同,它是一个Project Plugin。该插件是在Gradle Configuration 阶段执行,负责任务注册和构建配置。
其核心职责是:
- 插件初始化 :创建
ReactExtension配置扩展 - 多项目协调:管理 App 和 Library 项目的不同配置
- 任务注册:注册 Autolinking、Codegen 等关键任务
- AGP 集成:与 Android Gradle Plugin 深度集成
- 依赖配置:读取 ReactSettingsPlugin 的输出并进行后续处理
源码react-native/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt
kotlin
class ReactPlugin : Plugin<Project> {
override fun apply(project: Project) {
// 第一步:JVM 版本检查
checkJvmVersion(project)
// 第二步:创建 ReactExtension 配置扩展
// 这允许用户在 build.gradle 中使用 react { ... } 配置块
val extension = project.extensions.create("react", ReactExtension::class.java, project)
// 第三步:创建或获取根项目的私有扩展
// 我们在 rootProject 上注册一个私有扩展,以便将项目范围的配置(如代码生成配置)从应用程序项目传播到库。
val rootExtension = project.rootProject.extensions.findByType(PrivateReactExtension::class.java)
?: project.rootProject.extensions.create(
"privateReact",
PrivateReactExtension::class.java,
project,
)
// 第四步:Hermes V1 配置
if (project.rootProject.isHermesV1Enabled) {
rootExtension.hermesV1Enabled.set(true)
}
// 第五步:应用项目特定配置
project.pluginManager.withPlugin("com.android.application") {
// 我们将根扩展与来自应用的值(用户填充或默认值)连接起来。
rootExtension.root.set(extension.root)
rootExtension.reactNativeDir.set(extension.reactNativeDir)
rootExtension.codegenDir.set(extension.codegenDir)
rootExtension.nodeExecutableAndArgs.set(extension.nodeExecutableAndArgs)
// 延迟配置:在项目评估完成后执行
project.afterEvaluate {
// 在所有用户配置完成后执行
val reactNativeDir = extension.reactNativeDir.get().asFile
val propertiesFile = File(reactNativeDir, "ReactAndroid/gradle.properties")
val hermesVersionPropertiesFile =
File(reactNativeDir, "sdks/hermes-engine/version.properties")
// 读取 React Native 版本信息
val versionAndGroupStrings =
readVersionAndGroupStrings(propertiesFile, hermesVersionPropertiesFile)
val hermesV1Enabled = rootExtension.hermesV1Enabled.get()
// 配置依赖和仓库
configureDependencies(project, versionAndGroupStrings, hermesV1Enabled)
configureRepositories(project)
}
// NDK 配置
configureReactNativeNdk(project, extension)
// 为应用配置 BuildConfig 字段
configureBuildConfigFieldsForApp(project, extension)
// 配置开发服务器位置
configureDevServerLocation(project)
// 配置向后兼容性 React 映射
configureBackwardCompatibilityReactMap(project)
// 配置 Java 工具链
configureJavaToolChains(project)
// AGP 变体配置
project.extensions.getByType(ApplicationAndroidComponentsExtension::class.java).apply {
onVariants(selector().all()) { variant ->
// 为每个构建变体(debug、release 等)配置 React 任务
project.configureReactTasks(variant = variant, config = extension)
}
}
// 关键:配置 Autolinking
configureAutolinking(project, extension)
// 配置 Codegen(应用项目)
configureCodegen(project, extension, rootExtension, isLibrary = false)
// 配置 Android 资源
configureResources(project, extension)
// 为应用配置构建类型
configureBuildTypesForApp(project)
}
// 库项目专用配置
configureBuildConfigFieldsForLibraries(project)
configureNamespaceForLibraries(project)
project.pluginManager.withPlugin("com.android.library") {
configureCodegen(project, extension, rootExtension, isLibrary = true)
}
}
这里面关于configureCodegen方法调用,也是一个重要内容,但我们此文主要是分析三方TurboModule库的注册链接,暂时按下不表,在后面专项分析codegen机制时再来重点关注。
我们先来重点关注configureAutolinking方法,这是 React Native Autolinking 机制在 Gradle Configuration 阶段的核心实现。该方法负责注册所有 Autolinking 相关的 Gradle 任务,配置目录结构,并与 Android Gradle Plugin 进行深度集成。
kotlin
/** 此函数为应用用户设置 Autolinking */
private fun configureAutolinking(
project: Project, // ← 当前 Gradle 项目实例
extension: ReactExtension, // ← React Native 配置扩展
) {
// 配置生成的 Java 源码目录(位于build目录下)
// (存放生成的 Java 文件(PackageList.java、ReactNativeApplicationEntryPoint.java))
val generatedAutolinkingJavaDir: Provider<Directory> =
project.layout.buildDirectory.dir("generated/autolinking/src/main/java")
// 配置生成的 JNI 源码目录(存放生成的 C++ 文件,为 React Native 新架构提供 C++ 绑定)
val generatedAutolinkingJniDir: Provider<Directory> =
project.layout.buildDirectory.dir("generated/autolinking/src/main/jni")
// autolinking.json 文件在根构建文件夹中可用,因为它是由ReactSettingsPlugin.kt 生成的
val rootGeneratedAutolinkingFile =
project.rootProject.layout.buildDirectory.file("generated/autolinking/autolinking.json")
// 我们添加一个名为 generateAutolinkingPackageList 的任务,
// 以免与现有的名为 generatePackageList 的任务冲突。
// 一旦我们解除 rn <-> cli依赖关系,这可以重命名。
val generatePackageListTask =
project.tasks.register(
"generateAutolinkingPackageList",
GeneratePackageListTask::class.java,
) { task ->
task.autolinkInputFile.set(rootGeneratedAutolinkingFile) // 设置输入文件:autolinking.json
task.generatedOutputDirectory.set(generatedAutolinkingJavaDir)
// ↑ 设置输出目录:build/generated/autolinking/src/main/java
}
val generateEntryPointTask =
project.tasks.register(
"generateReactNativeEntryPoint",
GenerateEntryPointTask::class.java,
) { task ->
task.autolinkInputFile.set(rootGeneratedAutolinkingFile)
task.generatedOutputDirectory.set(generatedAutolinkingJavaDir)
}
// 我们还需要为 C++ Autolinking 生成代码
val generateAutolinkingNewArchitectureFilesTask =
project.tasks.register(
"generateAutolinkingNewArchitectureFiles",
GenerateAutolinkingNewArchitecturesFileTask::class.java,
) { task ->
task.autolinkInputFile.set(rootGeneratedAutolinkingFile)
task.generatedOutputDirectory.set(generatedAutolinkingJniDir)
}
// 配置 C++ 任务依赖(执行顺序generateAutolinkingNewArchitectureFiles → preBuild)
project.tasks
.named("preBuild", Task::class.java)
.dependsOn(generateAutolinkingNewArchitectureFilesTask)
// 我们让 generateAutolinkingPackageList 和 generateEntryPoint 依赖于 preBuild 任务,
// 这样它就会在其他所有任务之前执行。
project.tasks
.named("preBuild", Task::class.java)
.dependsOn(generatePackageListTask, generateEntryPointTask)
// 我们告诉 Android Gradle Plugin,在 /build/generated/autolinking/src/main/java 目录中
// 也有需要编译的源码。
project.extensions.getByType(ApplicationAndroidComponentsExtension::class.java).apply {
onVariants(selector().all()) { variant ->
variant.sources.java?.addStaticSourceDirectory( // 源码目录添加
generatedAutolinkingJavaDir.get().asFile.absolutePath
)
}
}
}
完整任务执行流程图
markdown
任务执行顺序:
┌─────────────────────────────────────────────────────────────────┐
│ 1. generateAutolinkingPackageList │
│ └── 生成 PackageList.java │
├─────────────────────────────────────────────────────────────────┤
│ 2. generateReactNativeEntryPoint │
│ └── 生成 ReactNativeApplicationEntryPoint.java │
├─────────────────────────────────────────────────────────────────┤
│ 3. generateAutolinkingNewArchitectureFiles │
│ └── 生成 C++ 新架构代码 │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ 4. preBuild │
│ └── AGP 预构建任务 │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ 5. 后续编译任务 │
│ ├── compileDebugJavaWithJavac │
│ ├── compileDebugKotlin │
│ └── 其他编译任务... │
└─────────────────────────────────────────────────────────────────┘
流程已经非常清晰,但是有两个地方需要特别注意,一个是代码生成的目录不要搞错,这里是有两个build目录的:
bash
YourReactNativeApp/ ← 根项目
├── android/ ← Android 应用模块
└── app
│ └──build/
│ └── generated/autolinking/ ← ReactPlugin 生成的文件
└── build/ ← 根项目构建目录
└── generated/
└── autolinking/
└── autolinking.json ← ReactSettingsPlugin 生成的文件
第二个是AGP 源码目录集成之后的项目结构:
css
Android 项目源码结构:
app
└── src/
└── main/
│ ├── java/ ← 标准 Java 源码
│ └── kotlin/ ← 标准 Kotlin 源码
└── build/generated/autolinking/src/main/java/ ← 动态添加的源码目录
└── com/facebook/react/
├── PackageList.java ← 生成的文件
└── ReactNativeApplicationEntryPoint.java
这里我们有必要了解一下AGP 集成原理。上面代码中是有通过addStaticSourceDirectory添加源码目录的,首先为什么需要手动添加源码目录?
- 动态生成:源码是在构建时动态生成的,不在标准源码目录中
- AGP 感知:AGP 需要知道这些文件的存在才能编译它们
- 变体支持:确保所有构建变体都包含生成的代码
- 增量编译:AGP 可以跟踪这些文件的变化,支持增量编译
GeneratePackageListTask
接下来我们需要分析GeneratePackageListTask类的实现,这关系到三方TurboModule 模块的收集。
源码react-native/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/GeneratePackageListTask.kt
kotlin
abstract class GeneratePackageListTask : DefaultTask() {
init {
group = "react"
}
@get:InputFile
abstract val autolinkInputFile: RegularFileProperty // ← 输入:autolinking.json
@get:OutputDirectory
abstract val generatedOutputDirectory: DirectoryProperty // ← 输出:生成目录
@TaskAction
fun taskAction() {
// 第一步:解析 autolinking.json
val model = JsonUtils.fromAutolinkingConfigJson(autolinkInputFile.get().asFile)
?: error(
"""
RNGP - Autolinking: Could not parse autolinking config file:
${autolinkInputFile.get().asFile.absolutePath}
The file is either missing or not containing valid JSON so the build won't succeed.
"""
.trimIndent()
)
// 第二步:提取 Android 包名
val packageName = model.project?.android?.packageName
?: error("RNGP - Autolinking: Could not find project.android.packageName")
// 第三步:过滤 Android 包
val androidPackages = filterAndroidPackages(model)
// 第四步:生成 import 语句
val packageImports = composePackageImports(packageName, androidPackages)
// 第五步:生成实例化代码
val packageClassInstance = composePackageInstance(packageName, androidPackages)
// 第六步:组合最终文件内容
val generatedFileContents = composeFileContent(packageImports, packageClassInstance)
// 第七步:写入生成的文件
val outputDir = generatedOutputDirectory.get().asFile
outputDir.mkdirs()
File(outputDir, GENERATED_FILENAME).apply {
parentFile.mkdirs()
writeText(generatedFileContents)
}
}
}
过滤 Android 包
- 平台检查:只保留有 Android 平台配置的依赖
- 类型过滤:排除纯 C++ 依赖(它们不需要 Java 包装)
- 数据转换 :将列表转换为
Map<依赖名, Android配置>格式
kotlin
internal fun filterAndroidPackages(
model: ModelAutolinkingConfigJson?
): Map<String, ModelAutolinkingDependenciesPlatformAndroidJson> {
val packages = model?.dependencies?.values ?: emptyList()
return packages
.filter { it.platforms?.android != null } // ← 必须有 Android 配置
.filterNot { it.platforms?.android?.isPureCxxDependency == true } // ← 排除纯 C++ 依赖
.associate { it.name to checkNotNull(it.platforms?.android) } // ← 构建映射
}
来看一下生成 PackageList.java 的模板字符串,就会更加清晰:
kotlin
val generatedFileContentsTemplate =
"""
package com.facebook.react;
import android.app.Application;
import android.content.Context;
import android.content.res.Resources;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainPackageConfig;
import com.facebook.react.shell.MainReactPackage;
import java.util.Arrays;
import java.util.ArrayList;
{{ packageImports }}
@SuppressWarnings("deprecation")
public class PackageList {
private Application application;
private ReactNativeHost reactNativeHost;
private MainPackageConfig mConfig;
public PackageList(ReactNativeHost reactNativeHost) {
this(reactNativeHost, null);
}
public PackageList(Application application) {
this(application, null);
}
public PackageList(ReactNativeHost reactNativeHost, MainPackageConfig config) {
this.reactNativeHost = reactNativeHost;
mConfig = config;
}
public PackageList(Application application, MainPackageConfig config) {
this.reactNativeHost = null;
this.application = application;
mConfig = config;
}
private ReactNativeHost getReactNativeHost() {
return this.reactNativeHost;
}
private Resources getResources() {
return this.getApplication().getResources();
}
private Application getApplication() {
if (this.reactNativeHost == null) return this.application;
return this.reactNativeHost.getApplication();
}
private Context getApplicationContext() {
return this.getApplication().getApplicationContext();
}
public ArrayList<ReactPackage> getPackages() {
return new ArrayList<>(Arrays.<ReactPackage>asList(
new MainReactPackage(mConfig){{ packageClassInstances }}
));
}
}
"""
.trimIndent()
注意到,这里是通过new MainReactPackage(mConfig){{ packageClassInstances }} 动态插入了收集到的三方TurboModule 。
GenerateAutolinkingNewArchitecturesFileTask
接下来再研究一下GenerateAutolinkingNewArchitecturesFileTask类,这关系到我们前面说的纯C++ TurboModule的自动链接注册。
源码react-native/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/GenerateAutolinkingNewArchitecturesFileTask.kt
kotlin
abstract class GenerateAutolinkingNewArchitecturesFileTask : DefaultTask() {
init {
group = "react"
}
@get:InputFile abstract val autolinkInputFile: RegularFileProperty
// 输出:build/generated/autolinking/src/main/jni/
@get:OutputDirectory abstract val generatedOutputDirectory: DirectoryProperty
@TaskAction
fun taskAction() {
// 1. 解析 autolinking.json
val model = JsonUtils.fromAutolinkingConfigJson(autolinkInputFile.get().asFile)
// 2. 过滤 Android 平台的包
val packages = filterAndroidPackages(model)
// 3. 生成 CMake 文件内容
val cmakeFileContent = generateCmakeFileContent(packages)
// 4. 生成 C++ 文件内容
val cppFileContent = generateCppFileContent(packages)
// 5. 写入文件
val outputDir = generatedOutputDirectory.get().asFile
outputDir.mkdirs()
File(outputDir, CMAKE_FILENAME).apply { writeText(cmakeFileContent) }
File(outputDir, CPP_FILENAME).apply { writeText(cppFileContent) }
File(outputDir, H_FILENAME).apply { writeText(hTemplate) }
}
internal fun filterAndroidPackages(
model: ModelAutolinkingConfigJson?
): List<ModelAutolinkingDependenciesPlatformAndroidJson> =
// 从dependencies中提取所有Android平台的配置,如果为空则返回空列表
model?.dependencies?.values?.mapNotNull { it.platforms?.android } ?: emptyList()
internal fun generateCmakeFileContent(
packages: List<ModelAutolinkingDependenciesPlatformAndroidJson>
): String {
val libraryIncludes =
packages.joinToString("\n") { dep ->
var addDirectoryString = ""
val libraryName = dep.libraryName // 获取库名称
// 获取CMakeLists.txt文件路径
// 用于Codegen生成的TurboModule/Fabric组件,生成的库名:react_codegen_${libraryName}
// 通过 autolinking_ModuleProvider() 提供 Java TurboModule
val cmakeListsPath = dep.cmakeListsPath
// 用于纯C++ TurboModule,生成的库名由第三方库的CMakeLists.txt自定义
// 通过 autolinking_cxxModuleProvider() 提供 C++ TurboModule
val cxxModuleCMakeListsPath = dep.cxxModuleCMakeListsPath
if (libraryName != null && cmakeListsPath != null) {
// 如果用户提供了自定义的 cmakeListsPath,则使用它
val nativeFolderPath = sanitizeCmakeListsPath(cmakeListsPath)
addDirectoryString +=
"add_subdirectory(\"$nativeFolderPath\" ${libraryName}_autolinked_build)"
}
if (cxxModuleCMakeListsPath != null) {
// 如果用户提供了自定义的cxxModuleCMakeListsPath,则使用它
val nativeFolderPath = sanitizeCmakeListsPath(cxxModuleCMakeListsPath)
addDirectoryString +=
"\nadd_subdirectory(\"$nativeFolderPath\" ${libraryName}_cxxmodule_autolinked_build)"
}
addDirectoryString
}
// 生成库模块列表:收集所有需要自动链接的库名称
val libraryModules =
packages.joinToString("\n ") { dep ->
var autolinkedLibraries = ""
// 如果存在库名称,添加代码生成库前缀
if (dep.libraryName != null) {
autolinkedLibraries += "$CODEGEN_LIB_PREFIX${dep.libraryName}"
}
if (dep.cxxModuleCMakeListsModuleName != null) {
autolinkedLibraries += "\n${dep.cxxModuleCMakeListsModuleName}"
}
autolinkedLibraries
}
return CMAKE_TEMPLATE.replace("{{ libraryIncludes }}", libraryIncludes)
.replace("{{ libraryModules }}", libraryModules)
}
internal fun generateCppFileContent(
packages: List<ModelAutolinkingDependenciesPlatformAndroidJson>
): String {
// 过滤出有库名称的包(只有这些包才需要生成C++代码)
val packagesWithLibraryNames = packages.filter { android -> android.libraryName != null }
val cppIncludes =
packagesWithLibraryNames.joinToString("\n") { dep ->
var include = "#include <${dep.libraryName}.h>"
if (dep.componentDescriptors.isNotEmpty()) {
include +=
"\n#include <${COMPONENT_INCLUDE_PATH}/${dep.libraryName}/${COMPONENT_DESCRIPTOR_FILENAME}>"
}
if (dep.cxxModuleHeaderName != null) {
include += "\n#include <${dep.cxxModuleHeaderName}.h>"
}
include
}
// 生成Java TurboModule提供者的C++代码
val cppTurboModuleJavaProviders =
packagesWithLibraryNames.joinToString("\n") { dep ->
val libraryName = dep.libraryName
// language=cpp
"""
auto module_$libraryName = ${libraryName}_ModuleProvider(moduleName, params);
if (module_$libraryName != nullptr) {
return module_$libraryName;
}
"""
.trimIndent()
}
// 生成C++ TurboModule提供者的代码
val cppTurboModuleCxxProviders =
packagesWithLibraryNames
.filter { it.cxxModuleHeaderName != null }
.joinToString("\n") { dep ->
val cxxModuleHeaderName = dep.cxxModuleHeaderName
// language=cpp
"""
if (moduleName == $cxxModuleHeaderName::kModuleName) {
return std::make_shared<$cxxModuleHeaderName>(jsInvoker);
}
"""
.trimIndent()
}
val cppComponentDescriptors =
packagesWithLibraryNames
.filter { it.componentDescriptors.isNotEmpty() }
.joinToString("\n") {
it.componentDescriptors.joinToString("\n") {
"providerRegistry->add(concreteComponentDescriptorProvider<$it>());"
}
}
return CPP_TEMPLATE.replace("{{ autolinkingCppIncludes }}", cppIncludes)
.replace("{{ autolinkingCppTurboModuleJavaProviders }}", cppTurboModuleJavaProviders)
.replace("{{ autolinkingCppTurboModuleCxxProviders }}", cppTurboModuleCxxProviders)
.replace("{{ autolinkingCppComponentDescriptors }}", cppComponentDescriptors)
}
companion object {
const val CMAKE_FILENAME = "Android-autolinking.cmake"
const val H_FILENAME = "autolinking.h"
const val CPP_FILENAME = "autolinking.cpp"
// React代码生成库的前缀
const val CODEGEN_LIB_PREFIX = "react_codegen_"
// 组件描述符头文件名
const val COMPONENT_DESCRIPTOR_FILENAME = "ComponentDescriptors.h"
// 组件的包含路径前缀
const val COMPONENT_INCLUDE_PATH = "react/renderer/components"
/**
* 清理CMakeLists.txt路径,移除文件名部分并转义空格字符
*/
internal fun sanitizeCmakeListsPath(cmakeListsPath: String): String =
cmakeListsPath.replace("CMakeLists.txt", "").replace(" ", "\\ ")
// language=cmake
val CMAKE_TEMPLATE =
"""
# This code was generated by [React Native](https://www.npmjs.com/package/@react-native/gradle-plugin)
cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on)
# We set REACTNATIVE_MERGED_SO so libraries/apps can selectively decide to depend on either libreactnative.so
# or link against a old prefab target (this is needed for React Native 0.76 on).
set(REACTNATIVE_MERGED_SO true)
{{ libraryIncludes }}
set(AUTOLINKED_LIBRARIES
{{ libraryModules }}
)
"""
.trimIndent()
// language=cpp
val CPP_TEMPLATE =
"""
/**
* This code was generated by [React Native](https://www.npmjs.com/package/@react-native/gradle-plugin).
*
* Do not edit this file as changes may cause incorrect behavior and will be lost
* once the code is regenerated.
*
*/
#include "autolinking.h"
{{ autolinkingCppIncludes }}
namespace facebook {
namespace react {
std::shared_ptr<TurboModule> autolinking_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms) {
{{ autolinkingCppTurboModuleJavaProviders }}
return nullptr;
}
std::shared_ptr<TurboModule> autolinking_cxxModuleProvider(const std::string moduleName, const std::shared_ptr<CallInvoker>& jsInvoker) {
{{ autolinkingCppTurboModuleCxxProviders }}
return nullptr;
}
void autolinking_registerProviders(std::shared_ptr<ComponentDescriptorProviderRegistry const> providerRegistry) {
{{ autolinkingCppComponentDescriptors }}
return;
}
} // namespace react
} // namespace facebook
"""
.trimIndent()
// language=cpp
val hTemplate =
"""
/**
* This code was generated by [React Native](https://www.npmjs.com/package/@react-native/gradle-plugin).
*
* Do not edit this file as changes may cause incorrect behavior and will be lost
* once the code is regenerated.
*
*/
#pragma once
#include <ReactCommon/CallInvoker.h>
#include <ReactCommon/JavaTurboModule.h>
#include <ReactCommon/TurboModule.h>
#include <jsi/jsi.h>
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
namespace facebook {
namespace react {
std::shared_ptr<TurboModule> autolinking_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms);
std::shared_ptr<TurboModule> autolinking_cxxModuleProvider(const std::string moduleName, const std::shared_ptr<CallInvoker>& jsInvoker);
void autolinking_registerProviders(std::shared_ptr<ComponentDescriptorProviderRegistry const> providerRegistry);
} // namespace react
} // namespace facebook
"""
.trimIndent()
}
}
此处需要特别注意的是,有两个cmake路径:cmakeListsPath和cxxModuleCMakeListsPath
- cmakeListsPath :编译由Codegen生成的TurboModule/Fabric组件,库名称是
react_codegen_${libraryName},使用autolinking_ModuleProvider()函数注册。简单说,就是从JS规范自动生成的相关代码,主要就基于原生的TurboModule。 - cxxModuleCMakeListsPath :主要就是我们手写的纯C++TurboModule,库名称由我们自定义,使用
autolinking_cxxModuleProvider()注册。也就是说,如果我们打算开发一个纯C++TurboModule,那么此路径就是必须要提供的
这里有一个疑问,前面我们分析了基于原生的TurboModule(即Java TurboModule)是通过自动生成PackageList类来实现注册的,那么这里的autolinking_ModuleProvider又是什么呢?是否出现了重复注册?
其实在上面Java TurboModule 分析一节,我们已经触及到了这个问题。回顾一下,那里提到了两层查找,首先是反射kotlin类的getTurboJavaModule方法,获取到了通过PackageList注册的TurboModule 的Kotlin类对象,然后将此对象封装成一个参数继续调用C++层的getTurboModule返回一个C++层的TurboModule对象,最终是将这个C++ TurboModule实例对象添加到缓存中,用于下次直接使用。也就是说,一个RN的第三方TurboModule库,其方法调用是使用这个C++ TurboModule实例对象,而不是Kotlin实现的TurboModule对象。
要想弄明白这个问题,可以创建一个hello项目,在项目中任意添加一个三方TurboModule。譬如我这里添加:"@dr.pogodin/react-native-fs",然后安装依赖,并构建项目,就会自动触发codegen。
现在我们来查看生成的autolinking_ModuleProvider,源码hello/android/app/build/generated/autolinking/src/main/jni/autolinking.cpp
c++
std::shared_ptr<TurboModule> autolinking_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms) {
auto module_RNReactNativeFsSpec = RNReactNativeFsSpec_ModuleProvider(moduleName, params);
if (module_RNReactNativeFsSpec != nullptr) {
return module_RNReactNativeFsSpec;
}
return nullptr;
}
源码node_modules/@dr.pogodin/react-native-fs/android/generated/jni/RNReactNativeFsSpec-generated.cpp:
c++
std::shared_ptr<TurboModule> RNReactNativeFsSpec_ModuleProvider(const std::string &moduleName, const JavaTurboModule::InitParams ¶ms) {
if (moduleName == "ReactNativeFs") {
return std::make_shared<NativeReactNativeFsSpecJSI>(params);
}
return nullptr;
}
NativeReactNativeFsSpecJSI::NativeReactNativeFsSpecJSI(const JavaTurboModule::InitParams ¶ms)
: JavaTurboModule(params) {
methodMap_["getConstants"] = MethodMetadata {0, __hostFunction_NativeReactNativeFsSpecJSI_getConstants};
methodMap_["addListener"] = MethodMetadata {1, __hostFunction_NativeReactNativeFsSpecJSI_addListener};
methodMap_["removeListeners"] = MethodMetadata {1, __hostFunction_NativeReactNativeFsSpecJSI_removeListeners};
methodMap_["appendFile"] = MethodMetadata {2, __hostFunction_NativeReactNativeFsSpecJSI_appendFile};
methodMap_["copyFile"] = MethodMetadata {3, __hostFunction_NativeReactNativeFsSpecJSI_copyFile};
// 省略......
}
static facebook::jsi::Value __hostFunction_NativeReactNativeFsSpecJSI_getConstants(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
static jmethodID cachedMethodId = nullptr;
return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, ObjectKind, "getConstants", "()Ljava/util/Map;", args, count, cachedMethodId);
}
最终返回的是一个自动生成的NativeReactNativeFsSpecJSI对象指针。我们看一下声明:
cpp
/**
* JNI C++ class for module 'NativeReactNativeFs'
*/
class JSI_EXPORT NativeReactNativeFsSpecJSI : public JavaTurboModule {
public:
NativeReactNativeFsSpecJSI(const JavaTurboModule::InitParams ¶ms);
};
可见,NativeReactNativeFsSpecJSI就是一个派生自JavaTurboModule的子类。在其构造方法中,为methodMap_注册了很多方法。这些方法也就是提供给上层JS调用的。查看任意一个方法,发现它还是反射的Kotlin的TurboModule实现类的方法。也就是说,绕了一大圈,最终调用的仍然是Kotlin编写的TurboModule实现。生成的PackageList仅用于首次注册模块,后续的方法调用基本都是基于NativeReactNativeFsSpecJSI的反射。当然,这里的反射也进行了一些优化,只会反射一次。
推测之所以要这样做,大概就是有两点,一是为了兼容旧架构基于PackageList注册模块的机制,第二是为了支持更自动化的Codegen。如果直接反射Kotlin的TurboModule实现类完成调用,就没办法自动规范化接口。简单说,当我们用TS定义好接口API后,Codegen可以根据接口的函数签名,自动对参数、返回值类型进行JSI类型映射,无需模块开发者操心。NativeReactNativeFsSpecJSI类就是自动生成的方法类型映射,这是基于编译时生成的,相比运行时手动处理更安全。
再看一下该三方库在node_modules中的文件结构:
java
node_modules/
├── @dr.pogodin/react-native-fs ← 三方TurboModule
└── android
└──generated/
└── jni/
└── CMakeLists.txt
|── RNReactNativeFsSpec.h
|── RNReactNativeFsSpec-generated.cpp
显然的,这里的CMakeLists.txt就是上面cmakeListsPath 路径指向的文件,用于编译当前目录下自动生成的C++代码RNReactNativeFsSpec-generated.cpp
最后总结一下,一个React Native项目中的cmake关系:
objectivec
hello/
├── android/
│ ├── settings.gradle ⓪ Gradle 配置入口
│ ├── build.gradle ⓪ 根 build.gradle
│ │
│ └── app/
│ ├── build.gradle ⓪ App build.gradle (应用 react 插件)
│ │
│ ├── src/main/
│ │ └── res/...
│ │
│ └── build/
│ ├── generated/
│ │ └── autolinking/
│ │ └── src/main/
│ │ ├── java/com/facebook/react/
│ │ │ └── PackageList.java ⓹ 生成的 Java 包列表
│ │ │
│ │ └── jni/
│ │ ├── Android-autolinking.cmake ⓷ 生成的 CMake 配置
│ │ ├── autolinking.h ⓹ 生成的 C++ 头文件
│ │ └── autolinking.cpp ⓸ 生成的 C++ 实现
│
└── node_modules/
├── react-native/
│ └── ReactAndroid/
│ └── cmake-utils/
│ ├── default-app-setup/
│ │ ├── CMakeLists.txt ⓵ 入口 CMakeLists.txt
│ │ └── OnLoad.cpp ⓺ JNI 入口点
│ │
│ ├── ReactNative-application.cmake ⓶ 核心构建逻辑
│ └── folly-flags.cmake ⓪ Folly 编译选项
│
└── react-native-third-party/
└── android/
├── build.gradle ⓪ 第三方库 Gradle
└── build/generated/source/codegen/jni/
└── CMakeLists.txt ⓺ 第三方库 CMake
CMake包含关系:
scss
default-app-setup/CMakeLists.txt
│
├─ include(${REACT_ANDROID_DIR}/cmake-utils/ReactNative-application.cmake)
│ │
│ ReactNative-application.cmake
│ │
│ ├─ include(${CMAKE_CURRENT_LIST_DIR}/folly-flags.cmake)
│ │ └─ 设置 Folly 编译选项
│ │
│ ├─ include(${PROJECT_BUILD_DIR}/generated/autolinking/src/main/jni/Android-autolinking.cmake)
│ │ │
│ │ Android-autolinking.cmake
│ │ │
│ │ └─ add_subdirectory(
│ │ "/path/to/node_modules/react-native-third-party/android/generated/jni/"
│ │ RNReactNativeFsSpec_autolinked_build
│ │ )
│ │ │
│ │ react-native-third-party/CMakeLists.txt
│ │ │
│ │ └─ add_library(react_codegen_RNReactNativeFsSpec OBJECT ${react_codegen_SRCS})
│ │
│ └─ add_subdirectory(${BUILD_DIR}/generated/source/codegen/jni/)
│ └─ App 级别的 Codegen (如果存在)
│
└─ 创建 libappmodules.so
在React Native 的原生项目中,并没有做CMake相关的配置,那么到底是哪里触发的CMake构建的呢?
CMake 链路图:
kotlin
┌──────────────────────────────────────────────────────────────────────┐
│ ⓪ android/app/build.gradle │
├──────────────────────────────────────────────────────────────────────┤
│ apply plugin: "com.facebook.react" │
│ │
│ react { │
│ autolinkLibrariesWithApp() ← 启用 autolinking │
│ } │
│ │
│ android { │
│ ndkVersion rootProject.ext.ndkVersion │
│ // 注意:并没有 externalNativeBuild 配置! │
│ } │
└──────────────────────────────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────────────────────────┐
│ ReactPlugin.kt (apply 插件时执行) │
├──────────────────────────────────────────────────────────────────────┤
│ import com.facebook.react.utils.NdkConfiguratorUtils.configureReactNativeNdk|
| |
| override fun apply(project: Project) { │
│ configureReactNativeNdk(project, extension) ← 配置 NDK │
│ configureAutolinking(project, extension) ← 配置 autolinking │
│ } │
└──────────────────────────────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────────────────────────┐
│ NdkConfiguratorUtils.kt (自动配置 CMake) │
├──────────────────────────────────────────────────────────────────────┤
│ val ext = project.extensions.getByType(BaseExtension::class.java) │
│ ext.buildFeatures.prefab = true │
│ │
│ // 关键代码:如果用户没有提供 CMakeLists.txt,使用默认的 │
│ if (ext.externalNativeBuild.cmake.path == null) { │
│ ext.externalNativeBuild.cmake.path = File( │
│ extension.reactNativeDir.get().asFile, │
│ "ReactAndroid/cmake-utils/default-app-setup/CMakeLists.txt" │
│ ) │
│ } │
│ │
│ // 添加 CMake 参数 │
│ val cmakeArgs = ext.defaultConfig.externalNativeBuild.cmake.arguments│
│ cmakeArgs.add("-DPROJECT_BUILD_DIR=${project.layout.buildDirectory.get().asFile}") │
│ cmakeArgs.add("-DREACT_ANDROID_DIR=${extension.reactNativeDir.file("ReactAndroid").get().asFile}") │
│ cmakeArgs.add("-DANDROID_STL=c++_shared") │
│ │
│ 结果: │
│ externalNativeBuild.cmake.path = │
│ "/path/to/node_modules/react-native/ReactAndroid/cmake-utils/ │
│ default-app-setup/CMakeLists.txt" │
└──────────────────────────────────────────────────────────────────────┘
可见,是com.facebook.react插件脚本中动态注入了构建CMake的配置,从而触发了CMake构建。
流程总结
- Settings 阶段 :
ReactSettingsPlugin在settings.gradle解析时自动执行 - 配置阶段 :
ReactPlugin在项目配置时注册GeneratePackageListTask - 构建阶段 :
preBuild任务自动依赖generateAutolinkingPackageList任务 - 编译阶段 :生成的
PackageList.java被自动编译到应用中
完整的任务流程图
scss
┌─────────────────────────────────────────────────────────────────┐
│ Gradle 构建 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Settings 阶段: │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ ReactSettingsPlugin.apply() │ │
│ │ ↓ │ │
│ │ ReactSettingsExtension.autolinkLibrariesFromCommand() │ │
│ │ ↓ │ │
│ │ 生成 autolinking.json │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ↓ │
│ Configuration 阶段: │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ ReactPlugin.apply() │ │
│ │ ↓ │ │
│ │ configureAutolinking() │ │
│ │ ↓ │ │
│ │ 注册 Autolinking 任务: │ │
│ │ - generateAutolinkingPackageList │ │
│ │ - generateReactNativeEntryPoint │ │
│ │ - generateAutolinkingNewArchitectureFiles │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ↓ │
│ Execution 阶段: │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ preBuild │ │
│ │ ├── generateAutolinkingPackageList │ │
│ │ │ └── 生成 PackageList.java │ │
│ │ ├── generateReactNativeEntryPoint │ │
│ │ │ └── 生成 ReactNativeApplicationEntryPoint.java │ │
│ │ └── generateAutolinkingNewArchitectureFiles │ │
│ │ └── 生成 C++ 新架构代码 │ │
│ │ │ │
│ │ compileDebugJavaWithJavac │ │
│ │ └── 编译生成的 Java 代码 │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘