基于DX的Android动态更新技术
热文导读 | 点击标题阅读
金九银十跳槽季如何进阶找到合适满意的工作?
转自:文/Mob开发者平台 技术副总监 余勋杰
DX简介
安卓程序的主要代码是java 代码,不过由于安卓系统不直接使用sun的jvm,所以从javac编译过来的class文件并不能直接被安卓系统加载运行。
要想运行java代码,需要除了和以前一样调用javac将java代码编译为class文件以外,还需要调用dx这个工具,将class文件转成dex文件。然后安卓系统才会去加载dex文件并执行程序。因此说,dx是将class文件(及class文件集合)转成dex的工具。
dx属于安卓开源项目的一部分,它的源码位于“external/dexmaker”目录下,纯java。由于是纯java工具,所以可以打包成依赖库放到我们的 项目里面,实现安卓平台上,将一组class文件或一个jar文件转换成dex的功能。下面我将通过代码演示将一组class文件编译成dex,并加载运 行。
使用DX
导入class文件
程序的第一步是将class文件以字节数组的方式读入内存。为了演示方便,这里我将运行时编译的class集合选择为MobTools,为了简化示例程序,我事先将它解压到项目的assets中。下面的代码演示的是以递归的方式读取所有class文件到内存中:
初始化dex配置信息
载入class文件后,就可以将它们写入dex,但是dex会有一些配置信息需要初始化,下面的代码演示了这个过程:
我并没有仔细查阅过这两个类的用途,希望后续使用此功能的同事可以追加注释。
添加class文件
接下来是将class类名和数据添加到dex实例中:
DexFile是dex数据在内存中的抽象,可以理解为一个列表。列表中存放着一个个ClassDefItem,携带着各个class的完整路径和描述数据。
我在添加的过程中利用ClassDefItem对类描述数据的解析功能,获取了每一个类的父类和接口列表,并缓存了起来。这个会在后续加载dex的时候用到。
保存dex文件
DexFile是dex文件在内存的解析结果,不能直接拿来使用。安卓系统加载dex都是基于文件的,所以现在还需要将DexFile转成dex数据:
toDex的第一个参数是日志输出工具,如果设置为null,表示不输出日志。
完成转换以后得到一个byte数组,只要将byte数组写入文件,并保存为dex后缀就可以被多数的安卓系统加载。但是对于一些定制的系统,比方说小米,他 们只识别压缩包里的dex,比方说jar包里的dex。这种方式普通系统也是支持的,为了更好的兼容性,我将dex文件保存到了应用私有目录下的一个 jar文件中:
数据会保存到“/data/data/包名/files”下的MobTools.jar中。打开个jar包,里面就是我们熟悉的“classes.dex”文件。
加载dex文件
有了文件,现在就可以调用系统的api来加载它:
dalvik.system.DexFile是android.jar里面提供的api,虽然名字也是DexFile,但是功能和dx里面DexFile不同。使用时通过其静态方法loadDex来获取实例。传入的参数jarPath表示jar包的路径,dexPath表示准备加压的dex文件路径。
由于MobTools中有很多各类,而且不同的类有继承关系,所以这里我开辟了一个方法loadClass做递归加载。其原理是:循环获取 classesNames列表中的第一个元素尝试进行加载,加载前判断这个类是否有父类,如果有,将这个父类从classesNames列表中取出,然后先对这个父类进行加载。如果被加载的类没有父类,则再检查他是否有实现什么借口,如果有,循环加载这些接口的类。父类和接口列表都完成加载以后,再加载最 初尝试加载的类。这样子就不会再加载的过程中报出“子类找不到父类”的问题。
使用
完成了上述的操作后,原先assets中的class文件就被全部加载如内容,可以被使用了。
使用的方式有两种,一直是基于反射,先获取某个类的Class 对象,然后通过Class对象获取方法或字段,最后invoke或者get/set。第二种方式是实现通过依赖mobtools的方式编写一份代码,并将 这份代码编译为jar包,被我们的项目依赖;等到mobtools加载完毕后,代用这个jar包中的api,使用其中的功能。
这两种办法各有优劣。第一种办法编写起来很麻烦,容易出错,但是随时可以改变逻辑。第二种办法由于编码的时候依赖了mobtools,所以能以常规的编码方式进行开发,后来使用时调用它的api时也比较直观。但是它在使用时已经是静态的jar包,要改动比较麻烦。
动态更新
我研究dx的目的在于动态更新,否则在安卓系统上编译和加载class文件并没有什么意义。一般操作方式是这样子:首先,对于一些可能日后要改变的功能,编 译代码时不将它们打包到apk的dex中,而是打包到zip文件,当做资源的方式随包发布;使用时会对zip文件进行解压,然后运行时合并为dex,并 path到内存中使用。然后,等项目的逻辑改变了,服务端可以向客户端推送改变逻辑后的class文件集合,去覆盖原来的class文件;等到下一次客户 端重新启动时,发现class文件版本已经改变了,于是再度动态合并dex,并path。
如此,apk的升级可以变得频繁,而且每次都是增量更新。如果用户删除了缓存的数据,应用只会恢复到初始状态,然后再从服务端获取最新版本的“class补丁”替换合并即可。
甚至,如果服务端的性能更强的情况下,可以对不同的客户端下发不同的class文件,比方说为不同的客户端定制RSA的公钥,或者其它的配置信息等等。
与groovy结合
如果嫌以class为单位来更新代码,颗粒度还不够的话,还可以结合groovy的解析器。
groovy 是一种脚本语言,它基于java虚拟机,并且完全兼容java语法。groovy能将自己的脚本编译为java的class,所以刚好可以被我们用来减少 动态更新的颗粒度。比方说一开始我们发布的时候不是将class文件压缩为一个zip,而是直接压缩java源码。运行后调用groovy来解析这些 java源码,将它们编译为class文件,再调用dx对这些class文件进行合并。升级的时候,服务端指定具体的源码文件的具体段落进行替换,可以替 换一整个包,也可以只是替换一行代码。替换以后再调用groovy重新编译即可。
妈妈常教导我,让我养成良好习惯。这样长大才能成为一个有用的人。良好的习惯是尊敬师长这样长大才能成为一个有用的人。良好的习惯是尊敬师长,爱护同学,对人有礼貌;是不粗心,做事情不拖拉;还是爱护公物,不浪费粮食。为什么呢?因为拥有良好习惯,做一个品德高尚的人,懂得尊重别人,才会得到别人的尊重。我要努力地做到这些。我有一些坏习惯,有时候学习很粗心,把一些会做的题做错。在生活上,也很粗心,有一次早上起床居然穿反了衣服。我吃饭很慢,有的时候还剩饭。我还起床磨蹭,本来应该迅速地穿好衣服,但是,我总是磨磨蹭蹭地,速度很慢。“我打算在这学期里,改掉这些坏习惯。早上起来,迅速地穿好衣服,不拖拉。学习不粗心,仔细完成每一道题。吃饭的时候,要很快的把饭吃完,不剩饭。我要从一点一滴做起,逐渐养成良好习惯。我相信自己一定能成为一名品学兼优的好学生!我打算在这学期里,改掉这些坏习惯。早上起来,迅速地穿好衣服,不拖拉。学习不粗心,仔细完成每一道题。吃饭的时候,要很快的把饭吃完,不剩饭。我要从一点一滴做起,逐渐养成良好习惯。我相信自己一定能成为一名品学兼优的好学生!” 在上幼儿园以前,我什么也不会干,就连穿衣服也是妈妈给我穿好,就要上幼儿园了,这样可不行,妈妈锻炼我要学会自己穿衣服。 有一天,妈妈把衣服摆在我面前,开始让我自己穿。一开始。我又哭又叫就是不穿,还把衣服扔的满地都是,然后坐在地上开始大哭,等了好长时间,妈妈还是不理我,我只好自己乖乖的把衣服穿好, 一出了房间门,妈妈就笑了起来,再看看我的衣服,毛衣和裤子都穿反了,我赶紧回房间又重新穿了一遍,这次穿好了,拿起外套,可是外套的扣子又扣不上了,扣子可调皮了,好像故意和我作对,我把扣子往扣眼——人类邪恶的根源;爱情——幸福和光明的源泉。我一直在这些思想的舞台上徘徊。突然我发现两个身影从我面前经过,坐在不远的草地上。这是一对从农田那边走过来的青年男女。农田那边有农民的茅舍。在一阵令人伤心的沉默之后,随着一声长叹,我听见从一个肺痨病人的嘴里说出了这样的话:幸福和光明的源泉。我一直在这些思想的舞台上徘徊。突然我发现两个身影从我面前经过,坐在不远的草地上。这是一对从农田那边走过来的青年男女。农田那边有农民的茅舍。在一阵令人伤心的沉默之后,随着一声长叹,我听见从一个肺痨病人的嘴里说出了这样的话幸福和光明的源泉。我一直在这些思想的舞台上徘徊。突然我发现两个身影从我面前经过,坐在不远的草地上。这是一对从农田那边走过来的青年男女。农田那边有农民的茅舍。在一阵令人伤心的沉默之后,随着一声长叹,我听见从一个肺痨病人的嘴里说出了这样的话幸福和光明的源泉。我一直在这些思想的舞台上徘徊。突然我发现两个身影从我面前经过,坐在不远的草地上。这是一对从农田那边走过来的青年男女。农田那边有农民的茅舍。在一阵令人伤心的沉默之后,随着一声长叹,我听见从一个肺痨病人的嘴里说出了这样的话幸福和光明的源泉。我一直在这些思想的舞台上徘徊。突然我发现两个身影从我面前经过,坐在不远的草地上。这是一对从农田那边走过来的青年男女。农田那边有农民的茅舍。在一阵令人伤心的沉默之后,随着一声长叹,我听见从一个肺痨病人的嘴里说出了这样的话幸福和光明的源泉。我一直在这些思想的舞台上徘徊。突然我发现两个身影从我面前经过,坐在不远的草地上。这是一对从农田那边走过来的青年男女。农田那边有农民的茅舍。在一阵令人伤心的沉默之后,随着一声长叹,我听见从一个肺痨病人的嘴里说出了这样的话幸福和光明的源泉。我一直在这些思想的舞台上徘徊。突然我发现两个身影从我面前经过,坐在不远的草地上。这是一对从农田那边走过来的青年男女。农田那边有农民的茅舍。在一阵令人伤心的沉默之后,随着一声长叹,我听见从一个肺痨病人的嘴里说出了这样的话幸福和光明的源泉。我一直在这些思想的舞台上徘徊。突然我发现两个身影从我面前经过,坐在不远的草地上。这是一对从农田那边走过来的青年男女。农田那边有农民的茅舍。在一阵令人伤心的沉默之后,随着一声长叹,我听见从一个肺痨病人的嘴里说出了这样的话幸福和光明的源泉。我一直在这些思想的舞台上徘徊。突然我发现两个身影从我面前经过,坐在不远的草地上。这是一对从农田那边走过来的青年男女。农田那边有农民的茅舍。在一阵令人伤心的沉默之后,随着一声长叹,我听见从一个肺痨病人的嘴里说出了这样的话“亲爱的!擦干你的眼泪,至高无上的爱情已经打开了我们的眼界,使我们成了它的崇拜者。是它,
妈妈常教导我,让我养成良好习惯。这样长大才能成为一个有用的人。良好的习惯是尊敬师长这样长大才能成为一个有用的人。良好的习惯是尊敬师长,爱护同学,对人有礼貌;是不粗心,做事情不拖拉;还是爱护公物,不浪费粮食。为什么呢?因为拥有良好习惯,做一个品德高尚的人,懂得尊重别人,才会得到别人的尊重。我要努力地做到这些。我有一些坏习惯,有时候学习很粗心,把一些会做的题做错。在生活上,也很粗心,有一次早上起床居然穿反了衣服。我吃饭很慢,有的时候还剩饭。我还起床磨蹭,本来应该迅速地穿好衣服,但是,我总是磨磨蹭蹭地,速度很慢。“我打算在这学期里,改掉这些坏习惯。早上起来,迅速地穿好衣服,不拖拉。学习不粗心,仔细完成每一道题。吃饭的时候,要很快的把饭吃完,不剩饭。我要从一点一滴做起,逐渐养成良好习惯。我相信自己一定能成为一名品学兼优的好学生!我打算在这学期里,改掉这些坏习惯。早上起来,迅速地穿好衣服,不拖拉。学习不粗心,仔细完成每一道题。吃饭的时候,要很快的把饭吃完,不剩饭。我要从一点一滴做起,逐渐养成良好习惯。我相信自己一定能成为一名品学兼优的好学生!” 在上幼儿园以前,我什么也不会干,就连穿衣服也是妈妈给我穿好,就要上幼儿园了,这样可不行,妈妈锻炼我要学会自己穿衣服。 有一天,妈妈把衣服摆在我面前,开始让我自己穿。一开始。我又哭又叫就是不穿,还把衣服扔的满地都是,然后坐在地上开始大哭,等了好长时间,妈妈还是不理我,我只好自己乖乖的把衣服穿好, 一出了房间门,妈妈就笑了起来,再看看我的衣服,毛衣和裤子都穿反了,我赶紧回房间又重新穿了一遍,这次穿好了,拿起外套,可是外套的扣子又扣不上了,扣子可调皮了,好像故意和我作对,我把扣子往扣眼——人类邪恶的根源;爱情——幸福和光明的源泉。我一直在这些思想的舞台上徘徊。突然我发现两个身影从我面前经过,坐在不远的草地上。这是一对从农田那边走过来的青年男女。农田那边有农民的茅舍。在一阵令人伤心的沉默之后,随着一声长叹,我听见从一个肺痨病人的嘴里说出了这样的话:“亲爱的!擦干你的眼泪,至高无上的爱情已经打开了我们的眼界,使我们成了它的崇拜者。是它,
如你有好的文章想和大家分享欢迎投稿,直接向我投递文章链接即可
最后,国庆福利来了,我们的知识星球已达到1000人了,之前说过到达1000人时将大大幅涨价到169元,为了反馈大家对我们的关注和厚爱,国庆之际特此维持现价99元,国庆天后将涨到169元,欢迎大家加入我们的知识星球,更多星球信息参见:
微信扫描或者点击上方二维码领取Android\Python\AI\Java等高级进阶资源
更多学习资料点击下面的“阅读原文”获取