React native 與Android原生互動方式(一)

weixin_34321977發表於2017-06-10

前言##

最近在做React Native開發的時候避免不了的需要原生模組和JS之間進行互動,其實RN和原生的通訊大致分為兩種情況;一種是Android主動向RN端傳送事件和資料,另外一種是RN端被動向Android詢問獲取事件和資料,接下來的幾篇文章將向大家分享原生模組向JS互動的幾種方式。

1、傳送事件的方式

2、callback方式

3、Promise方式

由於篇幅原因本文只對傳送事件的方式進行詳細介紹

傳送事件的方式

使用了React Native的RCTDeviceEventEmitter,通過訊息機制來實現,原生可以向JS傳遞事件而不需要直接的呼叫,就像我們android中的廣播。
下面通就像大家暫時通過RCTDeviceEventEmitter來向JS傳遞事件。
原生模組中(Android端):
首先定義Module類:MyModule.java

public class MyModule extends ReactContextBaseJavaModule {
    private final String TAG = MyModule.class.getName();

    public MyModule(ReactApplicationContext reactContext) {
        super(reactContext);

        //給上下文物件賦值
        Test.mContext = reactContext;

    }

    //不能用return tag 來實現 不然不能呼叫方法...原因不理解
    @Override
    public String getName() {
        return "MyModule";
    }


    @ReactMethod
    public void callNativeBySend(){
        //呼叫Test類中的原生方法。
        Log.e("wangfang","called send");
        new Test().fun();
    }


}

傳送事件的程式碼在Test類中,
Test.java程式碼段如下:


public class Test {

    public static ReactContext mContext;
    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(mContext,"EventName",et);


            }
        }).start();

    }
    //定義傳送事件的函式
    public  void sendEvent(ReactContext reactContext, String eventName, @Nullable WritableMap params)
    {
        System.out.println("reactContext="+reactContext);

        reactContext
                .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                .emit(eventName,params);//原生調Rn
    }
}

從上述程式碼可以看到,原生向JS模組傳送了一個名為“EventName”的事件,並攜帶了"et"作為引數,那大家肯定會問JS中如何接收事件並進行處理呢,別急。。。。
先把原生的程式碼說完,得把模組新增到包中
MyPackage程式碼如下:

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();
    }
}

接著RN依附的Activity程式碼如下:

public class RnAndroidCommuActivity extends ReactActivity{


    @Override
    public String getMainComponentName() {
        return "testNative";
    }
}

原生的Activity的程式碼:

public class NativeActivity extends Activity {
    private Button btnGoReact;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnGoReact = (Button)this.findViewById(R.id.btn_go_react);
        btnGoReact.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(NativeActivity.this,RnAndroidCommuActivity.class);
                intent.putExtra("result","native go to react");
                startActivity(intent);
            }
        });

    }

activity_main資原始檔的程式碼:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.wangfang.rnproject.MainActivity">

    <Button
        android:id="@+id/btn_go_react"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="原生介面跳轉React介面"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

還有清單檔案 這裡就不貼程式碼了,相信做過Android的專案都知道咋寫吧。。。
RN端的程式碼:

import React from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  DeviceEventEmitter,
  NativeModules,
  View,
  TextInput
} from 'react-native';
import React from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  DeviceEventEmitter,
  NativeModules,
  View,
  TextInput
} from 'react-native';
export class RnAndroidCommunication extends React.Component{
  componentWillMount(){
    DeviceEventEmitter.addListener("EventName",()=>{
      this.showState();
      alert("send event success");
    });
  }

  constructor(props){
    super(props);
    this.state={
      content:"這是個預定的接受訊息"
    }

  }

  render(){
    return (
      <View style = {myStyles.container}>

        <Text style = {myStyles.welcome} onPress={this.callNative.bind(this)}>
            當你點我的時候會呼叫原生方法,原生方法延遲3s後會向前端傳送事件。
            前端一直在監聽該事件,如果收到,則給出alert提示! send 方式
        </Text>
       
        <Text style = {myStyles.instructions}>
            {this.state.content}
        </Text>

      </View>

    );

  }
  
  callNative(){
      console.log(" js called by send");

      NativeModules.MyModule.callNativeBySend();
  }
  showState(){
    this.setState({content:"已經收到了原生模組傳送來的事件,send event 方式"});
  }

}
const myStyles = 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('testNative', () => RnAndroidCommunication);

在JS中通過 DeviceEventEmitter 註冊監聽了名為“”的事件,當原生模組發出名為“EventName”的事件後,繫結在該事件上的 EventName = () 會被回撥。 然後通過就可獲得事件所攜帶的資料,並執行回撥函式,進行頁面顯示燈。
心得:如果在JS中有多處註冊了 EventName 事件,那麼當原生模組發出事件後,這些地方會同時收到該事件。不過大家也可以通過 DeviceEventEmitter.removeListener('onScanningResult',this.onScanningResult) 來移除對名為“EventName”事件的監聽。

下一篇:React native 與Android原生互動方式(二)

相關文章