转: 加快Android编译速度
转: http://timeszoro.xyz/2015/11/25/%E5%8A%A0%E5%BF%ABandroid%E7%BC%96%E8%AF%91%E9%80%9F%E5%BA%A6/
加快Android编译速度
对于Android开发者而言,随着工程不断的壮大,Android项目的编译时间也逐渐变长,即便是有时候添加一行代码也需要等待好久才能看见期待的效果。之前加快Android编译的工具相对较少,其中最具有代表性的开源项目当属FaceBook的Buck和 mmin18的LayoutCast,除此之外还有JRebel 和 Jimulabs。不过前两天google宣布推出Instant Run加快Android 编译速度,相信对其他的工具来说都是一次冲击,这也是写这篇文章的动机。
相对于Buck而言,LayoutCast显得更轻量一些,对项目的侵入性较弱。今年8月份的时候,花了一个星期左右的时间才完成公司的代码的适配,对于一些繁重的项目而言,Buck带来的好处是显而易见的,但是适配过程中的坑也是很多的。Instant Run 对项目的侵入性其实也是比较大的,但是这些都不需要用户去操作、配置,所以看起来和LayoutCast一样属于轻量型的。
时间去哪了?
Android程序编译大致过程如图所示,详细的过程可以参考gradle 中的tasks。

