React Native 不仅仅是用 JavaScript 开发原生应用。当涉及到一些复杂功能时,还可以直接调用 Android 原生的代码,这通过 NativeModule
模块来实现。
NativeModule
是 JavaScript 与原生代码通信的桥梁,我们以安卓中的 Java 代码为例,介绍下如何在 React 代码中调用 Java 代码中的方法。
安卓代码的实现主要分为三步:① 创建原生模块,② 添加原生模块,③ 注册原生模块。
创建原生模块就是新建一个 Java 类,在这个类中定义可被 JavaScript 调用的方法。该类必须继承 ReactContextBaseJavaModule
,并且类中方法必须被 @ReactMethod()
修饰才能被 JavaScript 成功调用,否则无效。
在源码目录(app/java/xxx)下新建一个 NativeClass 类,代码如下:
public class NativeClass extends ReactContextBaseJavaModule {NativeClass(ReactApplicationContext context) {super(context);}@Overridepublic String getName() {return "NativeClass";}@ReactMethod()public void getLogs(String log) {Log.d("NativeClass", "getLogs: "+log);}}
在 NativeClass 类中定义了 getLogs()
方法,该方法允许被 JavaScript 调用。
上一步创建了原生模块 NativeClass 类,现在要把它添加到 ReactPackage
中。ReactPackage 是 React Native 注册原生模块的方式。
在源码目录(app/java/xxx)下新建一个 AppPackage
类,该类继承 ReactPackage,代码如下:
public class AppPackage implements ReactPackage {@Overridepublic List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {List<NativeModule> modules = new ArrayList<>();modules.add(new NativeClass(reactContext));return modules;}@Overridepublic List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {return Collections.emptyList();}}
上述代码中,在 createNativeModules()
方法内添加了 NativeClass 模块,并返回模块列表。我们可以在此处添加多个原生模块。
React Native 通过 ReactPackage 来注册原生模块,我们需要在默认生成的 MainApplication
类中注册原生模块。
找到 new DefaultReactNativeHost()
代码块,注册上一步创建的 AppPackage 类,代码如下:
new DefaultReactNativeHost(this) {@Overrideprotected List<ReactPackage> getPackages() {@SuppressWarnings("UnnecessaryLocalVariable")List<ReactPackage> packages = new PackageList(this).getPackages();packages.add(new AppPackage());return packages;}}
上面的代码中的 getPackages()
方法,添加了 AppPackage 类并返回,这样我们模块注册的部分就实现了。
最后,因为原生代码不存在热更新,所以修改完成后,使用 yarn run android
命令重新运行项目。
JavaScript 中的实现方式比较简单,首先导入 NativeModules
:
import { NativeModules } from 'react-native';
接着在 NativeModules 中导出我们注册的 NativeClass:
const { NativeClass } = NativeModules;
现在,你可以调用 NativeClass 类中被 @ReactMethod() 修饰的任意方法了,如:
NativeClass.getLogs('呵呵');
现在打开你的 Android Studio,即可看到 Android 端打印的日志。
原生 Android 端可以接收到一些系统事件,比如电池耗尽、网络断开等。有时候需要在 React Native 中捕获这些事件,那么就可以通过原生代码主动向 JavaScript 推送事件。
(1)发送事件需要 ReactContext,首先我们在原生模块中将其保存为一个类属性:
public class NativeClass extends ReactContextBaseJavaModule {ReactApplicationContext reactContext;NativeClass(ReactApplicationContext context) {super(context);reactContext = context;}}
(2)新建 sendEvent() 方法,使用 RCTDeviceEventEmitter
在 ReactContext 下获取 JSModule,并向该 JSModule(对应 JavaScript 代码)发送事件,代码如下:
private void sendEvent(ReactContext reactContext,String eventName,WritableMap params) {reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, params);}
(3)定义一个 toToast()方法,向 JavaScript 发送一个 toast
事件并传参,方法如下:
public void toToast() {WritableMap params = Arguments.createMap();params.putInt("msg", "我来自原生模块");this.sendEvent(reactContext, "toast", params);}
(4)在 JavaScript 中通过 NativeEventEmitter
注册监听器并监听事件,代码如下:
import { NativeModules, NativeEventEmitter } from 'react-native';const { NativeClass } = NativeModules;useEffect(() => {let emitter = new NativeEventEmitter(NativeClass);let listener = emitter.addListener('toast', (event) => {console.log('收到参数:', event);});return async () => {listener.remove();};});
通过以上四个步骤,就可以实现安卓原生模块主动向 JavaScript 发送事件了。