ReactNative开发——RN与android Native交互初探

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a992036795/article/details/72781958

ReactNative开发——RN与android Native交互初探

环境

window10,reactnative 0.44版

RN调用android方法

1、导入NativeModules组件
import {NativeModules} from 'react-native';

2、在android中创建一个类继承自ReactContextBaseJavaModule,并定义一个方法供RN调用。
比如:



public class ExampleInterface extends ReactContextBaseJavaModule {

    public ExampleInterface(ReactApplicationContext reactContext) {
        super(reactContext);
    }

    @Override
    public String getName() {
        return "ExampleInterface";
    }

    /**
     * 这里是原生代码处理消息的函数。
     * <p>
     * 回调参数的对应关系,java -> js
     * Boolean -> Bool
     * Integer -> Number
     * Double -> Number
     * Float -> Number
     * String -> String
     * Callback -> function
     * ReadableMap -> Object
     * ReadableArray -> Array
     *
     * @param msg RN传过来的参数
     * @return void 函数不能又返回值
     */
    @ReactMethod
    public void handleMessage(String msg) {
        Log.i("RNMessage", "receive message from RN:" + msg);
    }

 }

我们使用注解@ReactMethod表名我们的方法可以被RN调用,其中参数的类型可以是一下几种:

Boolean -> Bool
Integer -> Number
Double -> Number
Float -> Number
String -> String
Callback -> function
ReadableMap -> Object
ReadableArray -> Array

3、创建一个React包管理类,将我们定义的NativeMoudle加入进去:


public class AnExampleReactPackage implements ReactPackage {
    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        List<NativeModule> nativeModules = new ArrayList<>();
        /*在这里加入开发的接口*/
        nativeModules.add(new ExampleInterface(reactContext));
        return nativeModules;
    }

    @Override
    public List<Class<? extends JavaScriptModule>> createJSModules() {
        return Collections.emptyList();
    }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }
}

4、在MainApplication代码中注入ReactNativeHost的地方添加定义的包管理类。

package com.project04;

import android.app.Application;

import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;

import java.util.Arrays;
import java.util.List;

public class MainApplication extends Application implements ReactApplication {

    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {

        @Override
        public boolean getUseDeveloperSupport() {
            return BuildConfig.DEBUG;
        }

        @Override
        protected List<ReactPackage> getPackages() {
            return Arrays.<ReactPackage>asList(
                    new MainReactPackage(),
                    /*将我们自定的包管理加入*/
                    new AnExampleReactPackage()
            );
        }
    };

    @Override
    public ReactNativeHost getReactNativeHost() {
        return mReactNativeHost;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        SoLoader.init(this, /* native exopackage */ false);
    }
}

5 、使用RN调用
NativeModules.ExampleInterface.handleMessage("I press button.");

我们在js端使用这句代码,就可以调用Android代码了。

Native使用Callback/Promise回调RN

传入Callback

我们在我们的定义的NativeMoudle(ExampleInterface.java)中定义一个方法供RN调用,其中有一个参数为Callback,它对应js的function,我们使用
callback.invoke(msg);即可以调用到js了。
示例:

@ReactMethod
public void handleCallback(String msg, Callback callback) {
    Log.i(TAG, "handleCallback: msg:" + msg);
    Log.i(TAG, "开始回调 js");
        callback.invoke(msg);
}

Js端

NativeModules.ExampleInterface.handleCallback('i will be print', (msg) => {
    console.log(msg);
});

在ReactNative开发中,即使原生代码正确的调用了回调函数,在ReactNative侧对应的回到函数也不会立即执行,因为混合开发中的桥接机制是异步的。

使用Promise

在Andorid平台混合开发中,同样支持使用原生代码实现Promise机制,当被桥接的原生代码函数的最后一个参数为Promise时,这个函数会返回一个JavaScript的Promise对象给他对应的JavaScript方法。

示例:
Native侧

