0评论

【进阶】全民系列游戏安卓多渠道增量更新方案

eldwinwang 2015-04-29 1k浏览

想免费获取内部独家PPT资料库?观看行业大牛直播?点击加入腾讯游戏学院游戏开发行业精英群711501594

随着安卓游戏应用的越来越酷,游戏安装包的体积也越来越大,同时游戏发布上线后玩法、数值等改变必然导致版本的不断更迭,游戏的升级就成了一个摆在游戏开发者面前的一个大问题。

以下全民系列游戏截至目前的安装包大小,大部分游戏的安装包都已经超过了50M。

http://km.oa.com/files/post_photo/965/210965/caf787b6d485143eb8d23efa7155d2f2.jpg

“战争总动员”游戏中采用了“增量更新机制”,当有版本升级时,只需要玩家下载两个版本的apk文件之间被修改的部分(通常较小),从而大大减少了安卓应用升级包的下载流量,也减少了版本更新的用户成本。

同时,针对不同发布渠道的安装包不一致的问题,“战争总动员”项目分析了渠道号写入机制,并合入到增量更新流程中,避免了不同的渠道包需要制作不同的升级包的问题。

 

1. 安卓应用升级机制

安卓上的应用程序安装运行机制与PC端应用不同,在pc端,可以将exe和资源直接拷贝到磁盘上运行。而在安卓下,应用程序必须通过安装以后才能正常使用。安卓系统安装一个应用时,系统大致会进行如下两个操作:

1)  把apk安装包复制到/data/app目录下,文件名会以应用的package命名;

2) 把dex文件(Dalvik字节码)保存到dalvik-cache目录,并data/data目录下创建对应的应用数据目录。

http://avocado.oa.com/fconv/files/201408/8e838c0f2089d02b799d3d5e0791437a.files/image002.gif

 

由于应用程序的资源都是经过压缩存放在apk文件中,因此应用程序在读取资源的时候会从apk文件把数据读取出来并使用。与pc端不同的是,存放在/data/app中的apk文件是只读不能写的,因此,当我们有资源更新的时候,无法将需要更新的文件写入apk中。

对于这些资源,可以将其放入SD卡上,通过fopen来直接读取,另外安卓系统在/data/data目录为每一个应用程序开辟了一个可读写目录,可以通过

getApplicationContext().getFilesDir().getAbsolutePath()

来获取可读写目录,将得到类似“/data/data/包名/files”的目录结构。

 

安卓系统下,通常使用的更新模式有如下几种:

a)  资源更新:若仅有针对资源和脚本的更新,可以将资源下载到SD卡或应用程序可读写目录,程序读取时优先读取这部分内容,再读取apk中的资源。

b)  全量更新:当apk包中的dex文件或so文件的内容有改变时,此时的更新就必须把整个apk下载到用户本地,再重新安装了。

c)  应用宝(或其他渠道)更新:通常不同的安卓应用渠道都提供了安装包的增量更新。但是这种更新的前提是玩家必须安装了应用或对应的渠道应用工具。对于没有安装的玩家来说,就无法使用到这样的功能,而且不同的发布渠道可能相互不支持。更新的简要逻辑如下:

http://avocado.oa.com/fconv/files/201408/8e838c0f2089d02b799d3d5e0791437a.files/image003.gif

d) 差异包增量更新:游戏自动下载差异包,自动合并安装,由于只下载两个版本apk之间的差异,所以通常升级版本消耗的流量较小,下面将做详细介绍。

 

2. 差异包增量更新机制

增量升级的原理很简单,即首先将应用的旧版本apk文件与新版本apk做差异分析,得到需要更新的增量包,例如旧版本的apk有62M,新版本有63M,更新的部分则可能只有1M左右,这样玩家在更新版本的时候,就不需要下载完整的63M文件,只需要下载1M的更新部分就可以了,从而很大程度上减少流量的损失。

根据上面描述的安卓应用的安装机制知道,安卓应用安装后,实际上系统的/data/app目录保留了与原始安装包完全一致的apk包,利用这个机制,我们可以在下载了增量包之后,在手机端将下载的增量包与这个原始包组合起来。同时保证这个生成的apk和新版本的apk是一致的。

了解原理后,其中首要的难点就是“增量包的制作”和“增量包与旧包的合并”。bsdiff是一种功能强大的二进制差异分析工具,可以通过这里下载到:

http://www.daemonology.net/bsdiff/

其中的bsdiff工具可以用来生成增量包,bspatch工具可以用来将增量包与原始包合并,得到新包。在使用中,需要将源码适当进行修改,将bspatch源码部分封装并编译为so库文件,引入到游戏工程中。

I) 增量包的制作:例如,通过如下命令可以生成530版本与531版本的增量包,生成增量包的时间会较长。http://avocado.oa.com/fconv/files/201408/8e838c0f2089d02b799d3d5e0791437a.files/image004.gif

II) 获取本地版本号:首先获取检查本地应用的VersionCode:

http://avocado.oa.com/fconv/files/201408/8e838c0f2089d02b799d3d5e0791437a.files/image005.gif

III)  检查版本:从/data/app目录下的旧版本apk包获取本地apk包的MD5编码,并将本地本地版本号和MD5值发送到版本服务器,与服务器上的最新版本的VersionCode做比较,如果有新版本,再把本地文件MD5与服务器端存储的对应版本apk的MD5比较,如果不一致,不能进行增量更新,如果一致,将增量包的url下载地址、增量包的MD5、全量包的MD5下发给客户端。获取系统安装包路径的java代码如下:

http://avocado.oa.com/fconv/files/201408/8e838c0f2089d02b799d3d5e0791437a.files/image006.gif

 

