06 ReactNative和Android原生的交互

作者: 与之书 | 来源:发表于2018-03-30 17:11 被阅读27次

    从RN向Android端发送消息

    • 创建一个类继承ReactContextBaseJavaModule
    public class RNTest extends ReactContextBaseJavaModule {
    
        private Context reactContext;
    
        public RNTest(ReactApplicationContext reactContext) {
            super(reactContext);
            this.reactContext= reactContext;
        }
    
        @Override
        public String getName() {
            return "RNTest";    // 不一定是类名,要和NativeModules.RNTest对应
        }
    
        // 自定义一个方法 处理消息
        // 通过注解  表示可以被RN侧调用
        @ReactMethod
        public void handleMsg(String msg){
            Toast.makeText(reactContext,msg,Toast.LENGTH_SHORT).show();
            // 自己创建一个普通的Activity
            Intent intent = new Intent(reactContext,MyActivity.class);
            // 一定要加  否则报错
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            reactContext.startActivity(intent);
        }
    
    }
    
    
    • 创建一个包管理器继承ReactPackage
    public class TestReactPackage implements ReactPackage {
        @Override
        public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
            List<NativeModule> modules = new ArrayList<>();
            modules.add(new RNTest(reactContext)); // 添加上面定义的类
            return modules;
        }
    
        @Override
        public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
            return Collections.emptyList();
        }
    }
    
    
    • 在默认生成的MainApplication的getPackage方法中添加代码
    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 TestReactPackage()
          );
        }
    
    ... 下略
    
    • 在App.js中添加一个方法,关联到某个button上调用即可
      sendMsgToAndroid() {
        var { NativeModules } = require('react-native');
        let test = NativeModules.RNTest;
        test.handleMsg('this is a rn msg');
      }
    
    • 正常应该就可以运行了,直接从AS运行可能会导致服务关闭,建议从rn端运行,或者先AS再rn。

    Android发送消息到RN端

    • 从手机读取联系人,先添加权限
        <!--读取联系人相关权限-->
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
        <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
        <uses-permission android:name="android.permission.READ_CONTACTS"/>
    
    • RNTest修改如下
    
    
    import static android.app.Activity.RESULT_OK;
    
    /**
     * Created by shakespace on 2018/3/15.
     */
    
    public class RNTest extends ReactContextBaseJavaModule {
        private static final String TAG = "RNTest";
        private ReactContext reactContext;
    
        // 创建一个mActivityEventListener
        private ActivityEventListener mActivityEventListener = new BaseActivityEventListener() {
            @SuppressWarnings("deprecation")
            @Override
            public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
                super.onActivityResult(activity, requestCode, resultCode, data);
                if (requestCode == 1 && resultCode == RESULT_OK && data != null) {
                    Uri uri = data.getData();   // 获取地址
    //                CursorLoader loader = new CursorLoader(activity, uri, null, null, null, null);
    //                Cursor cursor = loader.loadInBackground();
                    Log.e(TAG, uri.toString());
                    Cursor cursor = activity.managedQuery(uri, null, null, null, null);
    
                    cursor.moveToFirst();    //
                    String msg = getInfo(cursor);
                    sendMsgToRN(msg);
                }
            }
        };
    
    
        public RNTest(ReactApplicationContext reactContext) {
            super(reactContext);
            this.reactContext = reactContext;
            reactContext.addActivityEventListener(mActivityEventListener);
        }
    
        // 发送消息到RN端
        private void sendMsgToRN(String msg) {
            reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("AndroidToRNMessage", msg);
        }
    
        private String getInfo(Cursor cursor) {
            String name = "";
            String phone = "";
    
            //取得联系人姓名
            int nameFieldColumnIndex = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
            name = cursor.getString(nameFieldColumnIndex);
    
            // 联系人和电话数据库不在一起  获取字段  拼接查询条件
            int id = cursor.getColumnIndex(ContactsContract.Contacts._ID);
            String contactID = cursor.getString(id);
            String queryString = ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=" + contactID;
            Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
    
            Cursor phoneCursor = reactContext.getContentResolver().query(uri, null, queryString, null, null);
            String phoneNumber = ContactsContract.CommonDataKinds.Phone.NUMBER;
            if (phoneCursor != null && phoneCursor.moveToFirst()) {
                phone = phoneCursor.getString(phoneCursor.getColumnIndex(phoneNumber));
            }
            phoneCursor.close();
    //        cursor.close();//  使用managedQuery 不用手动关
            String result = "{\"msgType\": \"pickContactResult\",\"name\":\"" + name + "\", \"number\":\"" + phone + "\"}";
            Log.e(TAG, result);
            return result;
        }
    
        @Override
        public String getName() {
            return "RNTest";    // 要和NativeModules.RNTest对应
        }
    
        // 自定义一个方法 处理消息
        // 通过注解  表示可以被RN侧调用
        @ReactMethod
        public void handleMsg(String msg) {
            Toast.makeText(reactContext, msg, Toast.LENGTH_SHORT).show();
            Intent intent = new Intent(reactContext, MyActivity.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            reactContext.startActivity(intent);
        }
    
        @ReactMethod
        public void pickContact() {
            Intent intent = new Intent(Intent.ACTION_PICK);
            intent.setType(ContactsContract.Contacts.CONTENT_TYPE);
            reactContext.startActivityForResult(intent, 1, null);
        }
    
    }
    
    

    主要步骤

    添加监听 reactContext.addActivityEventListener
    RN调用的方法中使用startActivityForResult调起原生页面
    在onActivityResult处理回传的数据
    使用reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("AndroidToRNMessage", msg);向RN端发送消息

    • RN端处理

    import RCTDeviceEventEmitter组件,添加方法

      getContactFromAndroid(){
        // 监听安卓消息
        DeviceEventEmitter.addListener('AndroidToRNMessage',this.handleAndroidMsg.bind(this));
        var { NativeModules } = require('react-native');
        let test = NativeModules.RNTest;
        test.pickContact();
      }
    
      handleAndroidMsg(msg){
        console.log(msg);
        let aObj = JSON.parse(msg);
        this.updateName(aObj.name);
        this.updateNum(aObj.number);
        // this.setState({name:aObj.name,num:aObj.number})
        DeviceEventEmitter.removeAllListeners();
      }
    

    完整案例查看:Demo

    案例实现了从安卓端读取联系人并返回
    在魅族手机上可能无法申请联系人权限,没有做更多适配,需要安装后自己手动在设置里打开权限。否则报错。
    书中建议,如果是原生需要主动通信RN端,使用消息回调方式,如果是RN想原生请求操作并希望原生返回一个结果时,建议使用Promise方式。
    reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("AndroidToRNMessage", msg);属于消息机制
    下例属于回调函数方式,因为桥接是异步的,所以不会立即执行

    @ReactMethod
        public void handleMsg(String msg, Callback callback) {
            ...
          callback.invoke(msg);     // 回调函数方式
            ...
        }
    
    • Promise方式
    1. 创建一个promise成员变量,在ReactMethod中初始化
        Promise mPromise;  
        @ReactMethod
        public void pickContact(String msg,Promise promise) {
            this.mPromise = promise;    // 使用Promise方式时添加,在这里初始化
            ...
        }
    
    1. 返回结果时使用resolve函数
     mPromise.resolve(msg);
     发生异常时使用reject相关函数
    
    1. RN不用注册函数,调用方式如下
    getContactFromAndroid(){
        // 监听安卓消息
        // DeviceEventEmitter.addListener('AndroidToRNMessage',this.handleAndroidMsg.bind(this));
        var { NativeModules } = require('react-native');
        let test = NativeModules.RNTest;
        // test.pickContact('testMsg');
    
        // 使用promise方式时,上面不用再注册回调
        test.pickContact('testMsg').then(
          // 表示处理结果的回调
          (result)=>{
            this.handleAndroidMsg(result);
          }
        ).catch(
          // 异常的处理
          (error)=>{
            console.log(error+"msg = "+ error.message + "code = "+ error.code);
          }
        );
      }
    
    1. 监听生命周期
      通过implements LifecycleEventListener可以监听原生的生命周期

    2. 跨语言常量

        @Nullable
        @Override
        public Map<String, Object> getConstants() {
            Map<String,Object> cons = new HashMap<>();
            cons.put("consName","tom");
            return cons;
        }
        
        在RN侧使用 模块名.consName 来访问被导出的常量
    
    1. 类型对应
    Boolean -> Bool
    Integer -> Number
    Double -> Number
    Float -> Number
    String -> String
    Callback -> function
    ReadableMap -> Object
    ReadableArray -> Array
    

    相关文章

      网友评论

        本文标题:06 ReactNative和Android原生的交互

        本文链接:https://www.haomeiwen.com/subject/xwbicftx.html