ReactNative新架构之Android端TurboModule机制完全解析

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的初始化。initHybridinstallJSIBindings都是Native方法,我们稍后分析,先看看delegate?.getEagerInitModuleNames()返回的预加载模块是什么。这里的delegate是外部传入的TurboModuleManagerDelegate实例,在ReactInstance初始化中创建:

kotlin 复制代码
val turboModuleManagerDelegate = delegate.turboModuleManagerDelegateBuilder
                                  .setPackages(reactPackages)
                                  .setReactApplicationContext(context)
                                  .build()

我们知道ReactInstance中的delegateDefaultReactHostDelegate实例,而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)...)));
  }

这是两个模版函数。其主要的功能就是做了三件事:

  1. 根据模版类型创建 C++ 对象
  2. 创建 HybridData 包装器来包装 C++ 对象
  3. 返回 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方法,作为新架构,接下来就会调用TurboModuleBindinggetModule来查找模块。

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;
}

查找优先级

  1. C++ 缓存turboModuleCache_
  2. C++ TurboModule:通过 delegate
  3. 全局 C++ TurboModuleglobalExportedCxxTurboModuleMap()
  4. Java TurboModule :首先通过 JNI 调用 getTurboJavaModule(),接着调用C++层的getTurboModule
  5. 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的查找逻辑,这里的cxxDelegateTurboModuleManagerDelegate类型,其具体实现类是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 &params) {
        {{ 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 &params);
        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路径:cmakeListsPathcxxModuleCMakeListsPath

  • 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 &params) {
  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 &params) {
  if (moduleName == "ReactNativeFs") {
    return std::make_shared<NativeReactNativeFsSpecJSI>(params);
  }
  return nullptr;
}

NativeReactNativeFsSpecJSI::NativeReactNativeFsSpecJSI(const JavaTurboModule::InitParams &params)
  : 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 &params);
};

可见,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 阶段ReactSettingsPluginsettings.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 代码                                │   │
│ └──────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────┘
相关推荐
lili-felicity2 小时前
React Native for Harmony 个人消息列表最新消息置顶实现(多维度权重统计)
javascript·react native·react.js
iloveAnd2 小时前
Android开发中痛点解决(二)兼容性:AndroidX和gradle版本的兼容性
android·兼容性·androidx
stevenzqzq3 小时前
DataStore基本使用教程
android
LawrenceMssss4 小时前
由于创建一个完整的App涉及到多个层面(如前端、后端、数据库等),并且每种语言通常有其特定的用途(如Java/Kotlin用于Android开发,Swift/Objective-C用于iOS开发,Py
android·java·ios
2501_948122634 小时前
React Native for OpenHarmony 实战:Steam 资讯 App 个人中心页面
javascript·react native·react.js·游戏·ecmascript·harmonyos
chen_mangoo4 小时前
HDMI简介
android·linux·驱动开发·单片机·嵌入式硬件
阿里-于怀5 小时前
AgentScope AutoContextMemory:告别 Agent 上下文焦虑
android·java·数据库·agentscope
lili-felicity5 小时前
React Native for Harmony 企业级折叠面板 (Accordion) 组件
react native·react.js·ui
Larry_Yanan5 小时前
Qt安卓开发(三)双摄像头内嵌布局
android·开发语言·c++·qt·ui