React Native 与 Android 混编项目复盘反思

最近偷懒了一段时间(折腾了下小程序、跨平台开发框架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混编项目中的页面跳转和方法调用

React Native 与 Android 混编项目复盘反思_第1张图片
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中实现按需加载以及热更新功能的工具库。

你可能感兴趣的:(React Native 与 Android 混编项目复盘反思)