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

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

React—Native开发之原生模块向JavaScript发送事件的更多相关文章

  1. React Native开发入门

    目录: 一.前言 二.什么是React Native 三.开发环境搭建 四.预备知识 五.最简单的React Native小程序 六.总结 七.参考资料   一.前言 虽然只是简单的了解了一下Reac ...

  2. Hybrid App 和 React Native 开发那点事

    简介:Hybrid App(混合模式移动应用)开发是指介于Web-app.Native-App这两者之间的一种开发模式,兼具「Native App 良好用户交互体验的优势」和「Web App 跨平台开 ...

  3. React Native知识12-与原生交互

    一:原生传递参数给React Native 1:原生给React Native传参 原生给JS传数据,主要依靠属性. 通过initialProperties,这个RCTRootView的初始化函数的参 ...

  4. React Native开发技术周报2

    (1).资讯 1.React Native 0.22_rc版本发布 添加了热自动重载功能 (2).技术文章 1.用 React Native 设计的第一个 iOS 应用 我们想为用户设计一款移动端的应 ...

  5. React Native开发技术周报1

    (一).资讯 1.React Native 0.21版本发布,最新版本功能特点,修复的Bug可以看一下已翻译 重要:如果升级 Android 项目到这个版本一定要读! 我们简化了 Android 应用 ...

  6. React Native开发的通讯录应用

    React Native开发的通讯录应用(使用JavaScript开发原生iOS应用,vczero) 0.前言: 项目地址:https://github.com/vczero/React-Native ...

  7. 《React Native 精解与实战》书籍连载「Node.js 简介与 React Native 开发环境配置」

    此文是我的出版书籍<React Native 精解与实战>连载分享,此书由机械工业出版社出版,书中详解了 React Native 框架底层原理.React Native 组件布局.组件与 ...

  8. 【React Native】在原生和React Native间通信(RN调用原生)

    一.从React Native中调用原生方法(原生模块) 原生模块是JS中也可以使用的Objective-C类.一般来说这样的每一个模块的实例都是在每一次通过JS bridge通信时创建的.他们可以导 ...

  9. 【转】【React Native开发】

    [React Native开发]React Native控件之ListView组件讲解以及最齐全实例(19)  [React Native开发]React Native控件之Touchable*系列组 ...

随机推荐

  1. Swift 里字符串(八)UnicodeScalarView

    即以 Unicode Scarlar 的方式来查看字符串. /// let flag = "

  2. python3 内置函数详解

    内置函数详解 abs(x) 返回数字的绝对值,参数可以是整数或浮点数,如果参数是复数,则返回其大小. # 如果参数是复数,则返回其大小. >>> abs(-25) 25 >&g ...

  3. PKUWC 2018 铁牌记

    Day –INF: 联赛后根据分数一部分人继续停课.由于本蒟蒻撞上了狗屎运,联赛分数还行,可参加NOIWC和PKUWC,故继续停课训练.期间补全了一堆知识点,并成功翘掉期末考.(然而该还的还是要还的, ...

  4. MySql登陆密码忘记-解决方案

    方法一:MySQL提供跳过访问控制的命令行参数,通过在命令行以此命令启动MySQL服务器: safe_mysqld --skip-grant-tables& 即可跳过MySQL的访问控制,任何 ...

  5. 移植C/C++代码的十个技巧

    这篇文章是我翻译自Top 10 tips for code porting c/c++的一篇小短文,以下是翻译全文,如有错误请留言或查阅原文. 代码的可移植性基本上是指使得源代码能够在不同的平台上编译 ...

  6. C++的开源跨平台日志库glog学习研究(三)--杂项

    在前面对glog分别做了两次学习,请看C++的开源跨平台日志库glog学习研究(一).C++的开源跨平台日志库glog学习研究(二)--宏的使用,这篇再做个扫尾工作,算是基本完成了. 编译期断言 动态 ...

  7. Linq基础知识小记二

    书写Linq查询有两种方法,第一种是通过方法语法(也就是扩展方法),第二种是查询表达式语法. 1.方法语法 方法语法就是通过扩展方法和Lambda表达式来创建查询 (1).链式查询 这种查询方式很多语 ...

  8. vue-devtools必备工具

    1.github下载地址:https://github.com/vuejs/vue-devtools 2.下载安成之后打开cmd进入vue-devtools文件夹把依赖装好npm install 之后 ...

  9. 问题记录 | VScode中使用IntelliJ的快捷键

    问题记录 | VScode中使用IntelliJ的快捷键 主要想用ctrl+alt+l格式化Python代码 安装VScode的插件:IntelliJ IDEA Keybindings 安装方法: I ...

  10. protocol buffers的编码原理

    protocol buffers使用二进制传输格式传递消息,因此相比于xml,json来说要轻便很多. 示例:假设定义了一个Message message Test1 { required int32 ...