React native和原生之间的通信

2018-02-05 15:29:24 浏览数 (1)

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

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

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

2)使用Promise来实现。

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

关于使用回调,这是最简单的一种通信,这里可以看看官网的实现,今天要讲的是滴三种由原生模块向JavaScript发送事件。

(1)首先,你需要定义一个发送事件的方法。如下所示:

代码语言:js复制
/*原生模块可以在没有被调用的情况下往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赋值。

如下所示:

代码语言:java复制
public class MyModule extends ReactContextBaseJavaModule {  
 
 private BluetoothAdapter mBluetoothAdapter = null;  
 public MyModule(ReactApplicationContext reactContext) {  
 super(reactContext);  
 
        原生类1.MyContext=reactContext;  
 
 
    }  
.......以下写被@ReactNative所标注的方法  
............................  
...................  
}  

此时,reactContext将不会是null。也就不会报错。

(3)在某个原生函数中向JavaScript发送事件。如下所示:

代码语言:java复制
WritableMap event = Arguments.createMap();  
sendEvent(MyContext, "EventName",event);  

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

然后使用componentWillMount建立监听。

代码如下:

代码语言:js复制
componentWillMount(){    
 
                    DeviceEventEmitter.addListener('EventName', function() {    
 
                         alert("send success");    
                       });   
 
 
}  

注意:该监听必须放在class里边,和render、const对齐。

下边展示一个完整Demo,Demo功能如下:

(1)JavaScript端在监听一个事件。

(2)点击前端某行文字,调用原生方法。

(3)在原生方法中,延迟3s后向前端发送对应事件。

(4)前端接收到事件后,给出alert提示。

代码如下:

ManiActivity.Java

代码语言:js复制
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

代码语言:js复制
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

代码语言: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

代码语言: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

代码语言: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代码如下:

代码语言:java复制
/** 
 * 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,代码如下:

代码语言: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() {    
 
 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)

修改UI代码如下:

代码语言:java复制
/** 
 * 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);  

运行之后,界面刷新了。

0 人点赞