[React Native]原生模块(下)
上一篇文章[React Native]原生模块(上)中我们介绍了原生模块的基本用法,本篇文章将介绍原生模块的高级用法—ReactNative与原生模块的通信方式。
由于本文涉及到很多React方面的知识,比如state、生命周期等,没有接触过的同学建议首先阅读下阮一峰老师的React入门实例教程
通信方式总共分为以下三种:
- Callback
- Promise
- Event
本文将演示原生模块使用Thread
异步执行方法后回调JS
模块,并在界面上显示出线程的名字和执行时间,效果图如下
首先,按照上一篇文章[React Native]原生模块(上)的步骤
1、新建一个ReactNative
项目
2、并创建名称为NativeAndroid
的原生模块
3、最后完成他的注册工作
此时,NativeAndroid
的代码如下
public class NativeModule extends ReactContextBaseJavaModule {
ReactApplicationContext mReactContext;
public NativeModule(ReactApplicationContext reactContext) {
super(reactContext);
mReactContext = reactContext;
}
@Override
public String getName() {
return "NativeAndroid";
}
}
4、在index.android.js
中用TouchableOpacity
包裹三个可点击的View
:Callback
、Promise
、Event
render() {
return (
<View style={styles.container}>
<TouchableOpacity onPress={()=>{
}}>
<Text style={styles.welcome}>
Callback
</Text>
</TouchableOpacity>
<TouchableOpacity onPress={()=>{
}}>
<Text style={styles.welcome}>
Promise
</Text>
</TouchableOpacity>
<TouchableOpacity onPress={()=>{
}}>
<Text style={styles.welcome}>
Event
</Text>
</TouchableOpacity>
</View>
);
}
5、在index.android.js
中新建一个state
:result
简单理解就是一个特殊的属性,一旦修改它的值,会触发render方法,这样页面就得到刷新了。对这个用法不了解的同学,可以参考阮一峰老师的React入门实例教程
// es6语法,如果使用React.createClass则使用getInitialState
constructor () {
super();
this.state = {
result: ''
}
}
render() {
return (
<View style={styles.container}>
// 此处代码省略若干行
<Text style={styles.instructions}>
{this.state.result}
</Text>
</View>
);
}
准备步骤到此结束了。
下面,我们分别介绍三种实现方式
- Callback
回调函数,对应
JS
中的function
1、在NativeModule
中新建testCallback
方法 ,包含一个参数Callback
(对应JS
的function
)
@ReactMethod
public void testCallback(final Callback callback)
{
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
callback.invoke("Callback: " + Thread.currentThread().getName() + " has slept 3 ms.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
2、在Callback
的onPresss
方法中调用testCallback
方法
<TouchableOpacity onPress={()=>{
NativeAndroid.testCallback((msg)=>{
this.setState({result: msg});
});
}}>
<Text style={styles.welcome}>
Callback
</Text>
</TouchableOpacity>
这里给testCallback
方法传递了一个箭头函数,并将返回值msg
赋值给result
,然后会触发render
方法刷新界面。
- Promise
这里涉及到
ES7
标准的JS
语法,需要配合async/await
来使用。桥接的原生方法的最后一个参数为Promise
1、在NativeModule
中新建testPromise
方法 ,包含一个参数Promise
(当然,你也可以同时传递其他参数)
@ReactMethod
public void testPromise(final Promise promise)
{
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
WritableMap map = Arguments.createMap();
map.putString("msg", "Promise: " + Thread.currentThread().getName() + " has slept 3 ms.");
promise.resolve(map);
} catch (InterruptedException e) {
promise.reject("InterruptedException", e);
}
}
}).start();
}
这里,使用promise.resolve
来传递一个WritableMap
,使用promise.reject
来传递错误信息
WritableMap类似一个字典,用来包装多个输出参数给
JS
模块
2、在index.android.js
文件的class
新建一个方法promise
,方法需要使用async
来修饰
// es7语法,Promise必须配合async使用
async promise()
{
try {
var {
msg
} = await NativeAndroid.testPromise();
this.setState({result: msg});
} catch (error) {
console.log(error);
}
}
这里,var {msg} = await NativeAndroid.testPromise();
中的msg
对应原生方法public void testPromise(final Promise promise)
的语句promise.resolve(map);
中map
传递的参数msg
,最后使用this.setState({result: msg});
来刷新界面
3、在Promise
的onPress
方法中调用promise
方法
<TouchableOpacity onPress={()=>{
this.promise();
}}>
<Text style={styles.welcome}>
Promise
</Text>
</TouchableOpacity>
- Event
1、原生模块可以在没有被调用的情况下往JavaScript发送事件通知。最简单的办法就是通过RCTDeviceEventEmitter,这可以通过ReactContext来获得对应的引用,像这样:
mReactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("事件名称", Object);
2、原生模块可以主动通知
JS
模块,这里为了简单起见,还是通过JS
触发原生方法,原生方法再发送事件到JS
模块
1、在NativeModule
中新建testEvent
方法
/**
* 发送事件到JS
* [为了简单演示,从JS调用此方法再发送事件到JS]
*/
@ReactMethod
public void testEvent()
{
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
mReactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("testEvent", "Event: " + Thread.currentThread().getName() + " has slept 3 ms.");
} catch (InterruptedException e) {
}
}
}).start();
}
2、在JS
模块组件Component
的生命周期函数componentWillMount
中注册事件监听,并在收到通知后刷新界面
componentWillMount()
{
DeviceEventEmitter.addListener('testEvent', (msg) => {
this.setState({result: msg});
});
}
当然,你需要引入DeviceEventEmitter
import {
// ...省略若干行代码
DeviceEventEmitter
} from 'react-native';
3、在Event
的onPress
方法中调用testEvent
方法
<TouchableOpacity onPress={()=>{
NativeAndroid.testEvent();
}}>
<Text style={styles.welcome}>
Event
</Text>
</TouchableOpacity>
最后,让我们再次看下最终效果图
本文的源码地址:Demo3