您的位置:网站首页 > 源码环境 > 正文

Android 加快apk的构建速度如何把编译时间从130秒降到17秒

类别:源码环境 日期:2017-11-18 10:22:26 人气: 来源:

  在网上找了一大堆配置参数也没有很明显的效果, 尝试使用instant run效果也不怎么样,然后又尝试使用freeline编译速度还可以但是不稳定,每次失败后全量编译很耗费时间,既然没有好的方案就自己尝试做。

  优化构建速度首先需要找到那些环节导致构建速度这么慢,把下面的代码放进app/build.gradle里把时间花费超过50ms的任务时间打印出来

  从的输出可以发现总的构建时间为100秒左右(的输出不是按照真正的执行顺序输出的),transformClassesWithDexForDebug任务是最慢的耗费了65秒,它就是我们需要重点优化的任务。

  注意编写gradle插件时如果需要使用这些径不要硬编码的方式写死,最好从Android gradle api中去获取径,防止以后发生变化

  第一次全量打包执行完transformClassesWithDexForDebug任务后把生成的dex缓存下来,并且在执行这个任务前对当前所有的java源文件做快照,以后补丁打包的时候通过当前所有的java文件信息和之前的快照做对比,找出变化的java文件进而得到那些class文件发生变化,然后把app/build/intermediates/transforms/jarMerging/debug/jars/1/1f/combined.jar中没有变化的class移除掉,仅把变化class送去生成dex,然后选择一种热修复方案把这个dex当做补丁dex加载进来,有思了后面就是攻克各个技术点。

  从的日志输出证明这个hook点是有效的,在全量打包时执行transform前可以对java源码做快照,执行完以后把dex缓存下来;在补丁打包执行transform之前对比快照移除没有变化的class,执行完以后合并缓存的dex放进dex输出目录

  给源码目录做快照,直接通过文件复制的方式,把所有的srcDir目录下的java文件复制到快照目录下

  通过java文件的长度和上次修改时间两个要素对比可以得知同一个文件是否发生变化,通过快照目录没有某个文件而当前目录有某个文件可以得知增加了文件,通过快照目录有某个文件但是当前目录没有可以得知删除文件(为了效率可以不处理删除,仅造成缓存里有某些用不到的类而已)

  但是java文件编译的时候如果有内部类还会有其它的一些class输出,比如拿R文件做下编译,它的编译输出如下

  注: 这种映射方案如果了混淆就对应不上了,需要解析混淆以后产生的mapping文件才能解决,不过我们也没有必要在混淆的buildType下做开发开发调试,所以暂时可以不做这个事情

  由于patch.dex和缓存下来dex里面有重复的类,当加载引用了重复类的类时会造成pre-verify的错误,具体请参考QQ空间团队写的安卓App热补丁动态修复技术介绍()

  这篇文章详细分析了造成pre-verify错误的原因,文章里给的解决方案是往所有引用被修复类的类中插入一段代码,并且被插入的这段代码所在的类的dex必须是一个单独的dex,这个dex我们事先准备好,叫做stdex-runtime.dex(),它的代码结构是

  动态往class文件中插入代码使用的是asm(),我把做测试的时候找到的一些相关资料和代码都放到了github点我查看(),代码比较多只贴出来一部分,具体请查看ClassInject.groovy()

  对项目的入口Application做代理,并把这个代理类放在第一个dex里面,项目的dex按照顺序放在后面

  第一种方案方案由于必须让maindexlist.txt中大量的类参与了补丁的生成,与之前尽量减少class文件参与dex生成的思想是相冲突的,效率相对于第二个方案比较低,另外一个原因是无法项目的Application中使用了MultiDex;

  根据之前的任务说明生成manifest文件的任务是processDebugManifest,我们只需要在这个任务执行完以后做处理,创建一个实现类为FastdexManifestTask的任务,核心代码如下

  使用下面的代码把这个任务加进去并在processDebugManifest任务执行完毕后执行

  开发完以上功能后做下面的四次打包做时间对比(其实只做一次并不是太准确,做几十次测试取时间的平均值这样才最准)

  通过1和2对比发现,stdex进行第一次全量的打包时的时间花费比不多了10秒左右,这个主要是注入代码和IO上的开销

  通过2和3对比发现,stdex进行补丁打包时的时间花费比不快了60秒左右,这就是期待已久的构建速度啊^_^

  错误信息里的意思是为CustomView的tv1字段,寻找id=2131493007的view时没有找到,先反编译报错的apk,找到报错的地方

  在编译以后怎么就变成数字2131493007了呢,原因是java编译器做了一个性能优化,如果发现源文件引用的是一个带有final描述符的常量,会直接做值copy

  经过分析,当全量打包时 = 2131493007,由于R文件中的id都是final的,所以引用R.id.tv1的地方都被替换为它对应的值2131493007了;

  我的第一个想法是如果在执行完processDebugResources任务后,把R文件里id类的所有字段的final描述符去掉就可以把值copy这个编译优化绕过去 =>

  出错的原因是注解只能引用带final描述符的常量,除此之外switch语句的case也必须引用常量,具体请查看oracle对常量表达式()的说明

  如果采取这个方案,对id的引用就不能使用常量表达式,像ButterKnife这样的view依赖注入的框架都不能用了,性太大这个想法就放弃了

  还有一个思就是修改aapt的源码,使多次打包时名字相同id的值保持一致,这个肯定能解决不过工作量太大了就没有这样做,之后采用了一个折中的办法,就是每次把项目中的所有类(除去第三方库)都参与dex的生成,虽然解决了这个问题但效率一下子降低好多,需要将近40秒才能跑起来还是很慢

  这个问题困扰了好久,直到tinker开源后阅读它的源码TinkerResourceIdTask.groovy时,发现它们也碰到了同样的问题,并有了一个解决方案,我们的场景和tinker场景在这个问题上是一模一样的,直接照抄代码就解决了这个问题,重要的事情说三遍,感谢tinker、感谢tinker、感谢tinker!!

  如果项目中的资源特别多,第一次补丁打包生成public.xml和ids.xml时会占用一些时间,最好做一次缓存,以后的补丁打包直接使用缓存的public.xml和ids.xml**

  解决了的原问题后,接下来继续做优化,有讲到 transformClassesWithMultidexlistForDebug任务的作用,由于采用了隔离Application的做法,所有的项目代码都不在classes.dex中,这个用来分析那些项目中的类需要放在classes.dex的任务就没有意义了,直接禁掉它

  我们公司的项目在使用的过程中,发现补丁打包时虽然只改了一个java类,但构建时执行compileDebugJavaWithJavac任务还是花了13秒

  经过分析由于我们使用了butterknife和tinker,这两个里面都用到了javax.annotation.processing.AbstractProcessor这个接口做代码动态生成,所以项目中的java文件如果很多,挨个扫描所有的java文件并且做操作会造成大量的时间浪费,其实他们每次生成的代码几乎都是一样的,因此如果补丁打包时能把这个任务换成自己的实现,仅编译和快照对比变化的java文件,并把结果输出到app/build/intermediates/classes/debug,覆盖原来的class,能大大提高效率,部分代码如下,详情看FastdexCustomJavacTask.groovy

  既然有缓存,就有缓存过期的问题,假如我们添加了某个第三方库的依赖(依赖关系发生变化),并且在项目代码中引用了它,如果不清除缓存打出来的包运行起来后肯定会包类找不到,所以需要处理这个事情。

  可以在第一次全量打包时,和生成项目源码目录快照的同一个时间点,获取一份当前的依赖列表并保存下来,当补丁打包时在获取一份当前的依赖列表,与之前保存的作对比,如果发生变化就把缓存清除掉

  目前给项目源码目录做快照,使用的是文件copy的方式,如果能仅仅只把需要的信息写在文本文件里,能够在IO上省出一些时间

  apk的安装速度比较慢(尤其是ART下由于在安装时对应用做AOT编译,所以造成安装速度特别慢,具体请参考张邵文大神的文章Android N混合编译与对热补丁影响解析),通过socket把代码补丁和资源补丁发送给app,做到免安装

  阿里中间件承载了世界上最有挑战的场景,应对了一次次双十一的流量洪峰,他们对人才的要求优秀架构师之的方向。

  如果你有想学习的文章直接留言,我会整理征稿。如果你有好的文章想和大家分享欢迎,直接向我投递文章链接即可。

  推荐:

  

0
0
0
0
0
0
0
0
下一篇:没有资料

网友评论 ()条 查看

姓名: 验证码: 看不清楚,换一个

推荐文章更多

热门图文更多

最新文章更多

关于联系我们 - 广告服务 - 友情链接 - 网站地图 - 版权声明 - 人才招聘 - 帮助

CopyRight 2002-2012 技术支持 源码吧 FXT All Rights Reserved

赞助合作: