普益基金 “RN 热更新“ 解决方案

回到标题开始

 "RN 热更新" 解决方案

转至元数据结尾

  •  

转至元数据起始

 

本文主要分为以下几个部分:

  • 实施新方案的背景
  • 新热更新解决方案的具体描述
  • 客户端热更新相关事件统计
  • 需要后台配合所做的变更
  • 如何发布补丁(第一步是什么,第二部是什么)

实施背景

现有的通过 https://github.com/google/diff-match-patch 进行补丁合并的方案仅适用于 jsbundle 这种纯文本的补丁更新,对于图片资源的需要进行全量更新而不能通过补丁来更新。同时在测试过程中,发现:

  • 在 jsbundle 大小为 1.5M 的时候,差分出来的 patch 文件大概在 3M 左右。
  • Android 在进行合并操作的时候,读 App 中的 jsbundle 大致需要1分钟,读本地的 .patch 文件大致需要 2 分钟,合并过程需要 1 分钟或数分钟,且在调试合并逻辑时发现各个部分的操作也都需要花费很多时间。
  • 现有方案没有对合并文件进行 MD5 校验,因此无法确保合并后的文件就是预期的新版本 jsbundle。
  • 现有方案没有对热更新相关的事件进行记录,无法准确统计热风新的成功率,覆盖范围等。

 

通过实施新方案后,得到的益处有:

  • 图片加 bundle 一起压缩后的大小为 450 KB 左右,同样代码量原有方案大小在 1750 KB 左右,从而使得安装包小了 1.3 M 左右。
  • 新增图标和修改一行代码后,生成的 patch 文件大小在 410 KB 左右,下载速度更快。
  • 在测试环境测试时,补丁下载并进行合并生成新 jsbundle 文件的事件大致在 3 秒内,热更新速度更快。
  • 发生合并操作后,支持对合并生成的新文件进行 MD5 校验,热更新稳定性更好。
  • 合并错误和合并成功会进行事件统计,能有效监控线上版本热更新的成功率,并能够对当前用户的 RN 版本进行统计。

 

具体方案描述

 

 

打包脚本

对于客户端:

通过编写 node 脚本执行 RN 的打包命令,然后将 jsbundle 文件 (单个) 和图片文件 (多个) 压缩成一个 .zip 文件。

node 脚本会计算该 .zip 的 MD5,并且连同脚本配置的 "最低支持" 版本等信息生成一个 .json 文件来描述该 .zip 文件。

App 打安装包时会包含且仅需要包含该 .zip 文件和 .json 文件。

 

对于服务端:

node 脚本会根据脚本配置中的 "支持热更新的版本" 来生成 .patch 文件 (通过 bsdiff 这个差分工具来实现) 

如果有生成 .patch 文件的话,也会生成一个对应的 .json 文件来描述该 .patch 文件。

同时,不管有木有 .patch 文件,脚本都会生成一个 android.json 或 ios.json 文件来描述当前最新的 bundle 版本、以及是否启用热更新等配置。

发布热更新时,需要将 .path (及对应的 .json 文件) 和 android.json/ios.json 这三个文件放置到服务器对应目录下即可。

 

App 正常渲染

第 1 次启动时:

App 在启动后(打开资产页面前),需要将安装包里面的 .zip 和 .json 文件复制到沙盒 (手机本地) 中,然后解压 .zip 得到 jsbundle 和对应的图片资源。

然后可根据情况启动预加载(以加快资产页面渲染速度),资产页面打开后会读取沙河中的 bundle 文件渲染页面 (图片会根据 bundle 的相对路径进行读取)。

 

第 2..N 次启动时:

如果本地有 bundle 文件,无需再进行复制解压等操作,直接预加载 bundle 然后进入资产页面即可。

 

App 热更新后渲染:

  • 启动时,调用服务器接口查询热更新配置,如果有热更新:
    • 下载对应当前安装包 bundle 版本和最新版本的 patch 文件到沙河中 (假设 App Bundle 版本是 0.0.2,最新版本是 0.0.3,则补丁文件名为 0.0.2_0.0.3.patch)
    • 然后复制应用版本中的 .zip 文件到沙盒中,然后跟 .patch 文件合并生成新的 .zip 文件 ( old.zip + .patch = new.zip )
    • 解压 new.zip,然后通知现有页面重新进行渲染 (new.zip 解压后的 jsbundle 文件路径跟之前是一样的)
  • 如果没有热更新:
    • 当做没事发生,谢谢。

 