@ReactMethod
public void handlePromise(String msg, Promise promise) {
    try {
        promise.resolve(msg);
    } catch (Exception e) {
        promise.reject(e);
    }
}

RN侧:

/**
 * 测试Promise
 */
pressPromise() {
    /*NativeModule.ExampleInterface.handlePromise 返回的是一个Promise对象*/
    NativeModules.ExampleInterface.handlePromise('Promise').then((msg) => {
        console.log(msg)
    })
        .catch((error) => {
            console.log(error)
        });
}

发送事件到JavaScript

原生代码在没有被调用的情况往JavaScript发送事件通知,最简单的办法就是通过RCTDeviceEventEmitter,这可以通过ReactContext来获取对应的引用。

Native侧:

/**
 * 向RN发送消息
 *
 * @param msg
 */
private void sendMsgToRN(String msg) {
    mContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
            .emit("AndroidToRNMessage", msg);
}

RN 测:

import {DeviceEventEmitter} from 'react-native';
componentWillMount() {
    DeviceEventEmitter.addListener('AndroidToRNMessage', this.handleAndroidMessage.bind(this));
}

跨语言常量

在Native端,通过重写我们自定义的NativeMoudle中的 getConstant方法,可以将Andorid原生代码中定义的常量提供给React Native侧。
Native侧:

@Nullable
@Override
public Map<String, Object> getConstants() {
    final Map<String, Object> constants = new HashMap<>();
    constants.put("AA", "我是一个常量,我来自Native");
    return constants;
}

RN侧:

NativeModules.ExampleInterface.AA

Native层Activity相关的回调

在我们自定义的NativeMoulde(ExampleInterface.java)中,在构造方法中得到ReactApplicationContext之后可以添加2个回调函数。
1.添加Activity声明周期回调reactContext.addLifecycleEventListener
LifecycleEventListener的定义如下:

 */
public interface LifecycleEventListener {

  /**
   * Called either when the host activity receives a resume event (e.g. {@link Activity#onResume} or
   * if the native module that implements this is initialized while the host activity is already
   * resumed. Always called for the most current activity.
   */
  void onHostResume();

  /**
   * Called when host activity receives pause event (e.g. {@link Activity#onPause}. Always called
   * for the most current activity.
   */
  void onHostPause();

  /**
   * Called when host activity receives destroy event (e.g. {@link Activity#onDestroy}. Only called
   * for the last React activity to be destroyed.
   */
  void onHostDestroy();
}

2.添加ActivityEvent回调 reactContext.addActivityEventListener

ActivityEventListener的定义如下:

public interface ActivityEventListener {

  /**
   * Called when host (activity/service) receives an {@link Activity#onActivityResult} call.
   */
  void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data);

  /**
   * Called when a new intent is passed to the activity
   */
  void onNewIntent(Intent intent);
}

实战

实现功能

1.在RN界面有一个按钮点击之,跳转到原生选取联系人界面
2.选择完联系人之后,会回到Native的onActivityResult方法
3.我们从返回的方法中得到联系人信息,将他发送到ReactNative测。

使用JavaScript调用Native方法,并在Native方法中启动选择联系人界面

RN侧

/**
 * 调用Native页面选择联系人
 */
pressSelectContract() {
    console.log('pressSelectContract');
    // 调用Native页面
    NativeModules.ExampleInterface.handleMessage("I press button.");
}
render() {
    return (
        <View style={styles.container}>
            <Text style={styles.welcome}>
                这是一个常量:{NativeModules.ExampleInterface.AA}
            </Text>
            <Text style={styles.welcome}>
                {this.state.msg}
            </Text>
            <Button
                title="点击选择联系人"
                onPress={this.pressSelectContract}
                accessibilityLabel='这个是这个button的Label'
            />
            < Button
                title="测试Callback"
                onPress={this.pressCallback}/>
            < Button
                title="测试Promise"
                onPress={this.pressPromise}/>
        </View >
    );
}

