跳到主要內容

原生模組

您的 React Native 應用程式碼可能需要與原生平台 API 互動,而這些 API 並非由 React Native 或現有函式庫提供。您可以使用 Turbo 原生模組自行編寫整合程式碼。本指南將說明如何編寫一個。

基本步驟如下

  1. 使用最流行的 JavaScript 類型註解語言之一:Flow 或 TypeScript,定義類型化的 JavaScript 規格
  2. 配置您的依賴管理系統以運行 Codegen,它會將規格轉換為原生語言介面;
  3. 使用您的規格編寫您的應用程式碼;以及
  4. 使用產生的介面編寫您的原生平台程式碼,以編寫您的原生程式碼並將其掛鉤到 React Native 運行時環境中。

讓我們透過建立一個範例 Turbo 原生模組來逐步完成這些步驟。本指南的其餘部分假設您已使用以下命令建立您的應用程式

shell
npx @react-native-community/cli@latest init TurboModuleExample --version 0.76.0

原生持久儲存

本指南將說明如何編寫 Web Storage APIlocalStorage 的實作。此 API 與可能在您的專案上編寫應用程式碼的 React 開發人員相關。

為了使其在行動裝置上運作,我們需要使用 Android 和 iOS API

1. 宣告類型規格

React Native 提供了一個名為 Codegen 的工具,它採用以 TypeScript 或 Flow 編寫的規格,並為 Android 和 iOS 生成平台特定的程式碼。該規格宣告了將在您的原生程式碼和 React Native JavaScript 運行時之間來回傳遞的方法和資料類型。Turbo 原生模組既是您的規格、您編寫的原生程式碼,也是從您的規格產生的 Codegen 介面。

要建立規格檔案

  1. 在您的應用程式的根資料夾內,建立一個名為 specs 的新資料夾。
  2. 建立一個名為 NativeLocalStorage.ts 的新檔案。
資訊

您可以在附錄文件中查看您可以在規格中使用的所有類型以及產生的原生類型。

以下是 localStorage 規格的實作

specs/NativeLocalStorage.ts
import type {TurboModule} from 'react-native';
import {TurboModuleRegistry} from 'react-native';

export interface Spec extends TurboModule {
setItem(value: string, key: string): void;
getItem(key: string): string | null;
removeItem(key: string): void;
clear(): void;
}

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

2. 配置 Codegen 運行

該規格由 React Native Codegen 工具使用,為我們產生平台特定的介面和樣板。為此,Codegen 需要知道在哪裡找到我們的規格以及如何處理它。更新您的 package.json 以包含

package.json
     "start": "react-native start",