脚本逻辑和使用

脚本代码及其打包后的产物都跟现有业务代码在同一个 Git 仓库中(可参考下面两张图):

  • 脚本代码目录: ./scripts/rn-packer
  • 脚本打包产物目录:./packer

 

 

执行 packer 脚本的命令:

 

使用示例

假设我们之前已经打了个 0.0.2 版本的包,现在去修改了一行代码、修改了一张图片、增加了一张图片:

 

 

 

 

然后,我们到 ./scripts/rn-packer/configs.ts 中修改打包配置(新的版本是 0.0.3,并且需要打 0.0.2 热升级到 0.0.3 的补丁文件):

 

 

接着,再次执行 npm run packer 命令:

 

此时会发现我们在 app 目录的新 0.0.3 目录中得到了新的 .zip 文件,并且在 server 目录中的 0.0.2 中得到了该版本升级到新的 0.0.3 版本的补丁包。

 

在接着,将 server 目录下的生成的补丁文件和描述热更新信息的 "android.json / ios.json" 文件复制到服务端 (注意目录和打包脚本的目录是对应的):

 

 

以 Android 为例,放置服务端的两个 json 文件内容如下:

android.json

 

 

 

 

 

 

 


0.0.2_0.0.3.json (仅作为 .patch 文件的描述,在热更新实际过程中不会用到)

 

 

 

 

 

 

 

下面是热更新前后的效果图

  • 打补丁前

            

 

  • 打补丁后

       

 

热更新前后,Android 沙盒里面文件的变更

  • 热更新前:

           

 

  • 热更新后 (右边的箭头标错了,应该是 newBundleFile.zip 指向图片资源,同理左边箭头也错了):

          

 

Android 端的逻辑

启动时:复制安装包中的文件 zip 和 json 文件到本地,并解压。

 

 

进入到资产页面时,根据本地 jsbundle 文件渲染内容。

同时调用接口查询热更新信息(后面改为调用支持灰度更新的接口)。

 

 

调用支持灰度的热更新信息接口成功后,需要判断以下异常逻辑:

  • 是否有热更新信息 (开启灰度后,不在白名单的用户不会返回)。
  • 根据 open 字段表示判断是否启用热更新。
  • 根据 latestBundleVersion 字段判断当前安装包中的 bundle 版本是不是最新的,是最新的话则不需要热更新。
  • 根据 supportPatchBundleVersions 判断热更新是否支持当前安装包中的 bundle 版本,不支持的话则不需要热更新。
  • 根据 minSupportAppVersion 判断当前 App 的版本是否支持热更新(因为后续可能会升级 RN SDK,不同的 RN SDK 不能使用同一个 bundle 文件)。

 

下载补丁后:

 

合并前后主要设计到沙盒中的三个目录:

  • latest:始终作为存放最终渲染的 jsbundle 和图片资源文件的目录,执行合并操作前会被重命名为 backup。
  • download:作为存放下载后的补丁,并进行补丁合并、MD5 检验的目录,逻辑都正确走完后会重命名成 latest。 
  • backup:作为 latest 的备份目录,如果合并操作失败的话,会重命名回 latest。

 

客户端事件统计

Android 和 iOS 开发人员在热更新过程中,需要通过 GrowingIO 统计这个过程中相关的自定义事件。

参考代码

// Android: track API调用示例二
GrowingIO gio = GrowingIO.getInstance();
JSONObject jsonObject = new JSONObject();
jsonObject.put("gender", "male");
jsonObject.put("age", "21");
gio.track("registerSuccess", jsonObject);

 

// iOS:  track API调用示例二
[Growing track:@"registerSuccess" withVariable:@{@"gender":@"male", @"age":@"21"}];

自定义事件

事件名称:RN热更新

事件标识符:RnPatch

    
    
    
 

 

 

 

 

 

  

 

GIO 效果图如下:

 