Native侧

@ReactMethod
public void handleMessage(String msg) {
    Log.i("RNMessage", "receive message from RN:" + msg);
    /*调用联系人页面*/
    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_PICK);
    intent.setType(ContactsContract.Contacts.CONTENT_TYPE);
    this.mContext.startActivityForResult(intent, REQUEST_CONTACTS_CODE, new Bundle());
}

在回调中获取联系人信息,并发送给RN

private void setupActivityResultListener(ReactApplicationContext reactContext) {
    reactContext.addActivityEventListener(new BaseActivityEventListener() {
        @Override
        public void onActivityResult(Activity activity, int requestCode,
                                     int resultCode, Intent data) {
            if (requestCode != REQUEST_CONTACTS_CODE || resultCode != Activity.RESULT_OK) {
                return;
            }
            /*如果选取ok,开始读取联系人信息*/
            String msg = pareContactMsg(data.getData());
            /*发送给RN*/
            sendMsgToRN(msg);
        }
    });
}
/**
 * 向RN发送消息
 *
 * @param msg
 */
private void sendMsgToRN(String msg) {
    mContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
            .emit("AndroidToRNMessage", msg);
}

/**
 * 从返回的uri中查询出联系人信息。
 *
 * @param uri
 * @return
 */
private String pareContactMsg(Uri uri) {
    Cursor cursor = null;
    Cursor phoneNumberCursor = null;
    String msg = "";
    try {
        cursor = mContext.getContentResolver().query(uri, null, null, null, null);
        if (null != cursor && cursor.moveToFirst()) {
            long id = cursor.getLong(cursor.getColumnIndex(ContactsContract.Contacts._ID));
            String name = cursor.
                    getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
            int hasPhoneNumber = cursor
                    .getInt(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER));
            String phoneNumber = "";
            if (hasPhoneNumber == 1) {
                phoneNumberCursor = mContext.getContentResolver().query(
                        ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract
                                .CommonDataKinds.Phone.CONTACT_ID + " = ?",
                        new String[]{String.valueOf(id)}, null);
                if (phoneNumberCursor != null && phoneNumberCursor.moveToFirst()) {
                    phoneNumber = phoneNumberCursor.getString(
                            phoneNumberCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
                    );
                }
            }
            msg = "{姓名: " + name + ", 电话号码:" + phoneNumber + "}";
        }
    } finally {
        if (null != phoneNumberCursor) {
            phoneNumberCursor.close();
        }
        if (null != cursor) {
            cursor.close();
        }
        return msg;
    }
}

ReactNative侧接受到联系人信息之后,使用setState重新渲染界面

handleAndroidMessage(androidMeg) {
    this.setState({msg: androidMeg});
}
componentWillMount() {
    DeviceEventEmitter.addListener('AndroidToRNMessage', this.handleAndroidMessage.bind(this));
}

完整代码

ReactNative:
index.adroid.js

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, {Component} from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    Button,
    NativeModules,
    DeviceEventEmitter,
} from 'react-native';


/**
 * 获取一个联系人的信息
 */
export default class Project04 extends Component {
    state = {
        /*初始化msg*/
        msg: '暂时无信息',
    }

    /**
     * 调用Native页面选择联系人
     */
    pressSelectContract() {
        console.log('pressSelectContract');
        // 调用Native页面
        NativeModules.ExampleInterface.handleMessage("I press button.");
    }

    /**
     * 测试Callback
     */
    pressCallback() {
        NativeModules.ExampleInterface.handleCallback('i will be print', (msg) => {
            console.log(msg);
        });
    }

    /**
     * 测试Promise
     */
    pressPromise() {
        /*NativeModule.ExampleInterface.handlePromise 返回的是一个Promise对象*/
        NativeModules.ExampleInterface.handlePromise('Promise').then((msg) => {
            console.log(msg)
        })
            .catch((error) => {
                console.log(error)
            });
    }

