react-native中的NativeModules模块

2,018 阅读8分钟

NativeModules模板在官方中的解释:

Sometimes a React Native app needs to access a native platform API that is not available by default in JavaScript, for example the native APIs to access Apple or Google Pay. Maybe you want to reuse some existing Objective-C, Swift, Java or C++ libraries without having to reimplement it in JavaScript, or write some high performance, multi-threaded code for things like image processing. The NativeModule system exposes instances of Java/Objective-C/C++ (native) classes to JavaScript (JS) as JS objects, thereby allowing you to execute arbitrary native code from within JS. While we don't expect this feature to be part of the usual development process, it is essential that it exists. If React Native doesn't export a native API that your JS app needs you should be able to export it yourself!
有时,React Native 应用程序需要访问 JavaScript 默认不可用的原生平台 API,例如访问 Apple 或 Google Pay 的原生 API。也许你想重用一些现有的 Objective-C、Swift、Java 或 C++ 库,而不必在 JavaScript 中重新实现它,或者为图像处理等事情编写一些高性能、多线程代码。 NativeModule 系统将 Java/Objective-C/C++(本机)类的实例作为 JS 对象公开给 JavaScript (JS),从而允许您从 JS 中执行任意本机代码。虽然我们不希望此功能成为常规开发过程的一部分,但它的存在是必不可少的。如果 React Native 没有导出你的 JS 应用程序需要的原生 API,你应该可以自己导出它!

有两种方式来编写这个模块

  1. 在reactNative的项目中的android/ios
  2. 在npm包中,可以作为你或者其他reactnative项目依赖的程序

本项目中构建Nativemodule模块

假设从 React Native 应用程序中的 JavaScript 访问 iOS/Android 本机日历 API 以创建日历事件。

  • android
    您将创建一个本机模块,CalendarModule它允许您从 JavaScript 访问 Android 的日历 API。到最后,您将能够CalendarModule.createCalendarEvent('Dinner Party', 'My House');从 JavaScript 调用,调用创建日历事件的 Java/Kotlin 方法。

  • 首先,在 Android Studio 的 React Native 应用程序中打开 Android 项目。您可以在 React Native 应用程序中找到您的 Android 项目:

image.png

建议使用 Android Studio 编写您的本机代码。Android Studio 是专为 Android 开发打造的 IDE,使用它可以帮助您快速解决代码语法错误等小问题。

我们还建议启用Gradle Daemon以在您迭代 Java/Kotlin 代码时加速构建。

创建自定义本机模块文件

第一步是在文件夹中创建(CalendarModule.javaCalendarModule.kt)Java/Kotlin 文件android/app/src/main/java/com/your-app-name/(Kotlin 和 Java 的文件夹相同)。此 Java/Kotlin 文件将包含您的本机模块 Java/Kotlin 类。

在 Android Studio 中添加一个名为 CalendarModule.java 的类的图像。

然后添加以下内容:
java

package com.your-apps-package-name; // replace your-apps-package-name with your app’s package name  
import com.facebook.react.bridge.NativeModule;  
import com.facebook.react.bridge.ReactApplicationContext;  
import com.facebook.react.bridge.ReactContext;  
import com.facebook.react.bridge.ReactContextBaseJavaModule;  
import com.facebook.react.bridge.ReactMethod;  
import java.util.Map;  
import java.util.HashMap;  
  
public class CalendarModule extends ReactContextBaseJavaModule {  
CalendarModule(ReactApplicationContext context) {  
super(context);  
}  
}

kotlin


package com.your-apps-package-name; // replace your-apps-package-name with your app’s package name
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod

class CalendarModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {...}   

如您所见,您的CalendarModule班级扩展了ReactContextBaseJavaModule班级。对于 Android,Java/Kotlin 原生模块被编写为扩展ReactContextBaseJavaModule和实现 JavaScript 所需功能的类。

值得注意的是,从技术上讲,Java/Kotlin 类只需要扩展类BaseJavaModule或实现NativeModule接口即可被 React Native 视为 Native Module。

但是,我们建议您使用ReactContextBaseJavaModule,如上所示。ReactContextBaseJavaModule提供对ReactApplicationContext(RAC) 的访问权限,这对于需要挂接到活动生命周期方法的本机模块很有用。使用ReactContextBaseJavaModule也将使您的本机模块将来更容易类型安全。对于在未来版本中出现的本机模块类型安全,React Native 查看每个本机模块的 JavaScript 规范并生成一个抽象基类扩展ReactContextBaseJavaModule.

模块名称

Android中所有的Java/Kotlin原生模块都需要实现该getName()方法。此方法返回一个字符串,表示本机模块的名称。然后可以使用其名称在 JavaScript 中访问本机模块。例如,在下面的代码片段中,getName()返回"CalendarModule".

java

// add to CalendarModule.java  
@Override  
public String getName() {  
return "CalendarModule";  
}

kotlin

// add to CalendarModule.kt  
override fun getName() = "CalendarModule"

在js模块中可以这样子访问本机模块:

const {CalendarModule} = ReactNative.NativeModules;

将本机方法导出到JavaScript

接下来,您需要向本机模块添加一个方法,该方法将创建日历事件并可在 JavaScript 中调用。所有要从 JavaScript 调用的本机模块方法都必须用@ReactMethod.

设置一个可以在JS中调用的createCalendarEvent()方法。现在,该方法将名称和位置作为字符串。稍后将介绍参数类型选项。CalendarModule``CalendarModule.createCalendarEvent()

java

@ReactMethod
public void createCalendarEvent(String name, String location) {
}

kotlin

@ReactMethod fun createCalendarEvent(name: String, location: String) {}

