React—Native开发之原生模块向JavaScript发送事件

来源:互联网 发布:乐之网络 编辑:程序博客网 时间:2024/09/21 08:18


首先,由RN中文网关于原生模块(Android)的介绍可以看到,RN前端与原生模块之

间通信,主要有三种方法:

 

(1)使用回调函数Callback,它提供了一个函数来把返回值传回给JavaScript。

(2)使用Promise来实现。

(3)原生模块向JavaScript发送事件。

 

(尊重劳动成果,转载请注明出处http://blog.csdn.net/qq_25827845/article/details/52963594


 

其中,在我的博客React-Native开发之原生模块封装(Android)升级版 较为详细的阐述了如何使用回调函数Callback

来将数据传向JavaScript 端。

 

但是有一个比较难以解决的问题是:

         callback并非在对应的原生函数返回后立即被执行,因为跨语言通讯是异步的,这个执行过程会通过消息循环来进行。

 

也就是说,当我们在前端调用原生函数后,原生函数的返回值可能还没有得出,然而已经在执行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);


(4)在RN前端监听事件。首先导入DeviceEventEmitter,即import{ DeviceEventEmitter } from 'react-native'

然后使用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如何复用原生函数,请查看本博客这篇文章。

React-Native开发之原生模块封装(Android)升级版 

 

本博客源码详见github:https://github.com/chaohuangtianjie994/React-Native-Send-Event-from-Native-Module

 

如果对你有帮助,记得点赞哦大笑


















7 1
原创粉丝点击