最近偷懒了一段时间(折腾了下小程序、跨平台开发框架Weex / React Native / Flutter),到现在接入RN混合开发也已经七八个月的时间了,今天针对RN在Android混编项目做个整体的复盘反思。以下内容是之前总结,本次不再重复记录。

  1. 简述RN集成到Android原生项目
  2. RN与Android原生的消息通信
  3. RN使用Android原生控件或自定义组件
  4. RN修改React Native端口号本地调试
  5. 将RN项目打包成离线包
  6. React Native Linking与 Android原生页面路由跳转实现
  7. Android项目集成RN混淆打包问题

本次记录不作为使用指南记录,因此不再描述项目初始化等相关方面,需要的可以参考上面链接,这里主要站在我们业务选择和使用RN的角度来整体上分析跨平台RN开发的优缺点,及对React Native与 Android 混编项目简单做下复盘反思。

一、跨平台RN开发的优缺点:

  1. 优点:

    • 提高研发和测试效率
    • 各终端逻辑保持统一

    之前Android、iOS、M同一需求需三端开发人员各自去实现,以各自对需求的理解、各自的实现方式去码不同的代码,各自的逻辑和展示也可能就各不相同,很难保证功能一致性,自然测试人员需要针对三端各自测试,逻辑bug产生相对比较零散,各端各不相同,验证也相对比较耗时耗力,采用RN同一功能,同一套代码逻辑方式,测试起来发现逻辑bug相对统一,一些平台差异性兼容性bug除外,大大提高测试效率,同时各终端业务逻辑也相对统一,提高了研发效率。

  2. 缺点:
    因平台差异性可能会存在一些兼容性问题,如果想在前期就能抹平各端差异性问题及解决各平台兼容型问题,就需要RN开发人员熟悉Android、IOS、Web三端开发技术,对技术广度稍微有点高。

二、React Native在Android混编项目中的页面跳转和方法调用

Android与RN通信.png

大致通过上面这张简图来描述下:

  1. 页面跳转(RN与Android原生)
    调查背景:在设计与RN交互时,并不是仅站在Android一端的角度去设计,而是考虑通用型,尽量用RN本身特性去抹平差异性,避免RN在代码层面进行差异化处理,比如说页面跳转处理:

    • Android的页面跳转是通过Intent跳转
    • RN是通过路由(M版也通过路由跳转)

    如果保持各自特性,则两者直接页面互相跳转就需要原生借助JS暴露接口给RN来实现了,这样的话RN就需要根据终端环境进行差异化处理,为了避免RN在代码层面进行差异化处理,尽量寻找统一性方案确保整个项目的统一,通过调查发现RN提供的Linking方式进行跳转,那么就有两个问题,RN怎么拿?原生怎么传?然后通过源码发现RN分别针对Android和IOS进行了封装映射,我们只需要把数据传送到对应的位置即可,

    const LinkingManager =    Platform.OS === 'android'    ? NativeModules.IntentAndroid : NativeModules.LinkingManager;

    在Android中对应的是IntentAndroid,查看对应的源码:

    /** * Return the URL the activity was started with ** @param promise a promise which is resolved with the initial URL*/ @ReactMethod public void getInitialURL(Promise promise) {   try {     Activity currentActivity = getCurrentActivity();     String initialURL = null;     if (currentActivity != null) {       Intent intent = currentActivity.getIntent();       String action = intent.getAction();       Uri uri = intent.getData();       if (Intent.ACTION_VIEW.equals(action) && uri != null) {         initialURL = uri.toString();       }     }     promise.resolve(initialURL);   } catch (Exception e) {     promise.reject(new JSApplicationIllegalArgumentException(         "Could not get the initial URL : " + e.getMessage()));   } }

    通过上面源码也就了解了Android与RN如何根据Linking进行页面跳转,我先按照这种形式在Android上进行测试发现可行,当然也有bug,就是拿的时候可能为空,调查发现RN与原生页面装载之前就调用这个方法导致拿不到上下文,只要创建rootview就会执行rn的生命周期,而此时rN与原生还没有绑定,后来发现RN是能监听到原生页面的活动状态,此时再去获取路由即可。详细内容可参考React Native Linking与 Android原生页面路由跳转实现

  2. 方法调用
    RN通信原理简单地讲就是,一方native(java)将其部分方法注册成一个映射表,另一方(js)再在这个映射表中查找并调用相应的方法,而Bridge担当两者间桥接的角色。
    其实方法调用大致分为2种情况:

    • Android主动向JS端传递事件、数据
    • JS端主动向Android询问获取事件、数据

    RN调用Android需要module名和方法名相同,而Android调用RN只需要方法名相同。
    (1)RCTDeviceEventEmitter 事件方式
    ​ 优点:可任意时刻传递,Native主导控制。
    (2)Callback 回调方式
    ​ 优点:JS调用,Native返回。
    ​ 缺点:CallBack为异步操作,返回时机不确定
    (3)Promise
    ​ 优点:JS调用,Native返回。
    ​ 缺点:每次使用需要JS调用一次
    (4)直传常量数据(原生向RN)
    ​ 跨域传值,只能从原生端向RN端传递。RN端可通过 NativeModules.[module名].[参数名] 的方式获取。

    注意:RN层调用NativeModule层进行界面跳转时,需要设置FLAG_ACTIVITY_NEW_TASK标志。