在方法中添加调试日志以确认当您从应用程序调用它时已调用它。下面是一个示例,说明如何从 Android util 包中导入和使用Log类:
java

import android.util.Log;

@ReactMethod
public void createCalendarEvent(String name, String location) {
   Log.d("CalendarModule", "Create event called with name: " + name
   + " and location: " + location);
}

kotlin

import android.util.Log  
  
@ReactMethod  
fun createCalendarEvent(name: String, location: String) {  
Log.d("CalendarModule", "Create event called with name: $name and location: $location")  
}

完成本机模块的实现并将其连接到 JavaScript 后,您可以按照以下步骤从您的应用程序中查看日志

同步方法

您可以传递isBlockingSynchronousMethod = true给本机方法以将其标记为同步方法。

java

@ReactMethod(isBlockingSynchronousMethod = true)

目前,我们不建议这样做,因为同步调用方法会带来严重的性能损失,并将与线程相关的错误引入您的本机模块。此外,请注意,如果您选择启用isBlockingSynchronousMethod,您的应用将无法再使用 Google Chrome 调试器。这是因为同步方法需要 JS VM 与应用程序共享内存。对于 Google Chrome 调试器,React Native 在 Google Chrome 的 JS VM 中运行,并通过 WebSockets 与移动设备进行异步通信。

注册模块(Android 专用)

编写本机模块后,需要向 React Native 注册它。为此,您需要将本机模块添加到 aReactPackage并向 React Native 注册ReactPackage。在初始化期间,React Native 将遍历所有包,并为每个包ReactPackage注册其中的每个本机模块。

createNativeModules()React Native 调用a 上的方法ReactPackage以获取要注册的本机模块列表。对于 Android,如果模块未在 createNativeModules 中实例化和返回,则它将无法从 JavaScript 获得。

要将您的本机模块添加到ReactPackage,首先创建一个名为 (MyAppPackage.java或) 的新 Java/Kotlin 类,该类在文件夹内MyAppPackage.kt实现:ReactPackage``android/app/src/main/java/com/your-app-name/

然后添加以下内容:

java

package com.your-app-name; // replace your-app-name with your app’s name
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

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

public class MyAppPackage implements ReactPackage {

   @Override
   public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
       return Collections.emptyList();
   }

   @Override
   public List<NativeModule> createNativeModules(
           ReactApplicationContext reactContext) {
       List<NativeModule> modules = new ArrayList<>();

       modules.add(new CalendarModule(reactContext));

       return modules;
   }

}

该文件导入您创建的本机模块,CalendarModule. 然后它在函数CalendarModule内实例化createNativeModules()并将其作为要注册的列表返回NativeModules。如果您添加更多本机模块,您还可以实例化它们并将它们添加到此处返回的列表中。

值得注意的是,这种注册原生模块的方式会在应用程序启动时急切地初始化所有原生模块,这会增加应用程序的启动时间。您可以使用TurboReactPackage作为替代方案。TurboReactPackage不是createNativeModules返回实例化本机模块对象列表的 ,而是实现一个getModule(String name, ReactApplicationContext rac)在需要时创建本机模块对象的方法。TurboReactPackage 目前的实现有点复杂。除了实现一个getModule()方法之外,您还必须实现一个getReactModuleInfoProvider()方法,该方法返回包可以实例化的所有本机模块的列表以及实例化它们的函数,示例here. 同样,使用 TurboReactPackage 将使您的应用程序具有更快的启动时间,但目前编写起来有点麻烦。因此,如果您选择使用 TurboReactPackages,请谨慎行事。

要注册CalendarModule包,您必须添加MyAppPackage到 ReactNativeHost 方法返回的包列表中getPackages()。打开您的MainApplication.javaorMainApplication.kt文件,可以在以下路径中找到它:android/app/src/main/java/com/your-app-name/.

找到 ReactNativeHost 的getPackages()方法并将您的包添加到包列表getPackages()返回:

java

@Override
  protected List<ReactPackage> getPackages() {
    @SuppressWarnings("UnnecessaryLocalVariable")
    List<ReactPackage> packages = new PackageList(this).getPackages();
    // below MyAppPackage is added to the list of packages returned
    packages.add(new MyAppPackage());
    return packages;
  }

您现在已经成功注册了您的 Android 原生模块!

测试构建的

此时,您已经为 Android 中的本机模块设置了基本脚手架。通过访问本机模块并在 JavaScript 中调用其导出的方法来测试它。

在您的应用程序中找到您想要添加对本机模块createCalendarEvent()方法的调用的位置。下面是一个组件示例,NewModuleButton您可以将其添加到您的应用程序中。NewModuleButton您可以在的函数中调用本机模块onPress()

import React from 'react';
import {NativeModules, Button} from 'react-native';

const NewModuleButton = () => {
  const onPress = () => {
    console.log('We will invoke the native module here!');
  };

  return (
    <Button
      title="Click to invoke your native module!"
      color="#841584"
      onPress={onPress}
    />
  );
};

export default NewModuleButton;

为了从 JavaScript 访问您的原生模块,您需要首先NativeModules从 React Native 导入:

import {NativeModules} from 'react-native';

然后,您可以访问CalendarModule本机模块NativeModules

const {CalendarModule} = NativeModules;

现在您有了可用的 CalendarModule 本机模块,您可以调用您的本机方法createCalendarEvent()。下面将其添加到onPress()方法中NewModuleButton

const onPress = () => {
  CalendarModule.createCalendarEvent('testName', 'testLocation');
};

最后一步是重建 React Native 应用程序,以便您可以使用最新的本机代码(使用新的本机模块!)。在反应本机应用程序所在的命令行中,运行以下命令:

npx react-native run-android

官方文档