zoukankan      html  css  js  c++  java
  • React—Native开发之原生模块向JavaScript发送事件


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

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


















  • 相关阅读:
    Hack The Box——Traverxec
    Hack The Box——AI
    Hack The Box——Json
    BZOJ1036 树的统计Count
    BZOJ1036 树的统计Count
    BZOJ1036 树的统计Count
    BZOJ1036 树的统计Count
    .net 面试题
    .net 面试题
    .net 面试题
  • 原文地址:https://www.cnblogs.com/lanzhi/p/6467245.html
Copyright © 2011-2022 走看看