ReactNative | 项目复盘,涉及环境、RN 版本升级、安全等方案
背景
此篇文章,复盘下,短期兼职 RN 开发遇到的一些问题,附:JS-OC 通信详解。
一、环境问题
如果你是首次使用 RN,最初搭环境下依赖包构建 APK,建立你科学上网,因为容易少包。
编译运行
报错:device is unauthoried
清除编译报错缓存
打包
打包发布发行版 apk,出现 unable to process incoming event 'ProcessComplete'
测试运行打包闪退,执行此命令,可以查看错误日志
报错:React-Native version mismatch
报错:Activity class {} does not exist.Error while Launching activity
二、开发调试
调试工具
react-native-debugger
手机 APP 开启调试
手机摇一摇,会弹窗调试选项:reload/live reload...等选项
app 调用本机服务
app 的 10.0.2.2 默认映射到本机地址 127.0.0.1:8080
查看调试工具 Network 面板
三、RN 大版本升级方案
项目 2 年没人碰,版本为 0.52 版本,当时认为老旧,因此提出升级到 0.62 版本。
升级版本有 2 种方案,一种是对比新旧版本变更改动,在老版本代码一处一处改动,另一种是新建新版本项目,迁移业务代码过去。
1、改造老版本工程
版本升级,RN 社区也提供了方案,在老工程上改造,地址为:https://react-native-community.github.io/upgrade-helper,可以选择从指定版本到指定版本,系统会生成新旧代码的对照,自己比对,改造老项目。
考虑到比对工作量,也怕比对有出入,作为新手,难以把控,最终放弃这个方案。
2、新建工程承载业务代码
这是我采取的方案,罗列下升级流程,当然在运行中构建会报很多错,以及组件提示弃用或者不再支持,依次去修改即可,全程可控。
新建 0.62 RN 初始项目。
迁移业务代码 与 构建配置(android 目录底下)。
配置修改:build.gradle/MainApplication/...
迁移 keystore/gradle.properties
修改三方库版本问题(升级|改造)。
大版本升级后,依赖的三方库可能未升级,因此需要将三方库抽离出来改造,当做项目库,切莫继续放在 node_modules 底下,防止被覆盖。
RN 基础组件升级修复(如,webview 组件的剥离)。
四、WebView 与 H5 通信
1、WebView 配置说明
属性
函数
2、WebView 向 H5 注入 Js
通过 injectedJavaScript 注入 JS ,在 H5 页面加载之后立即执行。相当于 webview 端主动调用 H5 的方法。注入的内容可以是方法实现,也可以是方法名字。
3、WebView 和 H5 相互发送监听消息
RN 向 H5 发送消息
H5 向 RN 发送消息
注意:webview 组件在被官方放弃之后,react-native-webview 作为替代,react-native-webview 5.0 版本需要往 H5 注入 js:
五、RCTDeviceEventEmitter
RCTDeviceEventEmitter 是 RN 的原生模块,初始化项目的时候已经下载到 node_modules,地址为:node_modules/react-native/Libraries/EventEmitter 目录下,所以在项目中可以通过 require("RCTDeviceEventEmitter")直接引用,因此在 package.json 也就找不到依赖。
RCTDeviceEventEmitter 主要用于 native 向 js 发送消息,当然也可以通过 js 向 js 发送消息,使用例子如下:
当然,应用上也可以使用 redux 来共享数据状态。
六、安全问题
1、so 库未使用编译器堆栈保护技术
修复方案:
位置:node_modules\react-native\ReactAndroid\src\main\jni\Application.mk
新增指令:LOCAL_CFLAGS := -Wall -O2 -U_FORTIFY_SOURCE -fstack-protector-all
2、Janus 漏洞
3、应用数据任意备份风险
修复方案:android:allowBackup 属性值设为 false。
4、使用 360 加固助手加固应用
七、总结
初探 RN,让人感慨跨端的强大,之前有过短暂原生 Android 开发经历,对于前端人员跨门槛高,混合开发大大降低成本。文章主要介绍通信相关,下面一起看看 JS-OC 通信原理。
附:JS-OC 通信详解
1、概述
React Native 用 iOS 自带的 JavaScriptCore 作为 JS 的解析引擎,但并没有用到 JavaScriptCore 提供的一些可以让 JS 与 OC 互调的特性,而是自己实现了一套机制,这套机制可以通用于所有 JS 引擎上,在没有 JavaScriptCore 的情况下也可以用 webview 代替,实际上项目里就已经有了用 webview 作为解析引擎的实现,应该是用于兼容 iOS7 以下没有 JavascriptCore 的版本。
普通的 JS-OC 通信实际上很简单,OC 向 JS 传信息有现成的接口,像 webview 提供的-stringByEvaluatingJavaScriptFromString 方法可以直接在当前 context 上执行一段 JS 脚本,并且可以获取执行后的返回值,这个返回值就相当于 JS 向 OC 传递信息。React Native 也是以此为基础,通过各种手段,实现了在 OC 定义一个模块方法,JS 可以直接调用这个模块方法并还可以无缝衔接回调。
举个例子,OC 定义了一个模块 RCTSQLManager,里面有个方法-query:successCallback:,JS 可以直接调用 RCTSQLManager.query 并通过回调获取执行结果。
接下来看看它是怎样实现。
2、模块配置
首先 OC 要告诉 JS 它有什么模块,模块里有什么方法,JS 才知道有这些方法后才有可能去调用这些方法。这里的实现是 OC 生成一份模块配置表传给 JS,配置表里包括了所有模块和模块里方法的信息。例:
OC 端和 JS 端分别各有一个 bridge,两个 bridge 都保存了同样一份模块配置表,JS 调用 OC 模块方法时,通过 bridge 里的配置表把模块方法转为模块 ID 和方法 ID 传给 OC,OC 通过 bridge 的模块配置表找到对应的方法执行之。
3、JS 调用 OC 详细流程
JS 端调用某个 OC 模块暴露出来的方法。
上一步的调用分解为 ModuleName,MethodName,arguments,再扔给 MessageQueue 处理。
在初始化时模块配置表上的每一个模块都生成了对应的 remoteModule 对象,对象里也生成了跟模块配置表里一一对应的方法,这些方法里可以拿到自身的模块名,方法名,并对 callback 进行一些处理,再移交给 MessageQueue。
在这一步把 JS 的 callback 函数缓存在 MessageQueue 的一个成员变量里,用 CallbackID 代表 callback。在通过保存在 MessageQueue 的模块配置表把上一步传进来的 ModuleName 和 MethodName 转为 ModuleID 和 MethodID。
把上述步骤得到的 ModuleID,MethodId,CallbackID 和其他参数 argus 传给 OC。
JS 不会主动传递数据给 OC,在调 OC 方法时,会在上述第 4 步把 ModuleID,MethodID 等数据加到一个队列里,等 OC 过来调 JS 的任意方法时,再把这个队列返回给 OC,此时 OC 再执行这个队列里要调用的方法,便通过返回值把数据传给 OC。
OC 接收到消息,通过模块配置表拿到对应的模块和方法。
实际上模块配置表已经经过处理了,跟 JS 一样,在初始化时 OC 也对模块配置表上的每一个模块生成了对应的实例并缓存起来,模块上的每一个方法也都生成了对应的 RCTModuleMethod 对象,这里通过 ModuleID 和 MethodID 取到对应的 Module 实例和 RCTModuleMethod 实例进行调用。
RCTModuleMethod 对 JS 传过来的每一个参数进行处理。
RCTModuleMethod 可以拿到 OC 要调用的目标方法的每个参数类型,处理 JS 类型到目标类型的转换,所有 JS 传过来的数字都是 NSNumber,这里会转成对应的 int/long/double 等类型,更重要的是会为 block 类型参数的生成一个 block。
这些参数组装完毕后,通过 NSInvocation 动态调用相应的 OC 模块方法。
OC 模块方法调用完,执行 block 回调。
调用到第 6 步说明的 RCTModuleMethod 生成的 block。
block 里带着 CallbackID 和 block 传过来的参数去调 JS 里 MessageQueue 的方法 invokeCallbackAndReturnFlushedQueue。
MessageQueue 通过 CallbackID 找到相应的 JS callback 方法。
调用 callback 方法,并把 OC 带过来的参数一起传过去,完成回调。
整个流程就是这样,简单概括下,差不多就是:JS 函数调用转 ModuleID/MethodID -> callback 转 CallbackID -> OC 根据 ID 拿到方法 -> 处理参数 -> 调用 OC 方法 -> 回调 CallbackID -> JS 通过 CallbackID 拿到 callback 执行。
附:JS-OC 通信详解篇幅参考来源于:React Native通信机制详解。
版权声明: 本文为 InfoQ 作者【梁龙先森】的原创文章。
原文链接:【http://xie.infoq.cn/article/396cc3720fd9cd78ca235847d】。文章转载请联系作者。
评论 (2 条评论)