React Native 原生模块开发
在 React Native 开发中,有时应用需要访问平台原生 API,但 React Native 可能尚未提供相应模块;或者您希望复用现有 Java 代码,而不是用 JavaScript 重新实现;又或者需要实现高性能、多线程的功能,如图片处理或数据库操作。React Native 允许开发者编写原生代码来扩展功能,并访问全部平台能力。虽然这不是日常开发中的常见需求,但掌握这一技能非常重要。本文将指导您如何创建原生模块,并以 Toast 消息和 Log 日志为例,同时介绍回调函数的使用方法。
一、Toast 和 Log 模块
本指南以 Android 平台为例,展示如何从 JavaScript 调用原生 Toast 消息(屏幕下方弹出通知)和打印 Logcat 日志。
1. 创建原生模块类
首先,在项目源码目录中创建原生模块类:ToastModule.java 和 LogModule.java。原生模块是一个继承 ReactContextBaseJavaModule 的 Java 类,用于实现 JavaScript 所需功能。
ToastModule.java 类:
public class ToastModule1 extends ReactContextBaseJavaModule {
private static final String DURATION_SHORT_KEY = "SHORT";
private static final String DURATION_LONG_KEY = "LONG";
public ToastModule1(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "Toast1";
}
@Override
public Map<String, Object> getConstants() {
final Map<String, Object> constants = MapBuilder.newHashMap();
constants.put(DURATION_SHORT_KEY, Toast.LENGTH_SHORT);
constants.put(DURATION_LONG_KEY, Toast.LENGTH_LONG);
return constants;
}
@ReactMethod
public void show(final String message, final int duration) {
UiThreadUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getReactApplicationContext(), message, duration).show();
}
});
}
}
LogModule.java 类:
public class LogModule extends ReactContextBaseJavaModule {
public LogModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "Log";
}
@ReactMethod
public void d(String tag, String msg) {
Log.d(tag, msg);
}
}
2. 注册模块
接下来,在应用的 Package 类中注册这些模块,以便 JavaScript 可以访问。创建一个 AppReactPackage.java 类,并在 createNativeModules 方法中添加模块。
AppReactPackage.java 类:
public class AppReactPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new LogModule(reactContext));
modules.add(new ToastModule1(reactContext));
return modules;
}
@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
然后,在 MainActivity.java 文件中注册这个 Package,确保 React Native 实例管理器包含它。
MainActivity.java 文件(部分代码):
public class MainActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler {
private ReactRootView mReactRootView;
private ReactInstanceManager mReactInstanceManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mReactRootView = new ReactRootView(this);
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setBundleAssetName("index.android.bundle")
.setJSMainModuleName("index.android")
.addPackage(new MainReactPackage())
.addPackage(new AppReactPackage())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
mReactRootView.startReactApplication(mReactInstanceManager, "MyAwesomeApp", null);
setContentView(mReactRootView);
}
// 其他生命周期方法省略
}
3. 创建 JavaScript 封装模块
为了方便 JavaScript 调用,通常将原生模块封装成 JavaScript 模块。在应用根目录下创建 ToastAndroid.js 文件。
ToastAndroid.js 文件:
var { NativeModules } = require('react-native');
module.exports = NativeModules.ToastAndroid;
4. 在 JavaScript 中调用 API
最后,在 index.android.js 文件中调用原生模块功能。
index.android.js 文件:
'use strict';
var React = require('react');
var ReactNative = require('react-native');
var ToastAndroid = require('./ToastAndroid');
var {
Text,
View,
StyleSheet,
AppRegistry,
NativeModules,
} = ReactNative;
var Log1 = NativeModules.Log;
Log1.d("Log1", "LOG");
ToastAndroid.show("Toast1", ToastAndroid.SHORT);
class MyAwesomeApp extends React.Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.hello}>Hello, World</Text>
</View>
);
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
hello: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
AppRegistry.registerComponent('MyAwesomeApp', () => MyAwesomeApp);
运行应用后,可以在 LogCat 中查看日志输出,并看到 Toast 消息显示。
二、回调函数
原生模块还支持回调函数参数,允许将结果从 Java 层返回给 JavaScript 层。这在处理异步操作(如网络请求)时非常有用。
1. 创建带回调的原生模块
创建一个 NetModule.java 类,继承 ReactContextBaseJavaModule,实现 getName 方法返回 "Net",并暴露一个 getResult 方法,该方法接受 URL 和回调函数参数。
NetModule.java 类:
public class NetModule extends ReactContextBaseJavaModule {
private static final String MODULE_NAME = "Net";
public NetModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return MODULE_NAME;
}
@ReactMethod
public void getResult(String url, final Callback callback) {
Log.e("TAG", "正在请求数据");
new Thread(new Runnable() {
@Override
public void run() {
try {
String result = "这是结果";
Thread.sleep(1000);
callback.invoke(true, result);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
}
2. 注册模块
同样,在 AppReactPackage.java 的 createNativeModules 方法中注册 NetModule(步骤同上,略)。
3. 创建 JavaScript 封装
在 JavaScript 层新建 Net.js 文件,封装原生模块调用。
Net.js 文件:
'use strict';
var { NativeModules } = require('react-native');
var RCTNet = NativeModules.Net;
var Net = {
getResult: function (url: string, callback: Function): void {
RCTNet.getResult(url, callback);
},
};
module.exports = Net;
4. 在 JavaScript 中调用
修改 index.android.js 文件,引入 Net 模块并调用 getResult 方法。
index.android.js 文件(部分代码):
import React from 'react';
import { ... } from 'react-native';
var Net = require('./Net');
Net.getResult("http://baidu.com", (code, result) => {
console.log("callback", code, result);
});
// 其他组件代码
AppRegistry.registerComponent('AwesomeProject', () => MyAwesomeApp);
运行应用后,在 Chrome 开发者工具中可以看到回调函数输出的日志,显示请求结果。通过这种方式,您可以轻松地在 React Native 中集成原生功能,实现更复杂的跨平台交互。