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:
- 定义 JavaScript 规范。
- 配置 Codegen 以生成脚手架。
- 注册原生模块。
- 编写原生代码完成模块的实现。
为新架构设置测试应用
第一步,创建一个新应用:
英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:
- TypeScript
- Flow
import {TurboModule, TurboModuleRegistry} from 'react-native';
export interface Spec extends TurboModule {
readonly reverseString: (input: string) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>(
'NativeSampleModule',
);
// @flow
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
// import type {TurboModule} from 'react-native'; in future versions
import {TurboModuleRegistry} from 'react-native';
export interface Spec extends TurboModule {
+reverseString: (input: string) => string;
}
export default (TurboModuleRegistry.getEnforcing<Spec>(
'NativeSampleModule',
): Spec);
2. 代码生成配置
接下来,你需要为 代码生成器 添加一些配置。
英Next, you need to add some configuration for Codegen.
应用
使用以下条目更新应用的 package.json
文件:
英Update your app's package.json
file with the following entries:
{
// ...
"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:
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.gradle
、CMakeLists.txt
、Onload.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:
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.txt
和 Onload.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.h
和 AppSpecsJSI-generated.cpp
文件。
英You can inspect the generated AppSpecsJSI.h
and AppSpecsJSI-generated.cpp
files inside the CxxTurboModulesGuide/ios/build/generated/ios
folder.
这些文件以 AppSpecs
为前缀,因为这与之前添加到 package.json
的 codegenConfig.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.h
和 AppSpecsJSI-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
在这种情况下,你可以使用 bridges
到 jsi::String
(默认值或 定制一)的任何 C++ 类型。 你不能指定不兼容的类型,例如 bool
、float
或 std::vector<>
,因为它不是 bridge
到 jsi::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:
#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:
#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.