Skip to main content

C++ Turbo 原生模块


提醒

本文档仍为 experimental,详细信息可能会随着我们的迭代而发生变化。 欢迎在此页面分享你对 工作组内部讨论 的反馈。

This documentation is still experimental and details are subject to changes as we iterate. Feel free to share your feedback on the discussion inside the working group for this page.

而且,它还包含几个 手动步骤。 请注意,一旦新架构稳定,这将不代表最终的开发者体验。 我们正在开发工具、模板和库,以帮助你快速开始使用新架构,而无需完成整个设置。

Moreover, it contains several manual steps. Please note that this won't be representative of the final developer experience once the New Architecture is stable. We're working on tools, templates and libraries to help you get started fast on the New Architecture, without having to go through the whole setup.

本指南向你展示如何仅使用 C++ 实现 Turbo Native 模块,这是一种与任何支持的平台(Android、iOS、macOS 或 Windows)共享相同实现的方法。

This guide shows you how to implement a Turbo Native Module using C++ only, a way to share the same implementation with any supported platform (Android, iOS, macOS or Windows).

在继续阅读本指南之前,请阅读 Turbo Native 模块 部分。 作为进一步的参考,我们为 RNTester 应用 (NativeCxxModuleExample) 准备了一个示例,并在我们的社区存储库 (运行/pure-cxx-模块) 中运行了一个示例。

Before continuing with this guide, please read the Turbo Native Modules section. As a further reference, we prepared an example for the RNTester app (NativeCxxModuleExample) and a sample run in our community repository (run/pure-cxx-module).

提醒

C++ Turbo Native 模块可在启用 新架构 的情况下使用。 要迁移到 新架构,请按照 迁移指南 操作:::

如何创建 C++ Turbo Native 模块

要创建 C++ Turbo Native 模块,你需要:

To create a C++ Turbo Native Module, you need to:

  1. 定义 JavaScript 规范。
  2. 配置 Codegen 以生成脚手架。
  3. 注册原生模块。
  4. 编写原生代码完成模块的实现。

为新架构设置测试应用

第一步,创建一个新应用:

As first step, create a new application:

npx react-native init CxxTurboModulesGuide
cd CxxTurboModulesGuide
yarn install

在 Android 上,通过修改 android/gradle.properties 文件启用新架构:

On Android enable the New Architecture by modifying the android/gradle.properties file:

- newArchEnabled=false
+ newArchEnabled=true

在 iOS 上,在 ios 文件夹中运行 pod install 时启用新架构:

On iOS enable the New Architecture when running pod install in the ios folder:

RCT_NEW_ARCH_ENABLED=1 bundle exec pod install

Turbo 模块文件夹设置

在项目内创建一个 tm 文件夹。 它将包含你的应用的所有 C++ TurboModule。 最终结果应如下所示:

Create a tm folder inside the project. It will contain all C++ TurboModules of your application. The final result should look like this:

CxxTurboModulesGuide
├── android
├── ios
├── js
└── tm

1. JavaScript 规范

tm 文件夹中创建以下规范:

Create the following spec inside the tm folder:

NativeSampleModule.ts
import {TurboModule, TurboModuleRegistry} from 'react-native';

export interface Spec extends TurboModule {
readonly reverseString: (input: string) => string;
}

export default TurboModuleRegistry.getEnforcing<Spec>(
'NativeSampleModule',
);

2. 代码生成配置

接下来,你需要为 代码生成器 添加一些配置。

Next, you need to add some configuration for Codegen.

应用

使用以下条目更新应用的 package.json 文件:

Update your app's package.json file with the following entries:

package.json
{
// ...
"description": "React Native with Cxx Turbo Native Modules",
"author": "<Your Name> <your_email@your_provider.com> (https://github.com/<your_github_handle>)",
"license": "MIT",
"homepage": "https://github.com/<your_github_handle>/#readme",
// ...
"codegenConfig": {
"name": "AppSpecs",
"type": "all",
"jsSrcsDir": "tm",
"android": {
"javaPackageName": "com.facebook.fbreact.specs"
}
}
}

它添加了必要的属性,我们稍后将在 iOS podspec 文件中重复使用这些属性,并配置 代码生成器 以在 tm 文件夹内搜索规范。

It adds necessary properties which we will later re-use in the iOS podspec file and configures Codegen to search for specs inside the tm folder.

提醒

C++ Turbo Native 模块不会自动链接,需要按照下面描述的步骤手动包含到应用中。

iOS:创建 podspec 文件

对于 iOS,你需要在 tm 文件夹中创建一个 AppTurboModules.podspec 文件 - 如下所示:

For iOS, you'll need to create a AppTurboModules.podspec file in the tm folder - which will look like:

AppTurboModules.podspec
require "json"

