热更新原理

  • react-native 的程序实际上是原生的模块+JS和图片资源模块,热更新,就是更新其中的js和图片资源。

  • 安卓程序把它名字命名为zip解压后可以清楚的看到其中的bundle文件和资源文件

热更新的方法

热更新又分为全量更新和增量更新。

  • 全量更新是直接去服务器抓取你上传的ppk文件,下载下来,直接覆盖本地的ppk文件。

  • 增量更新是使用了bsdiff算法,用来比对两者bundle之间的区别,然后只修改不一样的地方。

启动程序的时候,会发一个请求给服务器,询问我需不需要更新

URL: http://update.reactnative.cn/api/checkUpdate/${key}
抓包也好,看源码也好,结论就是使用的是这个请求带上自己热更新json的key值,用来匹配你的app

{
    "upToDate": true,
    "ok": 1
}

这个是服务器返回给我的值,如果是已经是最新的版本了,就会返回upToDate给我。

{
    "expired": true,
    "downloadUrl":'xxx',
    "ok": 1
}

如果是需要硬版本更新了,就会返回一个expired的给我,顺便给我一个downloadUrl(随便写的,具体可能不是这个名字,懒的再去删版本看了)的参数,当然这个参数是我在他家官网配置的,就是我新版本的下载地址。

这个时候,如果是需要热更新了,根据当前版本react-native-update@4.xxx的大量数据试验情况来看,返回格式无非就两种,虽然源码里有第三种

{
    "update": true,
    "hash": "FppJ-yU8-_bvYJe5Sg5_opUp_eFH",
    "name": "0.2.1",
    "description": "test",
    "metaInfo": "0.2.1",
    "updateUrl": "http://update-packages.reactnative.cn/FppJ-yU8-_bvYJe5Sg5_opUp_eFH?e=1483510148&token=made75kGFhOozkiRfa7LK_E1xG1pLOnhW8fhbnev:t2YXoxZZXQImvvyHH1hdrnNNRmQ=",
    "pdiffUrl": "http://update-packages.reactnative.cn/lpKbEZnU6_T-mvwZGfzIQby489Bm-FppJ-yU8-_bvYJe5Sg5_opUp_eFH.pdiff?e=1483510148&token=made75kGFhOozkiRfa7LK_E1xG1pLOnhW8fhbnev:YBI4sdIEr30wa1DHV4xnMUlI1bU=",
    "ok": 1
}

update为true代表我需要热更新,其中有个参数叫updateUrl,这个参数提供的地址就是全量更新的地址,会把我整个bundle都下载下来。
还有一个参数叫pdiffUrl,这个就是增量更新,我会去下载一个pdiff文件,然后你检测会发现,在安卓情况下,你的cpu已经跑起来了,后台程序数据涨起来了,但是流量没动,因为走的是内部计算,测试了一下发现在不同的手机上表现也不一样。
酷派A8930:120S
ViVioX7:90S
小米5:75S。
iphone全系手机1S-5S之间。(目测不是一个人写的代码,或者是安卓读写的线程出了某些问题,仅仅是猜测)
以上时间都是单单是计算时间,因为下载增量更新那几十K也就1S的事情。

关于服务器是否返回pdiffUrl的情况我还是不能推算出官方的原理,因为同样的一个包,有的时候它会返回pdiffUrl,有的时候却只有updateUrl,不过好在不管什么情况下,都有updateUrl,那么我们就可以利用这个点,把不必要的等待给改掉。
比如我流量很多,我不想让我的安卓机器等待100S,又不想后台静默更新,那就直接改源码,好在源码找的很快,修改更是容易。

export async function downloadUpdate(options) {
  if (!options.update) {
    return;
  }

  if (options.diffUrl) {
    await HotUpdate.downloadPatchFromPpk({
      updateUrl: options.diffUrl,
      hashName: options.hash,
      originHashName: currentVersion,
    });
  } else if (options.pdiffUrl) {
    await HotUpdate.downloadPatchFromPackage({
      updateUrl: options.pdiffUrl,
      hashName: options.hash,
    });
  } else {
    await HotUpdate.downloadUpdate({
      updateUrl: options.updateUrl,
      hashName: options.hash,
    });
    
  }
  return options.hash;
}

这个就是它判断是否走增量更新的代码,其中diffUrl我从来没见到过,所以不动逻辑,所以很简单,把增量和全量调换个顺序就OK了

export async function downloadUpdate(options) {
  if (!options.update) {
    return;
  }

  if (options.diffUrl) {
    await HotUpdate.downloadPatchFromPpk({
      updateUrl: options.diffUrl,
      hashName: options.hash,
      originHashName: currentVersion,
    });
  } else if (options.updateUrl) {
    await HotUpdate.downloadUpdate({
      updateUrl: options.updateUrl,
      hashName: options.hash,
    });
  } else {
    await HotUpdate.downloadPatchFromPackage({
      updateUrl: options.pdiffUrl,
      hashName: options.hash,
    });
  }
  return options.hash;
}

搞定,亲测后发现,安卓在即使服务器给了我pdiffUrl的情况下,我依然也会去选择全量更新,恩,也就费个3M流量,但是大家都是无线网,所以20S以内,这个热更新肯定就好了,就不需要等待那么久了。

问题

  • 我发现这个index.js文件里没有区分Ios和安卓,所以如果我想让ios走原来的逻辑还需要判断一下,所以后续会测试下好不好用。

  • 用上了bsdiff算法后,切记切记,你的下载地址的ipa包也好,apk包也好,一定一定要和update.reactnative.cn上的包保持完全一致。否则一旦第一次走的是增量更新,那么恭喜你,无限重启。
    为什么会出现这样的问题,我的猜测是:

(update.cn上的包是7号,给用户下载地址上的包是8号,8号的程序内容比7号仅仅是多了几个文案修改。这个时候,我推送一个热更新上去,名字叫10号。那么它的计算方式本来是10号减去7号的 = 3,然后拿着这个3去加7 = 10,更新成功。但是你手里的包却是8,他还是拿10-7=3,3+8 却=11!=10.boom爆炸。11!=10,无限热更新。却永远都对不上。所以无限重启)括号内容仅仅是猜测,没有和官方沟通。


jansen
130 声望16 粉丝

学习不能止步,学习就是兴趣!终生学习是目标