React Native 与 Android(安卓)混编项目复盘反思
最近偷懒了一段时间(折腾了下小程序、跨平台开发框架Weex / React Native / Flutter),到现在接入RN混合开发也已经七八个月的时间了,今天针对RN在Android混编项目做个整体的复盘反思。以下内容是之前总结,本次不再重复记录。
- 简述RN集成到Android原生项目
- RN与Android原生的消息通信
- RN使用Android原生控件或自定义组件
- RN修改React Native端口号本地调试
- 将RN项目打包成离线包
- React Native Linking与 Android原生页面路由跳转实现
- Android项目集成RN混淆打包问题
本次记录不作为使用指南记录,因此不再描述项目初始化等相关方面,需要的可以参考上面链接,这里主要站在我们业务选择和使用RN的角度来整体上分析跨平台RN开发的优缺点,及对React Native与 Android 混编项目简单做下复盘反思。
一、跨平台RN开发的优缺点:
-
优点:
- 提高研发和测试效率
- 各终端逻辑保持统一
之前Android、iOS、M同一需求需三端开发人员各自去实现,以各自对需求的理解、各自的实现方式去码不同的代码,各自的逻辑和展示也可能就各不相同,很难保证功能一致性,自然测试人员需要针对三端各自测试,逻辑bug产生相对比较零散,各端各不相同,验证也相对比较耗时耗力,采用RN同一功能,同一套代码逻辑方式,测试起来发现逻辑bug相对统一,一些平台差异性兼容性bug除外,大大提高测试效率,同时各终端业务逻辑也相对统一,提高了研发效率。
缺点:
因平台差异性可能会存在一些兼容性问题,如果想在前期就能抹平各端差异性问题及解决各平台兼容型问题,就需要RN开发人员熟悉Android、IOS、Web三端开发技术,对技术广度稍微有点高。
二、React Native在Android混编项目中的页面跳转和方法调用
Android与RN通信.png大致通过上面这张简图来描述下:
-
页面跳转(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原生页面路由跳转实现。
-
方法调用
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种参数类型发现,其中有ReadableMap 和 ReadableArray类型,对应JavaScript的Object。而在Java原生中,可以发现facebook定义了ReadableArray和ReadableMap接口,一层一层找一下,找到了WritableArray和WritableMap接口,以及实现了他们的WritableNativeArray和WritableNativeMap,我尝试利用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中实现按需加载以及热更新功能的工具库。
更多相关文章
- 做嵌入式的必须学Android吗
- android与html5的交互——数据库操作,UI操作,以及html5的localStor
- 修正Android摄像头API
- Android(安卓)Activity之间的跳转与传值(回传)
- 【android内核分析-输入输出】Android4.0 input touch解析
- Android调用WebService系列之封装能力
- android jni
- 我和我的Android
- Android中“分享”功能的实现