package = JSON.parse(File.read(File.join(__dir__, "../package.json")))

Pod::Spec.new do |s|
s.name = "AppTurboModules"
s.version = package["version"]
s.summary = package["description"]
s.description = package["description"]
s.homepage = package["homepage"]
s.license = package["license"]
s.platforms = { :ios => "12.4" }
s.author = package["author"]
s.source = { :git => package["repository"], :tag => "#{s.version}" }
s.source_files = "**/*.{h,cpp}"
s.pod_target_xcconfig = {
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
}
install_modules_dependencies(s)
end

你需要将其作为依赖添加到 ios/Podfile 中的应用中,例如,在 use_react_native!(...) 部分之后:

You need to add it as a dependency to your application in ios/Podfile, e.g., after the use_react_native!(...) section:

if ENV['RCT_NEW_ARCH_ENABLED'] == '1'
pod 'AppTurboModules', :path => "./../tm"
end

安卓:build.gradleCMakeLists.txtOnload.cpp

对于 Android,你需要在 tm 文件夹中创建一个 CMakeLists.txt 文件 - 如下所示:

For Android, you'll need to create a CMakeLists.txt file in the tm folder - which will look like:

cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on)

add_compile_options(
-fexceptions
-frtti
-std=c++17)

file(GLOB tm_SRC CONFIGURE_DEPENDS *.cpp)
add_library(tm STATIC ${tm_SRC})

target_include_directories(tm PUBLIC .)
target_include_directories(react_codegen_AppSpecs PUBLIC .)

target_link_libraries(tm
jsi
react_nativemodule_core
react_codegen_AppSpecs)

它将 tm 文件夹定义为原生代码的源并设置必要的依赖。

It defines the tm folder as a source for native code and sets up necessary dependencies.

你需要将其作为依赖添加到 android/app/build.gradle 中的应用中,例如,在该文件的最后:

You need to add it as a dependency to your application in android/app/build.gradle, e.g., at the very end of that file:

build.gradle
android {
externalNativeBuild {
cmake {
path "src/main/jni/CMakeLists.txt"
}
}
}
注意

确保选择正确的 android/app/build.gradle 文件而不是 android/build.gradle。

3. 模块注册

iOS

要在你的应用中注册 C++ Turbo Native 模块,你需要使用以下条目更新 ios/CxxTurboModulesGuide/AppDelegate.mm

To register a C++ Turbo Native Module in your app you will need to update ios/CxxTurboModulesGuide/AppDelegate.mm with the following entries:

#import "AppDelegate.h"

#import <React/RCTBundleURLProvider.h>
+ #import <React/CoreModulesPlugins.h>
+ #import <ReactCommon/RCTTurboModuleManager.h>
+ #import <NativeSampleModule.h>

+ @interface AppDelegate () <RCTTurboModuleManagerDelegate> {}
+ @end

// ...

᠆ (Class)getModuleClassFromName:(const char *)name
{
return RCTCoreModulesClassProvider(name);
}

+ #pragma mark RCTTurboModuleManagerDelegate

+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
+ jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
+ {
+ if (name == "NativeSampleModule") {
+ return std::make_shared<facebook::react::NativeSampleModule>(jsInvoker);
+ }
+ return nullptr;
+ }

这将实例化一个与我们之前在 JavaScript 规范文件中定义的名称 NativeSampleModule 关联的 NativeSampleModule

This will instantiante a NativeSampleModule associated with the name NativeSampleModule as defined in our JavaScript spec file earlier.

安卓

默认情况下,Android 应用未设置原生代码编译。

Android apps aren't setup for native code compilation by default.

1.) 创建文件夹 android/app/src/main/jni

1.) Create the folder android/app/src/main/jni

2.) 将 node_modules/react-native/ReactAndroid/cmake-utils/default-app-setup 中的 CMakeLists.txtOnload.cpp 复制到 android/app/src/main/jni 文件夹中。

2.) Copy CMakeLists.txt and Onload.cpp from node_modules/react-native/ReactAndroid/cmake-utils/default-app-setup into the android/app/src/main/jni folder.

使用以下条目更新 Onload.cpp

Update Onload.cpp with the following entries:

// ...

#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
#include <rncli.h>
+ #include <NativeSampleModule.h>

// ...

std::shared_ptr<TurboModule> cxxModuleProvider(
const std::string &name,
const std::shared_ptr<CallInvoker> &jsInvoker) {
+ if (name == "NativeSampleModule") {
+ return std::make_shared<facebook::react::NativeSampleModule>(jsInvoker);
+ }
return nullptr;
}

// ...

使用以下条目更新 CMakeLists.txt,例如在该文件的最后:

Update CMakeLists.txt with the following entries, e.g., at the very end of that file:

// ...

