React Native 详细实现热部署、增量差异化热更新

EmeryA35 8年前
   <p>本篇文章是React Native中比较经典的内容:热更新部署。</p>    <h2>(一).前言</h2>    <p>Android原生App中我们实现热修复有很多种选择:Tinker、hotFix、Qzone的热更新等等。基本的思路都是大同小异的。React Native中的热更新有点像App的版本更新,也就是根据查询server端的版本和手机端目前App的版本进行对比,然后来执行是否更新的操作。根本原因在于React Native的加载启动机制:React Native会将一系列资源打包成js bundle文件,系统加载js bundle文件,解析并渲染。所以,React Native热更新的根本原理就是更换js bundle文件,并重新加载,新的内容就完美的展示出来了。微软为我们提供了CodePush来简化热更新的操作,但是由于速度等原因在国内并没有备受青睐。本篇内容就以自己服务器来更新的方式实现。</p>    <p>前面简单的说了些基本原理,接下来先上一张具体的更新流程图:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/a3ac49f92b84e19bcb6171dc085841bc.png"></p>    <p>上面流程图中展示了如何实现更新的步骤,可以总结为如下几点:</p>    <p>进入App根据版本检查是否需要更新:</p>    <p>(1)更新:</p>    <p>下载最新JsBundle文件以及所需要的图片资源等,下载完成后解析最新JsBundle文件。</p>    <p>(2)不更新:</p>    <p>判断本地是否还有缓存的JsBundle文件:</p>    <p>1>存在:</p>    <p>本地存在JsBundle,即有过热更新操作。那么App直接加载在缓存目录下的JsBundle文件。</p>    <p>2>不存在:</p>    <p>本地不存在JsBundle,即之前从未有过热更新操作。那么App只能使用初始化时打包在assets目录下的index.android.bundle文件。</p>    <p>Ok,根据上面的流程,我们来看下代码实现过程:</p>    <h2>(二).具体实现</h2>    <p>2.1.检查是否需要更新</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/a3d5034cb9b131da6190fee915b786b8.png"></p>    <p>实现步骤即请求服务器中的版本号,然后与本地版本号进行对比,此处我为了代码清晰易懂,直接执行下载更新的流程。</p>    <p>2.2.Android为我们提供了下载工具类:DownLoadManager,我们使用它来执行下载</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/9875816a18c45da54ee6c1a7b3db876f.png"></p>    <p>首先去判断是否存在有下载的更新压缩包,如果有,则先删除旧的,然后下载最新压缩包。</p>    <p>2.3.下载完成后,DownLoadManager会发出一个DownloadManager.ACTION_DOWNLOAD_COMPLETE的广播,在收到广播后,对比下载任务ID</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/1347038696fdc3d457f2c217f2d9e061.png"></p>    <p>因为我们下载的是Zip压缩文件( Zip压缩文件体积下,有效控制了由于更新文件大以及图片资源占用给用户带来消耗流量的问题 ),所以我们需要先解压</p>    <p>2.4.解压Zip</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/52cbab0e0a2d84e29e584655c43b1b66.png"></p>    <p>2.5.解压完成后,加载最新Bundle和图片资源</p>    <p>如何控制RN加载Bundle的方式呢?没错,0.26版本之后的RN系统在ReactApplication下的ReactNativeHost为我们提供了getJsBundleFile方法,在该方法中默认返回null,即加载assets下的bundle文件。我们可以根据条件来加载不同目录下的bundle文件即可</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/8bc214a97bf3e123ed59048126183f08.png"></p>    <p>在当我们下载好最新更新文件后,跳转到RN界面,即会执行getJSBundleFile方法来执行加载Bundle文件的方式。在实际应用当中,我们可以在Splash页面去执行检查更新下载,然后在跳转到RN界面时,最新文件就会呈现出来。</p>    <p>如何获取最新的bundle文件和图片资源呢?我们在RN项目根目执行以下命令来得到bundle文件和图片资源:</p>    <p>react-native bundle --entry-file index.android.js --bundle-output ./bundle/index.android.bundle--platform android--assets-dest./bundle--dev false</p>    <p>(1)--entry   入口js文件,android系统就是index.android.js, <a href="/misc/goto?guid=4959742571462911723" rel="nofollow,noindex">iOS</a> 系统就是index.ios.js</p>    <p>(2)--bundle-output   生成的bundle文件路径</p>    <p>(3)--platform   平台</p>    <p>(4)--assets-dest  图片资源的输出目录</p>    <p>(5)--dev   是否为开发版本,打正式版的安装包时我们将其赋值为false</p>    <p>执行命令之前,首先要在根目录下创建好bundle文件夹,bundle文件和图片资源将会输出到已创建好的bundle文件夹下。</p>    <p>解压后的最新更新文件:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/b72b16897110a68f4436c59219adb9a8.png"></p>    <p>到此,我们便完成了代码的热更新工作。</p>    <h2>(三).优化补丁包更新</h2>    <p>大家可能会说,如果bundle太大的情况下怎么办呢?没错,这个问题同样在博客开始也提到了。打包成zip也是为了减小更新文件体积,减少用户流量消耗,同样,我们也可以生成用生成补丁包的方式来进一步减小更新包zip的体积。</p>    <p>初始项目发布时,生成并保留一份index.android.bundle文件。</p>    <p>有版本更新时,生成新的index.android.bundle文件,使用google-diff-match-patch对比两个文件,并生成差异补丁文件。app下载补丁文件,再使用google-diff-match-patch和assets目录下的初始版本合并,生成新的index.android.bundle文件。ok,来看下核心代码:</p>    <p>3.1.生成补丁包</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/653458b497b9f301ad4054e7c4c14c6b.png"></p>    <p>3.2.下载完成,解压后执行mergePatAndAsset方法将Assets目录下的index.android.bundle和pat文件合并</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/e8c9feae321b43d8d6c5c1d3be44861f.png"></p>    <p>3.3.合并</p>    <p><img src="https://simg.open-open.com/show/509314c04c5d26278414d8495b3f2f20.png"></p>    <p>从上述代码中我们看到,合并分为如下过程:</p>    <p>(1)获取Assets目录下的bundle文件,转换为字符串</p>    <p>(2)解析.pat文件将其转换为字符串</p>    <p>(3)调用patch_fromText获取patches补丁包</p>    <p>(4)调用patch_apply方法将第四步中生成patches补丁包与第一步中获取的bundle合并生成新的bundle</p>    <p>(5)保存bundle</p>    <p>3.4.读取pat文件的方法:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/e2e3fef67f3800b2c965e628bc20b241.png"></p>    <p>3.5.读取Assets目录下的bundle文件:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/ed96fcf6cef0085ba09495cb98c8a83b.png"></p>    <p>以上步骤执行完成后,我们就获取到了新的bundle文件,继而加载新的bundle文件,实现React Native热更新。</p>    <h2>(四).效果演示</h2>    <p>为了演示,先来看更新前的界面:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/34e211be39a86dbf56622273af7733c6.gif"></p>    <p>点击加载最新Bundle,下载最新的,然后加载最新界面:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/602550620d4ac427d583933f35434a4f.gif"></p>    <p>以上就是使用React Native关于热更新的内容,其实还有很多不足地方,例如对更新文件进行加密,防止被恶意修改等等一些内容还需要不断完善。</p>    <p> </p>    <p> </p>    <p>来自:http://www.lcode.org/react-native-hot-update/</p>    <p> </p>