首先,由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