那么为什么我们每次编译都需要等待那么久?事实上我们我们可以gradle中添加TaskExecutionListener来监听gradle脚本中每个task的执行时间。
1 |
class TimingsListener implements TaskExecutionListener, BuildListener { |
执行脚本可以发现主要的费时在dex(包含preDex)以及install这两个步骤。BUCK和LayoutCast的主要工作也是集中于这些费时的步骤上面。
如何加快?
开发过程中对项目的改动一般分为Java文件的修改以及资源文件的修改,这些修改都会涉及到上述的几个费时步骤,这也就是为什么即便我们修改一行代码也需要编译很久。
1、Java文件修改
通常,修改的.java文件会先经过javac操作生成.class文件。而后与其他的.class文件经过dx生成.dex文件。经过dx的操作很费时,针对这种情况,BUCK、LayoutCast和Instant Run采用了两种方法来解决。
BUCK
BUCK建立了一套完善的依赖规则以及细化的缓存系统来缩减编译时间,并通过使用三方的dex merege工具将.dex文件合并的时间复杂度从O(N^2)降到O(NlgN)。

如图所示,当修改A.java文件时,只涉及到相应的dx操作以及dex merge操作(红色部分),这样就大大的缩减了dx的操作时间。BUCK在依赖规则上狠下功夫推出了ABI,更是进一步的减少了不必要的操作。
LayoutCast
LayoutCast的实现同很多插件的实现原理差不多,具体分析如下:
在ClassLoader查找类的时候会先去调用BaseDexClassLoader类中的findClass方法。
1 |
//----dalvik/system/BaseDexClassLoader.java |
随后在DexPathList类中根据dexElements来查找相应的class。
1 |
//----dalvik/system/DexPathList.java |
其中dexElements代表着不同dex文件。
1 |
/** list of dex/resource (class path) elements */ |
也就是说,在ClassLoader加载类的时候会去按照dexElements中dex文件的顺序依次查找,如下图所示,在1.dex中查找到了A类,那么就不会再从后面的dex文件中继续查找了。

LayoutCast就是利用这样的原理,将修改的Java文件生成dex文件,并将此dex文件利用反射的方式插入到dexElements数组的前面。当然,从Java到dex的过程需要额外的查找各种依赖包之类的工作,这部分工作在cast.py中实现。
这种方式的实现在ART下是没有问题的,但是在Dalvik中就会出现IllegalAccessError的问题
1 |
java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation |
具体的原因以及解决方案可以参考Bugly的文章
Install Run
Install Run 同样也是生成新的增量dex,但是新增dex中的类和原来的类名有区别。比如说,在修改Hello.java类之后,会生成包含Hello$overide类的dex文件。
那么,这个新增的dex文件中Hello$Override类是如何被调用的?
我们先看看原来的Hello.java文件经过Instant Run 编译前后的区别:
编译前的hello.java文件
1 |
public String name(String str) { |
经过Instant Run之后的
1 |
---compiled Hello.java |
可以看出,如果$change存在的话,就会调用$change中相应的函数,那么我们只需要通过反射将Hello.java中$change字段改为修改后的Hello$override的类就Ok了。
这也就是为什么Instant Run并不存在前面说到的IllegalAccessError的问题,并且支持不重启就能看见修改效果的原因。具体可以看看寒江不钓的博客
2、Res修改
Resource文件的修改会涉及到AAPT、ApkBuilder以及最后的Install操作。其中APPT的操作要求比较高,LayoutCast、Instant Run均没有在这部分进行优化,他们的主要工作在于后面的两个操作。其主要的思路在于将修改的后的资源利用aapt打包成新的.ap_文件,并通过反射的方式将原来的资源文件改为修改后的。
LayoutCast
LayoutCast主要做了两件事。
修改LayoutInflater服务
对于下面的用法我们并不陌生:
1 |
LayoutInflater layoutInflater = LayoutInflater.from(context); |
其中LayoutInflater.from的实现是在Context的实现类ContextImp中获取LAYOUT_INFLATER_SERVICE
系统服务
1 |
//---- android/view/LayoutInflater.java |
那么ContextImpl又是如何获取相应的服务的,查看ContextImpl类可以发现,
1 |
//---- android/app/ContextImpl.java |
可以发现调用getSystemService的过程是在SYSTEM_SERVICE_MAP
的表中查找ServiceFetcher
,并返回ServiceFetcher
中的mCachedInstance
。那么只需要将mCachedInstance
替换为自定义的BootInflater并在BootInflater中完成Resource的Overrirde就可以了,如下图所示。

修改Resource
我们知道Activity中的通过调用getResources()
方法来访问资源,这实际上是调用ContextWrapper类中的getResource()
方法
1 |
public Resources getResources(){ |
LayoutCast中就采用替换mBase为自定义的OverrideContext,并在其中将Resource返回为修改后的Resource。
Instant Run
Instant Run 对资源文件的处理和LayoutCast基本类似,但是在细节的处理上有所不同,比如Instant Run 通过对ActivityThread
类中的mPackages
和mResourcePackages
的修改来改变LoadedApk
中mResDir
的值。
1 |
for (String fieldName : new String[] { "mPackages", "mResourcePackages" }) |
资源文件修改的处理相对于Java文件的处理较为复杂,这中间涉及到aapt、attribute唯一性 、ID值一致等问题都增加了资源文件处理的难度。
总结
总的来说,每种方法都有自己的特色,BUCK依赖于自己强大的缓存和依赖管理系统。而LayoutCast和Instant Run相对而言采用了更灵巧的方法。相对而言,Instant Run 凭借着天然的优势(和升级后的gradle结合),可以胜LayoutCast一筹,但是LayoutCast这种想法的提出还是很赞的。目前增量的编译集中在Java文件的修改,对于Res的修改暂时好像还不支持,这在后续应该会有提升吧。
转: 加快Android编译速度的更多相关文章
- 提升Android编译速度
Android codebase都非常大.编译一次都须要花非常多时间.假设是preloader/lk/bootimage还好,可是Android的话都是非常久. 实际上这个编译时间还是能够进一步缩短! ...
- 使用ant优化android项目编译速度,提高工作效率
1.Android项目编译周期长,编译项目命令取消困难 2.在进行Android项目的编译的同时,Eclipse锁定工作区不能进行修改操作 3.在只进行资源文件的修改时,Eclipse对资源文件的修改 ...
- 加快Android Studio的编译速度
从Eclipse切换到Android Studio后,感觉Android Studio的build速度比Eclipse慢很多,以下几个方法可以提高Android Studio的编译速度 使用Gradl ...
- 加快android studio 编译速度
工程build一次太慢 经过各种搜索 整合以下 仅供参考 1.在下列目录中新建 gradle.properties 文件 /home//.gradle/ (Linux) /Users//.gradl ...
- 加速 Android Studio 的编译速度 2.2
Android studio 2.2 当中有一项新的功能:Dex In Process. 这项功能可以动态的加快编译速度,以及提高Instant Run 的效率. 那么怎么来使用这项新功能呢?你只需要 ...
- Android Studio优化编译速度
随着Android Studio的不断完善,其安卓开发者阵营也基本从Eclipse转移到了Android Studio,毕竟Android Studio是谷歌亲力亲为开发的官方开发软件.不过其最重要的 ...
- Android提升Gradle编译速度或减少Gradle编译时间.md
目录 Android如何提升Gradle编译速度或减少Gradle编译时间 最终优化方案 优化效果比对 将所有项目源码,各种缓存临时目录都移动到高性能SSD磁盘上 gradle.properties ...
- 加快XCode的编译链接速度(200%+)—XCode编译速度慢的解决方案
最近在开发一个大项目的时候遇到一个很头疼的问题,由于项目代码较多,每次都要编译链接1分钟左右,调试的时候很浪费时间,于是研究了一下如何提高编译链接的速度,在这里分享给大家. 提升编译链接的速度主要有以 ...
- 加快XCode编译链接速度(200%+)—XCode编译慢液
最近在一个大型项目的开发的时候遇到一个很头疼的问题,由于该项目的代码更,每次建立联系1纪要.浪费时间调试.因此,一些研究如何提高编译链接速度,这里给大家分享. 为了提高编译和链接的是以下三种方式的速度 ...
随机推荐
- ARK登录信息
101,389B,382:仙境353:中心岛380:畸变404:孤岛371:焦土487:灭绝 eaglexmw:389b[65493013] : 初级畸变,高级飞升,TEK全解(有380权限),黑鬼, ...
- HDU 4417.Super Mario-可持久化线段树(无修改区间小于等于H的数的个数)
Super Mario Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- web服务器解析漏洞总结(转)
转:http://www.secpulse.com/archives/3750.html 解析漏洞总结 2015 /1/27 22:09 一.IIS 5.x/6.0解析漏洞 IIS 6.0解析利用方法 ...
- 《深入理解Android2》读书笔记(三)
接上篇<深入理解Android2>读书笔记(二) PackageManagerService PackageManagerService负责系统中Package的管理,应用程序的安装.卸载 ...
- Luogu P2486 染色(树链剖分+线段树)
题解 不妨采取重链剖分的方式把路径剖成区间,然后用线段树维护,考虑如何合并一个区间 struct Node { int lf, rg, tot; }seg[N << 2]; int col ...
- Flask实战第43天:把图片验证码和短信验证码保存到memcached中
前面我们已经获取到图片验证码和短信验证码,但是我们还没有把它们保存起来.同样的,我们和之前的邮箱验证码一样,保存到memcached中 编辑commom.vews.py .. from utils i ...
- UML对象图、包图
对象图(Object Diagram)显示了一组对象和他们之间的关系.使用对象图阿狸说明数据结构,类图中的类或组件等实例的快照.对象图和类图一样,反应了系统的静态过程,但它是以实际的或原型化为基础来表 ...
- ServletContext (上下文对象)
一.什么是ServletContext ServletContext代表是一个web应用的上下文对象(web应用对象) 里面封装的都是web应用信息 一个ServletContext对应一个应用 二. ...
- BZOJ 3544 [ONTAK2010]Creative Accounting(set)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3544 [题目大意] 找一段区间使得Σai mod m的值最大. [题解] 首先计算前缀 ...
- [ARC097F]Monochrome Cat
题意:一棵树,每个节点是黑色或白色,你可以从任意节点开始进行一些操作并在任意节点结束,如果当前在$x$,那么一次操作可以是:1.走到相邻节点$y$并翻转$y$的颜色,2.翻转$x$的颜色,问把所有节点 ...