创建Turbo模块
可以在独立库中编写Turbo模块,也可以将其作为应用程序包的一部分编写。我们将这些模块分别称为“独立”和“应用内”Turbo模块。
Vega SDK提供了在独立库中创建Turbo模块的模板,但您可以选择最适合您的项目的方式。以下步骤涵盖了两种方式。完成后,目录结构将如以下所示。
[project-root]/
├─ kepler/
│ ├─ turbo-modules/
│ │ ├─ generated/
│ │ │ ├─ (C++ specifications)
│ │ │
│ │ ├─ (C++ implementations)
│ │
│ ├─ AutoLinkInit.cpp (autolink setup)
│
├─ src/
│ ├─ turbo-modules/
│ │ ├─ (TypeScript interface files)
│ │
│ ├─ App.tsx (only for in-app Turbo Modules)
│
├─ tst/
│ ├─ (JS test files)
│
├─ .eslintrc
├─ .prettierrc
├─ babel.config.js
├─ CMakeLists.txt
├─ jest.config.json
├─ metro.config.js
├─ package.json
├─ react-native.config.js
├─ tsconfig.json
├─ tsconfig-build.json
设置
应用内Turbo模块
对于应用内模块,请按照构建应用中的说明创建启动应用。
独立Turbo模块
对于独立模块,可以从Turbo模块模板开始。在程序包的根目录下,运行下面的命令以查看可用模板的列表。
kepler project list-templates
对于Turbo模块,您将选择以下选项之一来生成项目:
idl-turbo-module: 如果您正在使用操作系统API (IDL)basic-turbo-module:如果您要用自己的代码创建Turbo模块
成功执行命令后,输出目录将包含一个新的Turbo模块项目。
IDL Turbo模块
kepler project generate -t idl-turbo-module -n <CamelCaseProjectName> --idlPackage <ReverseDNSNameForIDLPackage>
IDL Turbo模块模板接受以下所示的其他参数:
-
--idlPackage: 要在Turbo模块中使用的IDL程序包。它的格式采用反向DNS表示法,例如com.amazondeveloper.kepler.foo。 -
--outputDir:[可选] 生成项目的目录。如果不指定,则项目将在您从中运行命令的文件夹中生成。
模板生成的项目仅声明提供的OKIDL接口上的依赖项。要使用接口的功能,需要开发者更改Turbo模块C++实现文件中的代码。但是,即使没有这些更改,项目也会查找所请求的接口,并在从模板生成该接口后成功构建。
示例
kepler project generate -t idl-turbo-module -n TestIdlTm --idlPackage com.amazon.kepler.i18n.text.message_format_classic
普通Turbo模块
kepler project generate -t basic-turbo-module -n <CamelCaseProjectName> --idlPackage <ReverseDNSNameForIDLPackage>
普通Turbo模块模板接受以下所示的其他参数:
--outputDir:[可选] 生成项目的目录。如果不指定,则项目将在您从中运行命令的文件夹中生成。
构建Turbo模块
步骤1: 安装TurboModuleAPI
如果您使用模板创建了Turbo模块,@amazon-devices/keplerscript-turbomodule-api程序包则会在package.json的依赖项部分列出,您可以跳过此步骤。
在程序包的根目录下,运行以下命令。
npm install --save @amazon-devices/keplerscript-turbomodule-api
步骤2: 在TypeScript中创建接口定义
如果您使用了Turbo模块模板,则此文件已经存在,但必须对其进行修改以满足您的需求。文件NativePinger.ts(注意: 文件将根据您传递给模板的名称命名)定义了原生模块的JavaScript接口。按照惯例,它位于src/turbo-modules。
接口应如下所示:
import type {KeplerTurboModule} from '@amazon-devices/keplerscript-turbomodule-api';
import {TurboModuleRegistry} from '@amazon-devices/keplerscript-turbomodule-api';
export interface Pinger extends KeplerTurboModule {
ping: () => number;
}
export default TurboModuleRegistry.getEnforcing<Pinger>('Pinger');
上面的代码定义了一个名为Pinger的接口,该接口公开了一个返回数字的ping函数。有关可用返回类型的更多信息,请参阅原生代码中的JavaScript类型。
TurboModuleRegistry.getEnforcing使用'Pinger'作为查找键来加载模块的原生对象,如果找不到则会引发异常。然后,我们将Pinger接口绑定到返回的对象。
TurboModuleRegistry使用的字符串相匹配,也不必与以后使用的C++类或文件名相匹配。下一步中的Codegen工具在C++存根代码中生成匹配条目。但是,稍后在自动链接配置中必须再次使用此名称。步骤3: 运行Codegen来生成原生脚手架代码
TurbomoduleAPI库包含一个Codegen工具,让您可以基于TypeScript接口生成C++脚手架代码。生成的代码定义Turbo模块的C++规范。要使用Codegen,请运行以下命令。
npx keplerscript-turbomodule-api codegen src/turbo-modules/<NativePinger.ts> \
-o kepler/turbo-modules/ \
--new \
--namespace PingerTurboModule
Not Implemented(未实现)错误。Codegen一次只能接受一个TypeScript接口。如果您在同一程序包中创作多个Turbo模块,您可以使用不同的参数多次运行Codegen。
有关Codegen工具的更多信息,可通过运行npx keplerscript-turbomodule-api codegen -h来获得,或从Vega Codegen常见问题解答获得。
步骤4: 实现C++函数
通常,您无需手动修改Codegen在kepler/turbo-modules/generated/目录下创建的文件。如果已修改,请注意重新运行Codegen将覆盖这些文件。为避免丢失更新,请将修改后的文件缓存在其他地方,并在重新运行Codegen后重新应用修改。
kepler/turbo-modules/目录中的C++ (.h/.cpp) 文件是您实现接口的位置。您需要填写上一步生成的函数体。
// kepler/turbo-modules/Pinger.cpp
double Pinger::ping() {
return 0;
}
TurboModuleRegistry键的名称不同,则kepler/turbo-modules中有其他现有文件,例如TestIdlTm.h/cpp。可以删除这些文件,以支持Codegen创建的文件。IDL
如果您的Turbo模块通过IDL使用操作系统API,则需要添加导入项、实例化IDL组件并根据需要使用API。
示例
添加导入项。
// kepler/turbo-modules/Pinger.h
// 导入IDL框架头文件 (APMF)
#include <apmf/apmf_base.h>
#include <apmf/ptr.h>
#include <apmf/process.h>
...
class Pinger: public com::amazon::kepler::turbomodule::KeplerTurboModule {
public:
...
// 添加成员变量
apmf::Ptr<apmf::iface::com::amazon::apmf::IStandard> component;
...
}
实例化IDL组件并使用API。
// kepler/turbo-modules/Pinger.cpp
// 导入要使用的IDL程序包。
/ /请注意,IHello.h是一个示例,可能并不存在。
#include <apmf/iface/com/amazon/kepler/foo/IHello.h>
using apmf::iface::com::amazon::kepler::foo::IHello;
...
Pinger::Pinger() {
// 在构造函数中初始化“component”
auto const process = apmf::GetProcessObject();
this->component = process->getComponent("/com.amazondeveloper.kepler.foo");
}
...
// 可以使用“component”来访问API。
// 请注意,不能保证以下调用可用,仅作为示例。
auto const hello = this->component.TryQueryInterface<IHello>();
return hello->getHelloString();
...
com.amazondeveloper.kepler.foo应匹配您传递给模板的--idlPackage(如果有)。
如需更完整的示例,请参阅国际化开发者指南。
步骤5: 实现JavaScript层
您创建的TypeScript接口提供类型信息,允许从JavaScript调用原生对象。对于诸如错误处理、JavaScript日志记录、可选参数或任何不支持类型的参数预处理等操作的原生方法,您可能需要在这些方法上设置附加的JavaScript逻辑层。即使您现在不需要这样的逻辑,如果添加一个基本的包装器来分离JavaScript和原生API,也可以更灵活地应对未来的API变化。
import PingerModule from './NativePinger';
class Pinger {
// 这只是转发到原生模块,但您可以进行更复杂的操作,
// 甚至可以提供不需要原生模块的仅限JS的实用工具。
ping(): number {
return PingerModule.ping();
}
}
export default new Pinger();
您将为Turbo模块的用户导出一个包装器实例,在用户和原生对象之间提供一个层。例如,独立的Turbo模块程序包可能在其index.ts中具有export {默认为Pinger} from './turbo-modules/Pinger'。
添加JavaScript测试
您可以使用Jest等框架编写JavaScript代码。使用Jest运行测试时,C++ Turbo模块实例将不存在,因此您需要为JavaScript测试模拟它。
以下示例展示了如何模拟C++ Turbo模块实例。
// 在测试文件中,
jest.mock('<NativePinger.ts的路径>');
您可以在Turbo模块旁的__mocks__子目录下为Turbo模块添加手动模拟(仅提供英文版),例如src/turbo-modules/__mocks__/NativePinger.ts。
可以向您的Turbo模块的使用者提供模拟,以方便他们自己进行测试。例如,您可以提供一个模拟文件,他们可以将其传递至Jest的setupFiles。
步骤6: 添加构建配置
如果您是通过模板生成Turbo模块,则此步骤应该已经完成。但是,如果您对模板提供的代码或指定的其他Codegen参数进行了调整,您仍有可能会发现这些信息很有用。
自动链接
Vega Turbo模块是自动链接的,这意味着原生模块不是由应用程序直接链接的,而是由适用于Vega的React Native根据应用的依赖项和程序包配置加载的。作为Turbo模块开发者,您需要添加一些Turbo模块注册和配置代码。
步骤1: 在AutoLinkInit.cpp中注册模块
在kepler/AutoLinkInit.cpp中,您应该调整以下代码,以适应您的Turbo模块。
#include <kepler/turbomodule/KeplerTurboModuleRegistration.h>
// 您的模块实现的头文件
#include "turbo-modules/Pinger.h"
extern "C" {
__attribute__((visibility("default"))) void
autoLinkKeplerTurboModulesV1() noexcept {
KEPLER_REGISTER_TURBO_MODULE(
PingerTurboModule, /* 命名空间 */
Pinger /* 类名 */
);
// 如果您的程序包中有多个Turbo模块,请继续在此处注册它们。
}
}
命名空间和类名必须与传递给Codegen的参数相匹配。如果您没有传递类名,Codegen会使用TurboModuleRegistry.getEnforcing中的字符串,您可以像上面一样使用KEPLER_REGISTER_TURBO_MODULE(<命名空间>, <类名>)。如果传递的类名与TurboModuleRegistry使用的字符串不同,则可以使用KEPLER_REGISTER_TURBO_MODULE_WITH_NAME(<TurboModuleRegistry名称字符串>, <命名空间>, <类名>) 宏。
KEPLER_REGISTER_TURBO_MODULE(
"myName", /* TurboModuleRegistry字符串 */
PingerTurboModule, /* 命名空间 */
PingerModule /* 类名 */
);
步骤2: 在react-native.config.js中添加元数据
所有Turbo模块类型都在react-native.config.js中指定其自动链接配置。
适用于独立的Turbo模块库。
module.exports = {
dependency: {
platforms: {
kepler: {
autolink: {
"com.companyname.pinger": { // 此名称仅用于自动链接元数据。
// 我们建议使用反向DNS以避免命名冲突。
"libraryName": "libPinger.so", // 此项来自CMakeLists库名称
"provider": "application",
"linkDynamic": true,
"turbomodules": ["Pinger"] // 此项来自TurboModuleRegistry。
// 如果您有多个Turbo模块,请展开此列表。
}
},
},
},
},
};
适用于应用内Turbo模块。
{
...
"kepler": {
"keplerInitializationSharedLibrary": "com.companyname.pinger",
"autolink": {
"com.companyname.pinger": {
"libraryName": "libPinger.so",
"provider": "application",
"linkDynamic": true,
"turbomodules": ["Pinger"]
}
}
...
}
}
动态链接
linkDynamic控制自动链接库是在应用启动时加载(增加启动延迟),还是在JavaScript请求Turbo模块时加载。无论您的库中的设置如何,应用程序都可以覆盖它们自己的react-native.config.js中的值,从而使用能更好地满足其需求的值。如果您不确定,请将默认值设置为true。
CMakeLists.txt
CMakeLists.txt定义了项目的构建过程,并包含CMake用来为各种编译器和IDE生成“makefile”或项目文件的命令和指令。您的项目根目录中应该有一个CMakeLists.txt,和以下所示类似。
cmake_minimum_required(VERSION 3.19)
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
project(pinger-module # 这是库名称
VERSION 1.0
LANGUAGES CXX C)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# 在此处添加任何其他头文件
set(HEADERS
kepler/turbo-modules/generated/PingerSpec.h
kepler/turbo-modules/Pinger.h
)
# 在此处添加任何其他源文件
set(SOURCES
kepler/turbo-modules/generated/PingerSpec.cpp
kepler/turbo-modules/Pinger.cpp
kepler/AutoLinkInit.cpp
)
find_package(turbomoduleAPI CONFIG REQUIRED)
kepler_add_turbo_module_library(pinger-module ${HEADERS} ${SOURCES})
# 针对IDL
# find_package(Apmf REQUIRED)
# find_package(Vega REQUIRED)
#
# kepler_add_idl_import_library(
# idl_pkg
# PACKAGES
# <IDL程序包> #例如com.amazon.kepler.i18n.text.message_format_classic
# # 在此处添加其他IDL程序包
# )
#
# target_link_libraries(pinger-module PRIVATE idl_pkg)
install(TARGETS pinger-module)
此示例对项目的所有源使用单个CMakeLists.txt文件,但您可以选择根据项目需求对其进行拆分。
构建目标选择
对于应用内的Turbo模块,应按照应用设置说明完成构建目标选择。
可以通过package.json或CLI构建标记来指定构建目标选择。
步骤7: 构建Turbo模块
npm install
npm pack
您可以使用package.json脚本进行调整,以运行团队中常用的命令,也可以逐步运行现有脚本。
步骤8: 在应用中使用新的Turbo模块
对于应用内Turbo模块,您可以使用相对路径导入模块,然后像调用任何标准JS模块一样调用它。
import Pinger from ‘<Pinger.ts的路径>’;
Pinger.ping();
对于独立的Turbo模块库,您需要先将库程序包安装到您的应用中。您可以安装由npm pack生成的.tgz文件,也可以使用诸如yarn工作区或符号链接之类的设置来使用Turbo模块库,而无需将其发布到注册表。
npm install --save <pinger.tgz的路径/Pinger程序包的路径/ ‘@amazon-devices/pinger’>
然后,像在任何标准JS程序包中一样导入和使用它。
// 如果您的Turbo模块是默认的导出
import Pinger from ‘pinger’;
// 如果您的Turbo模块是命名导出
import { Pinger } from ‘pinger’;
Pinger.ping()
您需要模拟原生Turbo模块,才能用Jest通过应用测试。
jest.mock(‘@amazon-devices/pinger/dist/turbo-modules/NativePinger’);
重建
如果您使用npm pack生成的.tgz文件将独立的Turbo模块安装到应用,然后对Turbo模块进行更改,则需要在重建Turbo模块后重新安装新的.tgz。
这个过程通常为:
- 更改Turbo模块代码
- 重建Turbo模块 (npm pack)
- 运行
npm install <.tgz的路径> from the app package - 重建应用
如果应用通过符号链接使用独立的Turbo模块(这意味着依赖项通过npm install <TM程序包的路径>添加,或者应用的package.json显示的是目录路径而非.tgz的路径),或者使用诸如yarn工作区之类的设置,则无需重新安装步骤即可将更改从Turbo模块传播到应用。
相关主题
Last updated: 2025年10月13日