    handleAndroidMessage(androidMeg) {
        this.setState({msg: androidMeg});
    }

    componentWillMount() {
        DeviceEventEmitter.addListener('AndroidToRNMessage', this.handleAndroidMessage.bind(this));
    }

    componentWillUnmount() {
        DeviceEventEmitter.removeAllListeners();
    }

    render() {
        return (
            <View style={styles.container}>
                <Text style={styles.welcome}>
                    这是一个常量:{NativeModules.ExampleInterface.AA}
                </Text>
                <Text style={styles.welcome}>
                    {this.state.msg}
                </Text>
                <Button
                    title="点击选择联系人"
                    onPress={this.pressSelectContract}
                    accessibilityLabel='这个是这个button的Label'
                />
                < Button
                    title="测试Callback"
                    onPress={this.pressCallback}/>
                < Button
                    title="测试Promise"
                    onPress={this.pressPromise}/>
            </View >
        );
    }
}

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('Project04', () => Project04);

Native测:

MainActivity:


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 "Project04";
    }


}

MainApplication.java

package com.project04;

import android.app.Application;

import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;

import java.util.Arrays;
import java.util.List;

public class MainApplication extends Application implements ReactApplication {

    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {

        @Override
        public boolean getUseDeveloperSupport() {
            return BuildConfig.DEBUG;
        }

        @Override
        protected List<ReactPackage> getPackages() {
            return Arrays.<ReactPackage>asList(
                    new MainReactPackage(),
                    /*将我们自定的包管理加入*/
                    new AnExampleReactPackage()
            );
        }
    };

    @Override
    public ReactNativeHost getReactNativeHost() {
        return mReactNativeHost;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        SoLoader.init(this, /* native exopackage */ false);
    }
}

ExampleInterface.java

package com.project04;

import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.util.Log;

import com.facebook.react.bridge.BaseActivityEventListener;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.modules.core.DeviceEventManagerModule;

import java.util.HashMap;
import java.util.Map;

import javax.annotation.Nullable;

/**
 * Created by blueberry on 5/27/2017.
 */

public class ExampleInterface extends ReactContextBaseJavaModule {

    private static final String TAG = "ExampleInterface";

    public static final int REQUEST_CONTACTS_CODE = 100;

    private ReactApplicationContext mContext;

    public ExampleInterface(ReactApplicationContext reactContext) {
        super(reactContext);
        setupLifecycleEventListener(reactContext);
        setupActivityResultListener(reactContext);
        this.mContext = reactContext;
    }

    private void setupActivityResultListener(ReactApplicationContext reactContext) {
        reactContext.addActivityEventListener(new BaseActivityEventListener() {
            @Override
            public void onActivityResult(Activity activity, int requestCode,
                                         int resultCode, Intent data) {
                if (requestCode != REQUEST_CONTACTS_CODE || resultCode != Activity.RESULT_OK) {
                    return;
                }
                /*如果选取ok,开始读取联系人信息*/
                String msg = pareContactMsg(data.getData());
                /*发送给RN*/
                sendMsgToRN(msg);
            }
        });
    }

    private void setupLifecycleEventListener(ReactApplicationContext reactContext) {
        reactContext.addLifecycleEventListener(new LifecycleEventListener() {
            @Override
            public void onHostResume() {
                Log.i(TAG, "onHostResume: ");
            }

            @Override
            public void onHostPause() {
                Log.i(TAG, "onHostPause: ");
            }

            @Override
            public void onHostDestroy() {
                Log.i(TAG, "onHostDestroy: ");
            }
        });
    }


    /**
     * 向RN发送消息
     *
     * @param msg
     */
    private void sendMsgToRN(String msg) {
        mContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                .emit("AndroidToRNMessage", msg);
    }