# This file includes all the necessary to let you build your application with the New Architecture.
include(${REACT_ANDROID_DIR}/cmake-utils/ReactNative-application.cmake)

+ # App needs to add and link against tm (TurboModules) folder
+ add_subdirectory(${REACT_ANDROID_DIR}/../../../tm/ tm_build)
+ target_link_libraries(${CMAKE_PROJECT_NAME} tm)

这将实例化一个与我们之前在 JavaScript 规范文件中定义的名称 NativeSampleModule 关联的 NativeSampleModule

This will instantiante a NativeSampleModule associated with the name NativeSampleModule as defined in our JavaScript spec file earlier.

4. C++ 原生代码

最后一步,你需要编写一些原生代码以将 JavaScript 端连接到原生平台。 这个过程需要两个主要步骤:

For the final step, you'll need to write some native code to connect the JavaScript side to the native platforms. This process requires two main steps:

  • 运行 代码生成器 查看它生成的内容。
  • 编写你的原生代码,实现生成的接口。

运行代码生成器

信息

请遵循 代码生成器 指南了解一般信息。

在 iOS 上,每次在 ios 文件夹中执行时都会运行 Codegen:

On iOS Codegen is run each time you execute in the ios folder:

RCT_NEW_ARCH_ENABLED=1 bundle exec pod install

你可以检查 CxxTurboModulesGuide/ios/build/generated/ios 文件夹内生成的 AppSpecsJSI.hAppSpecsJSI-generated.cpp 文件。

You can inspect the generated AppSpecsJSI.h and AppSpecsJSI-generated.cpp files inside the CxxTurboModulesGuide/ios/build/generated/ios folder.

这些文件以 AppSpecs 为前缀,因为这与之前添加到 package.jsoncodegenConfig.name 参数匹配。

Those files are prefixed with AppSpecs as this matches the codegenConfig.name parameter added earlier to package.json.

在 Android 上,每次执行时都会运行 Codegen:

On Android Codegen is run each time you execute:

yarn android

你可以检查 CxxTurboModulesGuide/android/app/build/generated/source/codegen/jni 文件夹内生成的 AppSpecsJSI.hAppSpecsJSI-generated.cpp 文件。

You can inspect the generated AppSpecsJSI.h and AppSpecsJSI-generated.cpp files inside the CxxTurboModulesGuide/android/app/build/generated/source/codegen/jni folder.

如果你更改了 JavaScript 规范,则只需重新运行 codegen。

You only need to re-run codegen if you have changed your JavaScript spec.

为 JavaScript 规范文件生成的 C++ 函数如下所示:

The C++ function generated for our JavaScript spec file looks like:

virtual jsi::String reverseString(jsi::Runtime &rt, jsi::String input) = 0;

你可以直接使用更底层的 jsi:: 类型 - 但为了方便起见,C++ Turbo Native 模块会自动为你将 bridge 转换为 std:: 类型。

You can directly work with the lower level jsi:: types - but for convience C++ Turbo Native Modules automatically bridge into std:: types for you.

执行

现在创建一个包含以下内容的 NativeSampleModule.h 文件:

Now create a NativeSampleModule.h file with the following content:

注意

由于当前 CMake 和 CocoaPod 设置存在差异,我们需要一些创造力来在每个平台上包含正确的 Codegen 标头。

#pragma once

#if __has_include(<React-Codegen/AppSpecsJSI.h>) // CocoaPod headers on Apple
#include <React-Codegen/AppSpecsJSI.h>
#elif __has_include("AppSpecsJSI.h") // CMake headers on Android
#include "AppSpecsJSI.h"
#endif
#include <memory>
#include <string>

namespace facebook::react {

class NativeSampleModule : public NativeSampleModuleCxxSpec<NativeSampleModule> {
public:
NativeSampleModule(std::shared_ptr<CallInvoker> jsInvoker);

std::string reverseString(jsi::Runtime& rt, std::string input);
};

} // namespace facebook::react

在这种情况下,你可以使用 bridgesjsi::String(默认值或 定制一)的任何 C++ 类型。 你不能指定不兼容的类型,例如 boolfloatstd::vector<>,因为它不是 bridgejsi::String,因此会导致编译错误。

In this case you can use any C++ type which bridges to a jsi::String - default or custom one. You can't specify an incompatible type such as bool, float or std::vector<> as it does not bridge to jsi::String and hence results in a compilation error.

现在添加一个 NativeSampleModule.cpp 文件及其实现:

Now add a NativeSampleModule.cpp file with an implementation for it:

#include "NativeSampleModule.h"

namespace facebook::react {

NativeSampleModule::NativeSampleModule(std::shared_ptr<CallInvoker> jsInvoker)
: NativeSampleModuleCxxSpec(std::move(jsInvoker)) {}

std::string NativeSampleModule::reverseString(jsi::Runtime& rt, std::string input) {
return std::string(input.rbegin(), input.rend());
}

} // namespace facebook::react

