as

Settings
Sign out
Notifications
Alexa
亚马逊应用商店
AWS
文档
Support
Contact Us
My Cases
新手入门
设计和开发
应用发布
参考
支持

创建Turbo模块

创建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接口绑定到返回的对象。

步骤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

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

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。

这个过程通常为:

  1. 更改Turbo模块代码
  2. 重建Turbo模块 (npm pack)
  3. 运行npm install <.tgz的路径> from the app package
  4. 重建应用

如果应用通过符号链接使用独立的Turbo模块(这意味着依赖项通过npm install <TM程序包的路径>添加,或者应用的package.json显示的是目录路径而非.tgz的路径),或者使用诸如yarn工作区之类的设置,则无需重新安装步骤即可将更改从Turbo模块传播到应用。


Last updated: 2025年10月13日