首先,由RN中文网关于原生模块(Android)的介绍能够看到,RN前端与原生模块之javascript
间通讯,主要有三种方法:html
(1)使用回调函数Callback,它提供了一个函数来把返回值传回给JavaScript。前端
(2)使用Promise来实现。java
(3)原生模块向JavaScript发送事件。react
(尊重劳动成果,转载请注明出处http://blog.csdn.net/qq_25827845/article/details/52963594)android
其中,在个人博客React-Native开发之原生模块封装(Android)升级版 较为详细的阐述了如何使用回调函数Callbackgit
来将数据传向JavaScript 端。github
可是有一个比较难以解决的问题是:shell
callback并不是在对应的原生函数返回后当即被执行,由于跨语言通信是异步的,这个执行过程会经过消息循环来进行。segmentfault
也就是说,当咱们在前端调用原生函数后,原生函数的返回值可能尚未得出,然而已经在执行Callback了,因此我
们并不能获得准确的返回值。由于回调函数Callback 对原生模块 来讲是被动的,由前端来主动调用回调函数,而后
获得返回值,实现原生模块和前端的数据交互。
原生模块和前端数据交互的第三种方法:原生模块向JavaScript发送事件则是一种主动机制。当原生函数执行到任
何一步时均可以主动向前端发送事件。前端须要监视该事件,当前端收到某肯定事件时,则可准确获知原生函数目前
执行的状态以及获得原生函数的返回值等。这样前端能够进行下一步的操做,如更新UI等。
接下来咱们看一下如何由原生模块向JavaScript前端发送事件。
(1)首先,你须要定义一个发送事件的方法。以下所示:
/*原生模块能够在没有被调用的状况下往JavaScript发送事件通知。 最简单的办法就是经过RCTDeviceEventEmitter, 这能够经过ReactContext来得到对应的引用,像这样:*/ public static void sendEvent(ReactContext reactContext, String eventName, @Nullable WritableMap paramss) { System.out.println("reactContext="+reactContext); reactContext .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) .emit(eventName, paramss); }
其中方法名能够任意,可是参数不可改变。该方法能够放在你要复用的原生类中(即为原生类1)。
须要注意的是,因为版本问题,该函数中的参数reactContext有可能为null,此时会报NullPointException的错误。因此咱们须要手动给reactContext赋值,见步骤2.
(2)咱们在原生类1中,定义变量public static ReactContext MyContext;
而后在咱们自定义的继承至ReactContextBaseJavaModule的类中给reactContext赋值。
以下所示:
public class MyModule extends ReactContextBaseJavaModule { private BluetoothAdapter mBluetoothAdapter = null; public MyModule(ReactApplicationContext reactContext) { super(reactContext); 原生类1.MyContext=reactContext; } .......如下写被@ReactNative所标注的方法 ............................ ................... }
此时,reactContext将不会是null。也就不会报错。
(3)在某个原生函数中向JavaScript发送事件。以下所示:
WritableMap event = Arguments.createMap(); sendEvent(MyContext, "EventName",event);
而后使用componentWillMount创建监听。
代码以下:
componentWillMount(){ DeviceEventEmitter.addListener('EventName', function() { alert("send success"); }); }
注意:该监听必须放在class里边,和render、const对齐。
下边展现一个完整Demo,Demo功能以下:
(1)JavaScript端在监听一个事件。
(2)点击前端某行文字,调用原生方法。
(3)在原生方法中,延迟3s后向前端发送对应事件。
(4)前端接收到事件后,给出alert提示。
代码以下:
ManiActivity.java
package com.ywq; import com.facebook.react.ReactActivity; public class MainActivity extends ReactActivity { /** * Returns the name of the main component registered from JavaScript. * This is used to schedule rendering of the component. */ @Override protected String getMainComponentName() { return "ywq"; } }
ManiApplication.java
package com.ywq; import android.app.Application; import android.util.Log; import com.facebook.react.ReactApplication; import com.facebook.react.ReactInstanceManager; import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactPackage; import com.facebook.react.shell.MainReactPackage; import java.util.Arrays; import java.util.List; public class MainApplication extends Application implements ReactApplication { private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { @Override protected boolean getUseDeveloperSupport() { return BuildConfig.DEBUG; } @Override protected List<ReactPackage> getPackages() { return Arrays.<ReactPackage>asList( new MainReactPackage(), new MyPackage() ); } }; @Override public ReactNativeHost getReactNativeHost() { return mReactNativeHost; } }
MyModule.java
package com.ywq; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; /** * Created by Administrator on 2016/10/30. */ public class MyModule extends ReactContextBaseJavaModule { public MyModule(ReactApplicationContext reactContext) { super(reactContext); //给上下文对象赋值 Test.myContext=reactContext; } @Override public String getName() { return "MyModule"; } @ReactMethod public void NativeMethod() { //调用Test类中的原生方法。 new Test().fun(); } }
MyPackage.java
package com.ywq; import com.facebook.react.ReactPackage; import com.facebook.react.bridge.JavaScriptModule; 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; /** * Created by Administrator on 2016/10/30. */ public class MyPackage implements ReactPackage { @Override public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) { List<NativeModule> modules=new ArrayList<>(); modules.add(new MyModule(reactContext)); return modules; } @Override public List<Class<? extends JavaScriptModule>> createJSModules() { return Collections.emptyList(); } @Override public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { return Collections.emptyList(); } }
Test.java
package com.ywq; import android.provider.Settings; import android.support.annotation.Nullable; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.WritableMap; import com.facebook.react.modules.core.DeviceEventManagerModule; /** * Created by Administrator on 2016/10/30. */ public class Test { //定义上下文对象 public static ReactContext myContext; //定义发送事件的函数 public void sendEvent(ReactContext reactContext, String eventName, @Nullable WritableMap params) { System.out.println("reactContext="+reactContext); reactContext .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) .emit(eventName,params); } public void fun() { //在该方法中开启线程,而且延迟3秒,而后向JavaScript端发送事件。 new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } //发送事件,事件名为EventName WritableMap et= Arguments.createMap(); sendEvent(myContext,"EventName",et); } }).start(); } }
前端index.android.js代码以下:
/** * Sample React Native App * https://github.com/facebook/react-native * @flow */ import React, { Component } from 'react'; import { AppRegistry, StyleSheet, Text, DeviceEventEmitter, NativeModules, View } from 'react-native'; export default class ywq extends Component { componentWillMount(){ //监听事件名为EventName的事件 DeviceEventEmitter.addListener('EventName', function() { alert("send success"); }); } constructor(props) { super(props); this.state = { content: '这个是预约的接受信息', } } render() { return ( <View style={styles.container}> <Text style={styles.welcome} onPress={this.callNative.bind(this)} > 当你点个人时候会调用原生方法,原生方法延迟3s后会向前端发送事件。 前端一直在监听该事件,若是收到,则给出alert提示! </Text> <Text style={styles.welcome} > {this.state.content} </Text> </View> ); } callNative() { NativeModules.MyModule.NativeMethod(); } } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#F5FCFF', }, welcome: { fontSize: 20, textAlign: 'center', margin: 10, }, instructions: { textAlign: 'center', color: '#333333', marginBottom: 5, }, }); AppRegistry.registerComponent('ywq', () => ywq);
运行结果以下所示:
点击以前:
调用原生方法而且等待3s后:
再说一个值得注意的地方,通常咱们在接收到原生模块主动发来的事件时,都会进行一些操做,如更新UI,而不只仅是弹出alert 。
例如咱们须要更新UI,代码以下:
/** * Sample React Native App * https://github.com/facebook/react-native * @flow */ import React, { Component } from 'react'; import { AppRegistry, StyleSheet, Text, DeviceEventEmitter, NativeModules, View } from 'react-native'; export default class ywq extends Component { componentWillMount(){ //监听事件名为EventName的事件 DeviceEventEmitter.addListener('EventName', function() { this.showState(); alert("send success"); }); } constructor(props) { super(props); this.state = { content: '这个是预约的接受信息', } } render() { return ( <View style={styles.container}> <Text style={styles.welcome} onPress={this.callNative.bind(this)} > 当你点个人时候会调用原生方法,原生方法延迟3s后会向前端发送事件。 前端一直在监听该事件,若是收到,则给出alert提示! </Text> <Text style={styles.welcome} > {this.state.content} </Text> </View> ); } callNative() { NativeModules.MyModule.NativeMethod(); } showState() { this.setState({content:'已经收到了原生模块发送来的事件'}) } } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#F5FCFF', }, welcome: { fontSize: 20, textAlign: 'center', margin: 10, }, instructions: { textAlign: 'center', color: '#333333', marginBottom: 5, }, }); AppRegistry.registerComponent('ywq', () => ywq);
很明显,咱们的本意是:当收到事件时,改变一个文本框的内容,即更新UI。
运行结果以下,说明在此function中不能使用this,也就是咱们并不能更新UI。
那咱们能作到在接收到事件后更新UI等后续操做吗?
能!!!
如何作?
答:使用胖箭头函数(Fat arrow functions)。
胖箭头函数,又称箭头函数,是一个来自ECMAScript 2015(又称ES6)的全新特性。有传闻说,箭头函数的语法=>,是受到了CoffeeScript 的影响,而且它与CoffeeScript中的=>语法同样,共享this上下文。
箭头函数的产生,主要由两个目的:更简洁的语法和与父做用域共享关键字this。
具体给参考 JavaScript ES6箭头函数指南
修改UI代码以下:
/** * Sample React Native App * https://github.com/facebook/react-native * @flow */ import React, { Component } from 'react'; import { AppRegistry, StyleSheet, Text, DeviceEventEmitter, NativeModules, View } from 'react-native'; export default class ywq extends Component { componentWillMount(){ //监听事件名为EventName的事件 DeviceEventEmitter.addListener('EventName', ()=> { this.showState(); alert("send success"); }); } constructor(props) { super(props); this.state = { content: '这个是预约的接受信息', } } render() { return ( <View style={styles.container}> <Text style={styles.welcome} onPress={this.callNative.bind(this)} > 当你点个人时候会调用原生方法,原生方法延迟3s后会向前端发送事件。 前端一直在监听该事件,若是收到,则给出alert提示! </Text> <Text style={styles.welcome} > {this.state.content} </Text> </View> ); } callNative() { NativeModules.MyModule.NativeMethod(); } showState() { this.setState({content:'已经收到了原生模块发送来的事件'}) } } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#F5FCFF', }, welcome: { fontSize: 20, textAlign: 'center', margin: 10, }, instructions: { textAlign: 'center', color: '#333333', marginBottom: 5, }, }); AppRegistry.registerComponent('ywq', () => ywq);
代码的不一样之处就是使用 ()=>来替代function()
运行结果以下,由图能够看出,咱们文本框中的内容已经发生了改变,成功更新了UI界面。
至此,实现了原生模块主动向JavaScript发送事件,而且实现了接收事件以后的一些更新UI等操做。
若是不懂React-Native如何复用原生函数,请查看本博客这篇文章。
本博客源码详见github:https://github.com/chaohuangtianjie994/React-Native-Send-Event-from-Native-Module