后台需要配合实现的逻辑

  • 提供一个类似 APP 升级的接口,支持根据白名单进行灰度发布。
  • 假设开启灰度且当前请求该接口的用户在白名单内
    • 如果是 Android 用户,读取 /fundmobile/packer/server/android/android.json 文件的内容并原封不动的返回到客户端。
    • 如果是 iOS 用户,读取  /fundmobile/packer/server/ios/ios.json 文件的内容并原封不动的返回到客户端。
  • 假设当前用户不在白名单内,直接返回空字符串 (就当做没有读取到对应平台的 json 文件内容) 给客户端。

 

接口设计

接口:https://www.puyifund.com/fundmobile/checkRnVersion.do

名称:查询 RN Bundle 版本信息

入参:无

出参:

    
 

 

  •  
  •  

 

 

 

 

  
    

示例:

 

 

如何发布升级补丁

第一步

Android 和 iOS 开发人员分别提供补丁及其相关文件:

  • Android 平台: 0.0.2_0.0.3.patch、0.0.2_0.0.3.json 和 android.json 这三个文件。
  • iOS 平台: 0.0.2_0.0.3.patch、0.0.2_0.0.3.json 和 ios.json 这三个文件。

注意,上面的 patch 和 json 这两个文件,两个平台是不一样的。

 

另外,开发人员在提供上述文件时,注意比较是否跟生产包所携带的 .zip 是否对应。

如何比较?

  • 在 46 环境进行实际热更新测试。
  • 对比代码仓库 packer/app 目录下的 android-bundle.json / ios-bundle.json 和生产包的是否一致,
    然后执行另外编写的校验脚本命令 'npm run packer-test' 进行模拟的补丁合并操作,日志会告诉你模拟是否成功。 

 

第二步

配置人员 先开启灰度,然后将上述的 .patch、.json 文件放到服务器对应目录下:

Android 平台:

  • /fundmobile/packer/server/android/0.0.2/0.0.2_0.0.3.patch
  • /fundmobile/packer/server/android/0.0.2/0.0.2_0.0.3.json
  • /fundmobile/packer/server/android/android.json

 

iOS 平台:

  • /fundmobile/packer/server/ios/0.0.2/0.0.2_0.0.3.patch
  • /fundmobile/packer/server/ios/0.0.2/0.0.2_0.0.3.json
  • /fundmobile/packer/server/ios/ios.json

 

第三步

白名单用户测试热更新是否正常:

  • 正常:关闭灰度,或在白名单用户热更新后观察一定时间在关闭。
  • 不正常:开发人员分析原因
    • 根据 android.json 和 ios.json 文件中的 「open」「latestBundleMD5」等字段判断是否开启热更新,支持的热更新版本,客户端版本。
    • 根据 patch 文件的 json 文件中的 「version」、「platform」 和 「patchFileMD5 」等字段判断 patch 文件是否放错,或平台放置位置调换了。
    • 其它。

 

附注

I. 白名单

文件位置:/webapps/fundmobile/WEB-INF/properties/grayuserlist.properties

配置示例(注意右边的值是 userId,不是手机号):

 

上面的两个 userId 目前是通过 Debug 断点方式获取的,如下图:

 

II. 灰度开关

文件位置:/webapps/fundmobile/WEB-INF/properties/sysconfig.properties

配置示例(on 表示开启灰度,白名单用户才能获取到热更新信息;off 表示关闭灰度,所有用户都能获取到热更新信息,如果配置了的话 ):

 

III. 几张示例图

   

 

IV. 测试用例 ( 重要 )

用例一:假设当前安装包的 bundle 版本是 0.0.2,在服务端发了 0.0.3 的补丁,能正常热升级。

用例二:在用例一基础上, 在服务端再发了个 0.0.4 的补丁,能正常热升级。

用例三:在用例一基础上, 在服务端再发了个 0.0.5 的补丁,也能正常热升级 ( 跨版本,但本质上还是从 0.0.2 → 升级到 0.0.5 )。

用例四:在用例一基础上,覆盖安装 bundle 版本为 0.0.3 的安装包,确保覆盖安装后的的 bundle 版本还是 0.0.3 。

用例五:在用例一基础上,覆盖安装 bundle 版本为 0.0.4 的安装包,确保覆盖安装后的的 bundle 版本变成了 0.0.4 。

用例六:在用例五基础上,在服务端发了个 0.0.6 的补丁,确保能正常热升级到 0.0.6 。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值