"test": "jest"
},
"codegenConfig": {
"name": "NativeLocalStorageSpec",
"type": "modules",
"jsSrcsDir": "specs",
"android": {
"javaPackageName": "com.nativelocalstorage"
}
},
"dependencies": {

完成 Codegen 的所有設定後,我們需要準備我們的原生程式碼以掛鉤到我們產生的程式碼中。

Codegen 是透過 generateCodegenArtifactsFromSchema Gradle 任務執行的

bash
cd android
./gradlew generateCodegenArtifactsFromSchema

BUILD SUCCESSFUL in 837ms
14 actionable tasks: 3 executed, 11 up-to-date

當您建置 Android 應用程式時,這會自動運行。

3. 使用 Turbo 原生模組編寫應用程式碼

使用 NativeLocalStorage,以下是修改後的 App.tsx,其中包含我們想要持久保存的一些文字、一個輸入欄位和一些用於更新此值的按鈕。

TurboModuleRegistry 支援 2 種檢索 Turbo 原生模組的模式

  • get<T>(name: string): T | null,如果 Turbo 原生模組不可用,則會返回 null
  • getEnforcing<T>(name: string): T,如果 Turbo 原生模組不可用,則會拋出例外。這假設模組始終可用。
App.tsx
import React from 'react';
import {
SafeAreaView,
StyleSheet,
Text,
TextInput,
Button,
} from 'react-native';

import NativeLocalStorage from './specs/NativeLocalStorage';

const EMPTY = '<empty>';

function App(): React.JSX.Element {
const [value, setValue] = React.useState<string | null>(null);

const [editingValue, setEditingValue] = React.useState<
string | null
>(null);

React.useEffect(() => {
const storedValue = NativeLocalStorage?.getItem('myKey');
setValue(storedValue ?? '');
}, []);

function saveValue() {
NativeLocalStorage?.setItem(editingValue ?? EMPTY, 'myKey');
setValue(editingValue);
}

function clearAll() {
NativeLocalStorage?.clear();
setValue('');
}

function deleteValue() {
NativeLocalStorage?.removeItem('myKey');
setValue('');
}

return (
<SafeAreaView style={{flex: 1}}>
<Text style={styles.text}>
Current stored value is: {value ?? 'No Value'}
</Text>
<TextInput
placeholder="Enter the text you want to store"
style={styles.textInput}
onChangeText={setEditingValue}
/>
<Button title="Save" onPress={saveValue} />
<Button title="Delete" onPress={deleteValue} />
<Button title="Clear" onPress={clearAll} />
</SafeAreaView>
);
}

const styles = StyleSheet.create({
text: {
margin: 10,
fontSize: 20,
},
textInput: {
margin: 10,
height: 40,
borderColor: 'black',
borderWidth: 1,
paddingLeft: 5,
paddingRight: 5,
borderRadius: 5,
},
});

export default App;

4. 編寫您的原生平台程式碼

完成所有準備工作後,我們將開始編寫原生平台程式碼。我們分兩個部分進行

注意

本指南說明如何建立僅適用於新架構的 Turbo 原生模組。如果您需要同時支援新架構和舊版架構,請參閱我們的向後相容性指南

現在是編寫一些 Android 平台程式碼的時候了,以確保 localStorage 在應用程式關閉後仍然存在。

第一步是實作產生的 NativeLocalStorageSpec 介面

android/app/src/main/java/com/nativelocalstorage/NativeLocalStorageModule.java
package com.nativelocalstorage;

import android.content.Context;
import android.content.SharedPreferences;
import com.nativelocalstorage.NativeLocalStorageSpec;
import com.facebook.react.bridge.ReactApplicationContext;

public class NativeLocalStorageModule extends NativeLocalStorageSpec {

public static final String NAME = "NativeLocalStorage";

public NativeLocalStorageModule(ReactApplicationContext reactContext) {
super(reactContext);
}

@Override
public String getName() {
return NAME;
}

@Override
public void setItem(String value, String key) {
SharedPreferences sharedPref = getReactApplicationContext().getSharedPreferences("my_prefs", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString(key, value);
editor.apply();
}

@Override
public String getItem(String key) {
SharedPreferences sharedPref = getReactApplicationContext().getSharedPreferences("my_prefs", Context.MODE_PRIVATE);
String username = sharedPref.getString(key, null);
return username;
}

@Override
public void removeItem(String key) {
SharedPreferences sharedPref = getReactApplicationContext().getSharedPreferences("my_prefs", Context.MODE_PRIVATE);
sharedPref.edit().remove(key).apply();
}
}

接下來,我們需要建立 NativeLocalStoragePackage。它提供了一個物件,透過將我們的模組包裝為基礎原生套件,在 React Native 運行時中註冊我們的模組

android/app/src/main/java/com/nativelocalstorage/NativeLocalStoragePackage.java
package com.nativelocalstorage;

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

import java.util.HashMap;
import java.util.Map;

public class NativeLocalStoragePackage extends BaseReactPackage {

@Override
public NativeModule getModule(String name, ReactApplicationContext reactContext) {
if (name.equals(NativeLocalStorageModule.NAME)) {
return new NativeLocalStorageModule(reactContext);
} else {
return null;
}
}

@Override
public ReactModuleInfoProvider getReactModuleInfoProvider() {
return new ReactModuleInfoProvider() {
@Override
public Map<String, ReactModuleInfo> getReactModuleInfos() {
Map<String, ReactModuleInfo> map = new HashMap<>();
map.put(NativeLocalStorageModule.NAME, new ReactModuleInfo(
NativeLocalStorageModule.NAME, // name
NativeLocalStorageModule.NAME, // className
false, // canOverrideExistingModule
false, // needsEagerInit
false, // isCXXModule
true // isTurboModule
));
return map;
}
};
}
}

最後,我們需要告訴主要應用程式中的 React Native 如何找到這個 Package。我們稱之為在 React Native 中「註冊」套件。

在這種情況下,您將其新增到由 getPackages 方法返回。

資訊

稍後您將學習如何將您的原生模組作為 npm 套件發佈,我們的建置工具將為您自動連結。

android/app/src/main/java/com/turobmoduleexample/MainApplication.java
package com.inappmodule;

import android.app.Application;
import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactHost;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
import com.facebook.react.defaults.DefaultReactHost;
import com.facebook.react.defaults.DefaultReactNativeHost;
import com.facebook.soloader.SoLoader;
import com.nativelocalstorage.NativeLocalStoragePackage;

import java.util.ArrayList;
import java.util.List;

public class MainApplication extends Application implements ReactApplication {

private final ReactNativeHost reactNativeHost = new DefaultReactNativeHost(this) {
@Override
public List<ReactPackage> getPackages() {
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
packages.add(new NativeLocalStoragePackage());
return packages;
}

@Override
public String getJSMainModuleName() {
return "index";
}

@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}

@Override
public boolean isNewArchEnabled() {
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
}

@Override
public boolean isHermesEnabled() {
return BuildConfig.IS_HERMES_ENABLED;
}
};

@Override
public ReactHost getReactHost() {
return DefaultReactHost.getDefaultReactHost(getApplicationContext(), reactNativeHost);
}

@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, false);
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
// If you opted-in for the New Architecture, we load the native entry point for this app.
DefaultNewArchitectureEntryPoint.load();
}
}
}

您現在可以在模擬器上建置並運行您的程式碼

bash
npm run android