Android&Fire OS的React本地涡轮模块指南
#教程 #react #reactnative #android

作为React Native开发人员,我感谢框架的跨平台功能带来的灵活性。但是,有时候我确实需要访问本地功能。输入涡轮模块,反应本地人的魔术门通往本地世界。

在本文中,我将首先介绍什么是涡轮模块以及导致其创造的根本需求。从那里,我将指导您浏览一个逐步的示例,其中您将学习如何创建自定义的涡轮模块并将其集成到您的React Antive应用程序中,从而可以直接访问Android Native API。然后,该应用程序可以在下面的演示中所示的任何Android或Fire OS设备上运行:

Turbo Module demo app on Amazon Fire tablet

tldr :查看github repo here

为什么涡轮模块很重要 - 克服本机模块的局限性

以前与React Antial合作时,应用程序的本地和JavaScript层之间的通信是通过JavaScript桥实现的 - 也称为Native Modules。但是,这种方法有许多缺点:

  • 桥梁异步运行,这意味着它将多次调用到本机层,并以预定的间隔调用它们。
  • 通过桥梁的数据必须在本地侧进行序列化和避免,从而引入开销和延迟。
  • 桥没有类型的安全性。任何数据都可以在没有严格执行的情况下将其传递给它,使其可以适当地处理和处理数据。
  • 在应用程序启动期间,所有本机模块都必须加载到内存中,从而导致延迟为用户启动应用程序。

要解决这些问题,引入了React的创建者:

CodegenTurbo ModulesFabric,形成the New Architecture的react。

Image description

Turbo模块是本机模块的下一个迭代,该模块通过懒惰的加载模块来解决异步和加载问题,从而可以更快地进行应用启动。 Turbo模块通过绕过JavaScript桥并与本机代码直接通信,改善了您的应用程序的性能。

CodeGen通过在构建时间生成JavaScript接口来解决类型安全问题。这些接口确保本机代码与从JavaScript层传递的数据保持同步。此外,CodeGen促进了JSI绑定的创建,从而在无需桥梁的情况下可以在JavaScript和本机代码之间有效和直接交互。利用JSI绑定允许反应本机应用程序在本机和JavaScript层之间实现更快,更优化的通信。

此外,Fabric是用于利用Turbo模块和Codegen功能的React Native的新型渲染系统。这三个组件一起构成了React Native新体系结构的支柱,提供了增强的性能,改进的类型安全性以及简化的本机和JavaScript代码之间的互操作性。

ph,听起来很复杂! ðä,所以您可能会问自己:

在什么情况下,我需要一个涡轮模块?

  1. 访问设备API:涡轮模块可以使您可以直接访问未通过标准JavaScript模块暴露的设备API。这使您可以与设备特定的功能集成,例如访问传感器,蓝牙或其他硬件功能。虽然在某些情况下您可以使用标准的JavaScript模块,但性能可能不是最佳的,并且您可能无法访问本机API提供的所有高级功能。涡轮模块可创建对完整本地功能的访问。
  2. 本机UI组件: Turbo模块可用于创建自定义的本机UI组件,与基于JavaScript的配料相比,可提供更无缝和性能的用户体验。
  3. CPU密集型任务:如果您的应用程序执行CPU密集型任务,例如图像处理,音频/视频编码或复杂的计算,则使用Turbo模块可以帮助将这些任务卸载到本机代码,以利用本地代码设备的计算能力和优化性能。

为您的应用创建涡轮模块的步骤

让我们深入研究如何将自定义的涡轮模块添加到启用了新体系结构的React本机应用程序中。我们将设计的示例Turbo模块将拉动Android设备的型号,以便在屏幕上显示我们的应用程序。然后,我们可以在任何Android设备(包括Amazon Fire设备)上运行React Native应用程序。

先决条件

提示:删除react-native-cli package的任何旧版本,因为这可能会导致意外的构建问题。您可以使用命令npm uninstall -g react-native-cli @react-native-community/cli

步骤1:创建一个新应用程序和设置涡轮模块文件夹

  • 创建一个名为turbomoduledemo的新文件夹,其中创建一个名为deviceName的新应用程序
npx react-native@latest init DeviceName

提示:为了使涡轮模块与应用程序解耦,最好与应用程序分开定义模块,然后随后将其添加为对应用程序的依赖关系。这使您可以在需要时轻松释放它。

  • TurboModuleDemo中,创建一个称为RTNDeviceName的文件夹。 RTN代表“ React Native”,是React Native模块的推荐前缀。
  • RTNDeviceName中,创建两个子文件夹:jsandroid