三、Andorid 与 RN 传参数据类型映射关系:

Android React Native 备注
Boolean Boolean
Integer Number
Double Number
Float Number
String String
Callback function
ReadableMap / WritableMap Object
ReadableArray / WritableArray Object

观察着8种参数类型发现,其中有ReadableMapReadableArray类型,对应JavaScriptObject。而在Java原生中,可以发现facebook定义了ReadableArrayReadableMap接口,一层一层找一下,找到了WritableArrayWritableMap接口,以及实现了他们的WritableNativeArrayWritableNativeMap,我尝试利用WritableNativeArray push了几个参数,成功的传递到了参数:

//Android    @ReactMethod    public void show(Callback callback) {        WritableArray writableArray = new WritableNativeArray();        writableArray.pushString("one");        writableArray.pushString("two");        writableArray.pushString("three");        callback.invoke(writableArray);    }    //React Native    MyTest.show((result)=> {                ToastAndroid.show("结果是:" + result[2], ToastAndroid.SHORT);            }        );
     //Android    @ReactMethod    public void show(Callback callback) {        WritableMap writableMap = new WritableNativeMap();        writableMap.putString("1", "first");        writableMap.putString("2", "second");        writableMap.putString("3", "third");        callback.invoke(writableMap);    }        //React Native    MyTest.show((result)=> {                ToastAndroid.show("结果是:" + result["2"], ToastAndroid.SHORT);            }        );

备注: 如何更好地将RN与原生进行参数传递呢?虽说上面的映射关系可以让我们去准确的传递参数,但是比如说在Android原生中ReadableMap / WritableMap对应着RN中的 Object, 但是我们原生里面并不会全局使用ReadableMap / WritableMap来替换现有的Map或者HashMap吧,为尽量避免RN的侵入型,我们需要Native Module层进行抽象封装处理,将RN中的数据类型与Android原生的数据类型进行互转,稍后会整理相关转化工具类,方便Android与原生快速交互。

经过一段时间的梳理,不管是前面讲过的知识点还是没讲过的(预加载/热更新)后面都会在一个工具库中体现,旨在创建一个能够快速轻便集成RN与Android混编项目,优化两者相互通信方式(各自使用各自熟悉的数据格式,抹平差异性),以及RN bundle在Android中实现按需加载以及热更新功能的工具库。

更多相关文章

  1. 做嵌入式的必须学Android吗
  2. android与html5的交互——数据库操作,UI操作,以及html5的localStor
  3. 修正Android摄像头API
  4. Android(安卓)Activity之间的跳转与传值(回传)
  5. 【android内核分析-输入输出】Android4.0 input touch解析
  6. Android调用WebService系列之封装能力
  7. android jni
  8. 我和我的Android
  9. Android中“分享”功能的实现

随机推荐

  1. Android Toast无效的问题
  2. 编译Android 4.0时遇到的问题汇总
  3. 学习笔记 Android 使用AIDL实现进程间通
  4. Android Banner 的简单使用步骤
  5. Android教程之SQlit数据库操作
  6. Android从网上下载文件
  7. android图片缩放(指定大小)
  8. android实现Parcelable序列化对象
  9. Android(安卓)修改host文件的3种方法
  10. 关于如何使用Mumu模拟器连接电脑adb