    /**
     * 从返回的uri中查询出联系人信息。
     *
     * @param uri
     * @return
     */
    private String pareContactMsg(Uri uri) {
        Cursor cursor = null;
        Cursor phoneNumberCursor = null;
        String msg = "";
        try {
            cursor = mContext.getContentResolver().query(uri, null, null, null, null);
            if (null != cursor && cursor.moveToFirst()) {
                long id = cursor.getLong(cursor.getColumnIndex(ContactsContract.Contacts._ID));
                String name = cursor.
                        getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
                int hasPhoneNumber = cursor
                        .getInt(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER));
                String phoneNumber = "";
                if (hasPhoneNumber == 1) {
                    phoneNumberCursor = mContext.getContentResolver().query(
                            ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract
                                    .CommonDataKinds.Phone.CONTACT_ID + " = ?",
                            new String[]{String.valueOf(id)}, null);
                    if (phoneNumberCursor != null && phoneNumberCursor.moveToFirst()) {
                        phoneNumber = phoneNumberCursor.getString(
                                phoneNumberCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
                        );
                    }
                }
                msg = "{姓名: " + name + ", 电话号码:" + phoneNumber + "}";
            }
        } finally {
            if (null != phoneNumberCursor) {
                phoneNumberCursor.close();
            }
            if (null != cursor) {
                cursor.close();
            }
            return msg;
        }
    }

    @Override
    public String getName() {
        return "ExampleInterface1";
    }

    /**
     * 这里是原生代码处理消息的函数。
     * <p>
     * 回调参数的对应关系,java -> js
     * Boolean -> Bool
     * Integer -> Number
     * Double -> Number
     * Float -> Number
     * String -> String
     * Callback -> function
     * ReadableMap -> Object
     * ReadableArray -> Array
     *
     * @param msg RN传过来的参数
     * @return void 函数不能又返回值
     */
    @ReactMethod
    public void handleMessage(String msg) {
        Log.i("RNMessage", "receive message from RN:" + msg);
        /*调用联系人页面*/
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_PICK);
        intent.setType(ContactsContract.Contacts.CONTENT_TYPE);
        this.mContext.startActivityForResult(intent, REQUEST_CONTACTS_CODE, new Bundle());
    }

    @ReactMethod
    public void handleCallback(String msg, Callback callback) {
        Log.i(TAG, "handleCallback: msg:" + msg);
        Log.i(TAG, "开始回调 js");
            callback.invoke(msg);
    }

    @ReactMethod
    public void handlePromise(String msg, Promise promise) {
        try {
            promise.resolve(msg);
        } catch (Exception e) {
            promise.reject(e);
        }
    }

    /**
     * 返回常量
     *
     * @return 常量字典
     */
    @Nullable
    @Override
    public Map<String, Object> getConstants() {
        final Map<String, Object> constants = new HashMap<>();
        constants.put("AA", "我是一个常量,我来自Native");
        return constants;
    }
}

AnExampleReactPackage.java

package com.project04;

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 blueberry on 5/27/2017.
 * React包管理类
 */

public class AnExampleReactPackage implements ReactPackage {
    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        List<NativeModule> nativeModules = new ArrayList<>();
        /*在这里加入开发的接口*/
        nativeModules.add(new ExampleInterface(reactContext));
        return nativeModules;
    }

    @Override
    public List<Class<? extends JavaScriptModule>> createJSModules() {
        return Collections.emptyList();
    }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }
}

遇到的坑

  1. 我同时使用android Studio 和WebStrom开发的时候,如果用了AndroidStuiod运行运行了项目,以后在WebStrom运行的时候会报错说:无法删除 app/build目录下的文件,解决办法,手动删除那个文件夹就好了。原因可能是 AndoridStuio创建的build目录,使用WebStrom无权限删除那个目录导致的。

参考

ReacNative 中文文档 http://reactnative.cn/docs/0.44/native-modules-android.html#content
ReactNative 英文文档 https://facebook.github.io/react-native/docs/native-modules-android.html

猜你喜欢

转载自blog.csdn.net/a992036795/article/details/72781958
今日推荐