您的文件夹结构应该像这样:

TurboModulesDemo
├── DeviceName
└── RTNDeviceName
    ├── android
    └── js

步骤2:JavaScript规范

如前所述,新体系结构需要指定的接口,因此对于此演示,我们将使用Typescript。然后,CodeGen将使用这些规格以强大的语言(C ++,Objective-C ++,Java)生成代码

  • js文件夹中,创建一个称为NativeGetDeviceName.ts的文件。 CodeGen只会查找与.ts.tsx扩展的模式本机{module_name}匹配的文件。
  • 将以下代码复制到文件中:

NativeGetDeviceName.ts

import type { TurboModule } from "react-native/Libraries/TurboModule/RCTExport";
import { TurboModuleRegistry } from "react-native";

export interface Spec extends TurboModule {
  getDeviceModel(): Promise<string>;
}

export default TurboModuleRegistry.get<Spec>("RTNDeviceName") as Spec | null;

让我们看一下代码。首先是导入:涡轮模块类型定义了所有涡轮模块的基本接口,而涡轮模块的JavaScript模块包含用于加载涡轮模块的功能。

文件的第二部分包含涡轮模块的接口规范。在这种情况下,界面定义了getDeviceModel函数,该功能返回了解决字符串的承诺。此接口类型必须命名为涡轮模块的规格。

最后,我们调用TurboModuleRegistry.get,传递该模块的名称,如果可用

步骤3:添加配置

接下来,您需要添加一些配置以运行CodeGen。在RTNDeviceName文件夹的根部

  • 添加带有以下内容的package.jsonfile:

package.json

{
  "name": "rtn-device",
  "version": "0.0.1",
  "description": "Get device name with Turbo Modules",
  "react-native": "js/index",
  "source": "js/index",
  "files": [
    "js",
    "android",
    "!android/build"
  ],
  "keywords": [
    "react-native",
    "android"
  ],
  "license": "MIT",
  "devDependencies": {},
  "peerDependencies": {
    "react": "*",
    "react-native": "*"
  },
  "codegenConfig": {
    "name": "RTNDeviceSpec",
    "type": "modules",
    "jsSrcsDir": "js",
    "android": {
      "javaPackageName": "com.rtndevice"
    }
  }
}

Yarn安装模块时将使用此文件。这也是包含CodeGen配置的内容 - 由codegenConfig字段指定。

  • 接下来,在Android文件夹中创建一个build.gradle文件,其中包含以下内容:

build.gradle

buildscript {
  ext.safeExtGet = {prop, fallback ->
    rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
  }
  repositories {
    google()
    gradlePluginPortal()
  }
  dependencies {
    classpath("com.android.tools.build:gradle:7.3.1")
    classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.22")
  }
}

apply plugin: 'com.android.library'
apply plugin: 'com.facebook.react'
apply plugin: 'org.jetbrains.kotlin.android'

android {
  compileSdkVersion safeExtGet('compileSdkVersion', 33)
  namespace "com.rtndevice"
}

repositories {
  mavenCentral()
  google()
}

dependencies {
  implementation 'com.facebook.react:react-native'
}

此步骤创建了一个称为DevicePackage的类,该类扩展了TurboReactPackage接口。该类充当涡轮模块和React Native应用程序之间的桥梁。有趣的是,您不一定必须完全实现包装类。即使是空的实现也足以使该应用识别涡轮模块是一种反应本机依赖性并尝试生成必要的脚手架代码。

React Antial依赖于DevicePackage接口来确定库导出的ViewManager和本机模块应使用哪个本机类。通过扩展TurboReactPackage接口,您可以确保将涡轮模块正确集成到React Antive App的体系结构中。

这意味着即使包装类似乎是空的或缺乏实现的,React Native应用仍将识别和处理涡轮模块,试图生成所需的代码以使其功能正常。

  • 创建一个名为rtndevice下的文件夹:android/src/main/java/com。在文件夹中,创建一个DevicePackage.kt文件。

DevicePackage.kt

package com.rtndevice;

import com.facebook.react.TurboReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.module.model.ReacTurboModuleoduleInfoProvider

class DevicePackage : TurboReactPackage() {
  override fun getModule(name: String?, reactContext: ReactApplicationContext): NativeModule? = null

  override fun getReactModuleInfoProvider(): ReactModuleInfoProvider? = null
}

