React—Native開發之原生模塊向JavaScript發送事件


首先,由RN中文網關於原生模塊(Android)的介紹能夠看到,RN前端與原生模塊之javascript

間通訊,主要有三種方法:html

 

(1)使用回調函數Callback,它提供了一個函數來把返回值傳回給JavaScript。前端

(2)使用Promise來實現。java

(3)原生模塊向JavaScript發送事件。react

 

(尊重勞動成果,轉載請註明出處http://blog.csdn.net/qq_25827845/article/details/52963594android


 

其中,在個人博客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);


(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

 

若是對你有幫助,記得點贊哦大笑

相關文章
相關標籤/搜索