由于我们添加了在 ios 文件夹中运行的新 C++ 文件:

As we have added new C++ files run in the ios folder:

RCT_NEW_ARCH_ENABLED=1 bundle exec pod install

对于 iOS。 在 Xcode 中,它们出现在 Development Pods \ TurboModules 子文件夹中的 Pods 目标下。

for iOS. In Xcode they appear under the Pods target in the Development Pods \ TurboModules subfolder.

你现在应该能够在 Android 和 iOS 上编译你的应用。

You should now be able to compile your app both on Android and iOS.

CxxTurboModulesGuide
├── android
│ └── app
│ │── src
│ │ └── main
│ │ └── jni
│ │ ├── CMakeLists.txt
│ │ └── OnLoad.cpp
│ └── build.gradle (updated)
├── ios
│ └── CxxTurboModulesGuide
│ └── AppDelegate.mm (updated)
├── js
│ └── App.tsx|jsx (updated)
└── tm
├── CMakeLists.txt
├── NativeSampleModule.h
├── NativeSampleModule.cpp
├── NativeSampleModule.ts|js
└── TurboModules.podspec

5. 将 C++ Turbo Native 模块添加到你的应用中

出于演示目的,我们可以使用以下条目更新应用的 App.tsx|jsx

For demo purposes we can update our app's App.tsx|jsx with the following entries:

//...
import {
Colors,
DebugInstructions,
Header,
LearnMoreLinks,
ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';
+ import NativeSampleModule from './tm/NativeSampleModule';
//...
<View
style={{
backgroundColor: isDarkMode ? Colors.black : Colors.white,
}}>
+ <Section title="Cxx TurboModule">
+ NativeSampleModule.reverseString(...) ={' '}
+ {NativeSampleModule.reverseString(
+ 'the quick brown fox jumps over the lazy dog'
+ )}
+ </Section>;
<Section title="Step One">
Edit <Text style={styles.highlight}>App.tsx</Text> to change this
screen and then come back to see your edits.
</Section>
//...

运行应用以查看你的 C++ Turbo Native 模块的运行情况!

Run the app to see your C++ Turbo Native Module in action!

应用 TurboModuleProvider [可选]

添加多个 C++ Turbo Native 模块后,可以通过声明 AppTurboModuleProvider 来避免一些代码重复:

You can avoid some code duplication once you added multiple C++ Turbo Native Modules by declaring an AppTurboModuleProvider:

AppTurboModuleProvider.h
#pragma once

#include <ReactCommon/TurboModuleBinding.h>
#include <memory>
#include <string>

namespace facebook::react {

class AppTurboModuleProvider {
public:
std::shared_ptr<TurboModule> getTurboModule(
const std::string& name,
std::shared_ptr<CallInvoker> jsInvoker) const;
};

} // namespace facebook::react

并实现它:

And implementing it:

AppTurboModuleProvider.cpp
#include "AppTurboModuleProvider.h"
#include "NativeSampleModule.h"

namespace facebook::react {

std::shared_ptr<TurboModule> AppTurboModuleProvider::getTurboModule(
const std::string& name,
std::shared_ptr<CallInvoker> jsInvoker) const {
if (name == "NativeSampleModule") {
return std::make_shared<facebook::react::NativeSampleModule>(jsInvoker);
}
// Other C++ Turbo Native Modules for you app
return nullptr;
}

} // namespace facebook::react

然后在 Android 的 OnLoad.cpp 和 iOS 的 AppDelegate.mm 中重新使用它,例如,通过:

And then re-using it in OnLoad.cpp for Android and AppDelegate.mm for iOS, e.g., via:

static facebook::react::AppTurboModuleProvider appTurboModuleProvider;
return appTurboModuleProvider.getTurboModule(name, jsInvoker);

在相应的函数中。

in the corresponding functions.

调用操作系统特定的 API

只要方法签名仅使用 std::jsi:: 类型,你仍然可以在编译单元中调用操作系统特定的函数(例如,Apple 上的 NS/CF API 或 Windows 上的 Win32/WinRT API)。

You can still call OS specific functions in the compilation unit (e.g., NS/CF APIs on Apple or Win32/WinRT APIs on Windows) as long as the method signatures only use std:: or jsi:: types.

对于 Apple 特定的 API,你需要将实现文件的扩展名从 .cpp 更改为 .mm,以便能够使用 NS/CF API。

For Apple specific APIs you need to change the extension of your implementation file from .cpp to .mm to be able to consume NS/CF APIs.

扩展 C++ Turbo Native 模块

如果你需要支持某些尚不支持的类型,请查看 这另一本指南

If you need to support some types that are not supported yet, have a look at this other guide.