在这些步骤的末尾,Android文件夹应该看起来像这样:

android
├── build.gradle
└── src
    └── main
        └── java
            └── com
                └── rtndevice
                    └── DevicePackage.kt

步骤4:添加本机代码

为了创建涡轮模块的最后一步,您需要编写一些本机代码将JavaScript侧连接到本机平台。要生成Android的代码,您需要调用CodeGen。

  • 来自DeviceName项目文件夹运行:
yarn add ../RTNDeviceName
cd android
./gradlew generateCodegenArtifactsFromSchema

提示:您可以通过查看:DeviceName/node_modules/rtn-device/android/build/generated/source/codegen

来验证脚手架代码

提示:打开应用程序(DeviceName)中的android/gradle.properties文件,并确保newArchEnabled属性为真。

涡轮模块的Android侧的本机代码要求您创建一个实现模块的devicemodule.kt。

  • rtndevice文件夹中创建一个DeviceModule.kt文件:
android
├── build.gradle
└── src
    └── main
        └── java
            └── com
                └── rtndevice
                    ├── DeviceModule.kt
                    └── DevicePackage.kt

  • 添加以下代码the DeviceModule.kt文件:

DeviceModule.kt

package com.rtndevice
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
import com.rtndevice.NativeGetDeviceNameSpec
import android.os.Build

class DeviceModule(reactContext: ReactApplicationContext) : NativeGetDeviceNameSpec(reactContext) {

  override fun getName() = NAME

  override fun getDeviceModel(promise: Promise) {
    val manufacturer: String = Build.MANUFACTURER
    val model: String = Build.MODEL
    promise.resolve(manufacturer + model)
  }

  companion object {
    const val NAME = "RTNDeviceName"
  }
}

此类实现了DeviceModule,该DeviceModule扩展了codegen生成的NativeGetDeviceNameSpec接口,该界面是由bativegetDeviceName typescript规范文件生成的。也是包含我们的getDeviceModel函数的类,它将用设备模型作为字符串返回承诺。

package com.rtndevice;

import com.facebook.react.TurboReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.module.model.ReactModuleInfoProvider
import com.facebook.react.module.model.ReactModuleInfo

class DevicePackage : TurboReactPackage() {
    override fun getModule(name: String?, reactContext: ReactApplicationContext): NativeModule? =
       if (name == DeviceModule.NAME) {
        DeviceModule(reactContext)
       } else {
         null
       }

     override fun getReactModuleInfoProvider() = ReactModuleInfoProvider {
       mapOf(
         DeviceModule.NAME to ReactModuleInfo(
           DeviceModule.NAME,
           DeviceModule.NAME,
           false, // canOverrideExistingModule
           false, // needsEagerInit
           true, // hasConstants
           false, // isCxxModule
           true // isTurboModule
         )
       )
     }
}

步骤5:将涡轮模块添加到您的应用

  • 从DeviceName应用程序夹重新运行:将涡轮模块添加到您的应用中:
yarn add ../RTNDeviceName

提示:确保在执行纱线之前,在您的应用程序中反映了涡轮模块的更改。

现在您可以使用涡轮模块在应用中使用getDeviceName功能!

  • 在您的App.tsx中,请致电getDeviceModel方法:

App.tsx

import React from 'react';
import {useState} from 'react';
import {SafeAreaView, StatusBar, Text, Button} from 'react-native';
import RTNDeviceName from 'rtn-device/js/NativeGetDeviceName';

const App: () => JSX.Element = () => {
  const [result, setResult] = useState<string | null>(null);

  return (
    <SafeAreaView>
      <StatusBar barStyle={'dark-content'} />
      <Text style={{marginLeft: 20, marginTop: 20}}>
        {result ?? 'Whats my device?'}
      </Text>
      <Button
        title="Compute"
        onPress={async () => {
          const value = await RTNDeviceName?.getDeviceModel();
          setResult(value ?? null);
        }}
      />
    </SafeAreaView>
  );
};
export default App;

通过在任何Android设备上运行npm run android,包括Amazon Fire OS设备:

Turbo Module demo app on Amazon Fire tablet

祝贺在应用程序中成功实现了一个简单的涡轮模块!希望在这篇文章和sample code中,您现在可以更好地了解如何将涡轮模块集成到您的项目中。

在评论中让我知道您为!

创建的涡轮模块。

ð£在anisha.dev上关注我
ð°订阅我们的AmazonAppstoreDevelopers Youtube频道
ð§注册Amazon Developer Newsletter