IV)  下载增量包:根据版本服务器下发的增量包的url下载地址,从服务器下载增量更新包到本地SD卡上。下载完毕后检查下载文件的MD5是否有效。

 

V) 生成安装包:将增量包与本地旧版本apk做bspatch合并,生成新版本的apk安装包,调用bspatch源码部分封装的接口来生成:http://avocado.oa.com/fconv/files/201408/8e838c0f2089d02b799d3d5e0791437a.files/image007.gif

VI) 安装新版本:首先将生成的新版本apk的md5与服务器下发的md5做比较,如果一致,直接执行该apk文件的安装操作:

http://avocado.oa.com/fconv/files/201408/8e838c0f2089d02b799d3d5e0791437a.files/image008.gif

注意事项:

a)   以上流程中,任意一步操作失败或MD5校验失败,都需要提示用户进行全量包更新;

b)   需要对本地安装的原始apk进行校验,因为有的反编译工具可能对apk进行过修改,被修改过的apk是不能用来进行合并的;

c)   安装包的合并操作不能在应用程序的可读写目录操作(/data/data/包名/files),最好在SD卡上操作——当apk文件过大时,可能会因为系统缓存更新延迟,导致生成的apk文件不能执行安装操作;

d)   拉起新版本apk的安装以后,需要把当前的activity进行finish调用,否则新版本安装完毕后,不会提示“打开”界面。

 

3. 多渠道更新

通常安卓应用都有很多发布渠道和推广市场,为了方便经分统计用户来源,通常需要在生成apk安装包的时候,在安装包里加入渠道标记,在应用程序运行过程中获取该渠道号并发送回服务器,比如公司的游戏在登录、支付时都会上报游戏的渠道号。

公司提供了专门的工具生成apk包的渠道号:

http://avocado.oa.com/fconv/files/201408/8e838c0f2089d02b799d3d5e0791437a.files/image009.gif

同一个游戏在多个应用渠道发布,给增量更新带来了棘手的问题:不同渠道生成的apk文件的MD5编码是不一致的。根据上述增量更新机制:制作增量包时的apk文件与进行合并时的apk文件(即用户本地安装的apk文件)必须完全一致,否则会导致bspatch失败,或合并出来的包不可用。而渠道打包会对原始apk包做修改,也就是说用户本地的安装包与原始包已经不一致了。

简单的做法是,发布版本时把不同渠道的包都收集起来,针对每个渠道都做不同的增量包,升级时根据渠道号来下载不同的增量包。而显然这种做法每次版本升级的工作量都非常大。

              过分析渠道号的生成机制,可以解决这一问题。只需要生成一个增量包,可以被多个渠道包用来做增量升级。

A) 渠道打包机制分析

我们知道,apk文件本身就是zip格式文件,通过对渠道包的分析,我们可以知道渠道打包的基本原理。

Zip文件末尾有一段叫做“End of central directory record (EOCD)”的段,格式如下:

http://avocado.oa.com/fconv/files/201408/8e838c0f2089d02b799d3d5e0791437a.files/image010.gif

其中末尾的Comment位于文件的末尾,长度是不固定的:Comment length指定了Comment的长度。Comment中的内容可以任意写入并修改,只要Comment length指定的长度正确,修改后的zip包仍然可以正确打开使用。

安卓渠道号的写入其实就是把渠道内容写入到Comment中,并修改了Comment length的内容。初次编译出来的apk包是不包含Comment内容的,且Comment length字段为00 00。

下图是将“战争总动员”1.4.7版本通过公司的渠道工具写入应用宝渠道号2002后的二进制内容,其中,黄色部分是EOCD段的开始标志;红色部分就是Comment length字段,0x1400(20)表示Comment字段的内容长度是20个字节;其中绿色部分包含了应用宝渠道号(其中的具体格式我们不需要关注)。

http://avocado.oa.com/fconv/files/201408/8e838c0f2089d02b799d3d5e0791437a.files/image011.gif

 

我们再来对比“战争总动员”1.4.7版本在未写入渠道号之前的情况:r如下图,黄色框中的是EOCD标识的开始,从下图可以看到Comment length字段为00 00,表示Comment中没有内容。

http://avocado.oa.com/fconv/files/201408/8e838c0f2089d02b799d3d5e0791437a.files/image012.gif

因此,只要我们只要去除apk中的Comment字段,并把Comment length置为0,就可以得到与原始包完全一致的apk包。

B) 增量更新机制完善

通过以上分析我们知道,Comment中的渠道内容可以任意删除修改,完全删除以后的包与我们的原始包完全一致,且不影响apk的使用。

利用这一机制,我们可以这样来做版本升级:

a、制作增量包时使用未写入渠道号的apk文件来制作;

b、从/data/app目录中获取到本地安装包后,根据zip文件格式,判断apk文件是否是渠道包,如果是,读取渠道号的内容buffer,保存下来;

c、根据上述zip文件格式规范,将其渠道号抹去,并作为临时apk文件存储在本地SD卡上;

d、将增量包与生成的临时apk文件做合并,生成没有渠道号的apk包;

e、将步骤b获取到的渠道号内容回写到新生成的apk包中,保证升级后的版本的渠道号与升级前一致。

通过这一机制,就可以保证游戏在多渠道发布的情况下采用同一个增量更新包也能正常升级到最新版本。

 

http://avocado.oa.com/fconv/files/201408/8e838c0f2089d02b799d3d5e0791437a.files/image013.jpg http://avocado.oa.com/fconv/files/201408/8e838c0f2089d02b799d3d5e0791437a.files/image014.jpg http://avocado.oa.com/fconv/files/201408/8e838c0f2089d02b799d3d5e0791437a.files/image015.jpg http://avocado.oa.com/fconv/files/201408/8e838c0f2089d02b799d3d5e0791437a.files/image016.jpg