Android秒级编译工具Freeline新特性支持!

前言

Freeline最早诞生之初主要是为了支持蚂蚁聚宝的应用架构(mPaaS,插件化架构)的增量编译。

蚂蚁聚宝的Android开发团队使用Windows/Linux/Mac的均有,在高配mbp上,改一次代码并编译-安装-运行,大概需要1min+。在非SSD的Windows上,耗时则大于5min。完整地编译整个工程并安装,mbp上需要大于5min,而Windows上,甚至可以达到20min+。编译耗时严重影响了整个团队的开发效率,这也催发了Freeline原型的诞生。

在具体展开介绍之前,先来看下Freeline的开发历程以及社区中几个常见的加速构建方案的对比。

Freeline发展

  • 2015年底,聚宝内部诞生IncrementalBuilder
  • 2016.02 正式命名Freeline,对内发布,支持mPaaS框架
  • 2016.05 阿里内部开源,支持Gradle
  • 2016.08 正式对外发布

开源后主要还是在提升兼容性与持续开发新功能的阶段,靠用户的自发推广,慢慢地积累了1000+ Star,目前也已经有不少App Store排行前列的应用选择接入Freeline来改善日常开发的体验。

Freeline除了持续提高兼容性之外,也陆续支持了社区中呼声较高的retrolambda以及注解的增量编译,社区中也有第三方开发的Android Studio插件,与日常开发流程更加无缝贴合。

加速构建方案对比

先来对比一下社区中常见的几款加速编译的方案:

Instant-Run

  • Pros

    • Google官方支持的增量编译方案,随着Android Studio的迭代持续优化
    • 相对来说更加稳定,零配置,基本无侵入性影响
    • 几秒内可以完成编译,速度非常快
  • Cons
    • 对于可以修改的地方有局限性,具体可以参考官方文档
    • 除了资源修改之外,修改Java文件会重启整个应用,从Launcher Activity重新进入,如果是在开发一个层级较深的UI页面的话,使用起来不方便
    • 增量过的代码不支持debug
    • 对于复杂的工程结构支持程度不高
    • 不支持Kotlin

Buck/okbuck

  • Pros

    • Facebook出品的构建工具,支持多种语言/平台的构建
    • okbuck是一个帮助gradle工程快速集成buck的工具,目前转入uber进行维护
    • 多线程并发编译,充分利用缓存,近似增量编译
    • 目前支持了retrolambda与注解(?)
  • Cons
    • 对于有历史的大型工程接入成本较高,需要较高的时间成本
    • 构建过程与gradle不同,所以第一次接入可能会存在不少的问题需要解决
    • 安装apk的时间耗时较久
    • 不支持Kotlin
    • 不支持Windows

JRebel for Android

  • Pros

    • 在Instant Run之前就已经存在的Android平台上的增量编译解决方案,zeroturnround有大量JVM上热部署的实践积累
    • 零配置,只需安装Android Studio插件,立刻可以运行
    • 相比Instant Run支持的范围广,参考链接
    • 支持lambda与部分流行注解库
    • 字节码层面的动态加载,理论上支持几乎所有基于JVM语言,包括Kotlin、Groovy等
  • Cons
    • 收费,价格较高,可以参考链接
    • 只有收费版才能debug,有专门的debug工具
    • crash后需要重新全量编译,单次全量编译、安装的速度非常慢

Freeline

  • Pros

    • 支持大多数场景的增量编译
    • 支持retrolambda与注解
    • 支持so动态替换
    • 支持Windows/Linux/macOS
    • App crash后,仍然可以通过增量编译来修复
    • 大多数情况下增量编译可以在10s内完成
  • Cons
    • 初次接入可能存在一定的问题,需要稍微花点时间来解决
    • 在简单的工程上,与其他构建方案相比,没有明显的优势
    • 不支持删除带id的资源,会报错
    • 不支持Kotlin

LayoutCast也是一个常用的方案,不过对多module的工程支持不足,算是一个增量编译的工具原型,通常都需要改造一下才能应用起来,因此就不加入上面的比较了。

Freeline使用(只需三步即可接入)

以下是命令行版本,以Linux开发环境为基础,Win下替换相关命令即可。

  • 在根目录的build.gradle中添加classpath 'com.antfortune.freeline:gradle:${latest-version}'
  • 在主工程(application工程)的build.gradle中添加apply plugin: 'com.antfortune.freeline'
  • ./gradlew initFreeline -Pmirror:初始化Freeline相关依赖
  • 日常开发:
    • python freeline.py:Freeline会自动切换全量与增量编译模式
    • python freeline.py -f:强制进行全量编译

当然,也可以直接安装第三方插件,在Android Studio里的plugins中,搜索freeline并安装即可,就可以使用快捷键来迅速进行编译开发啦。

Freeline原理

在解析Freeline如何支持Gradle工程的增量编译前,先来回顾一下Freeline的原理。Freeline本质上是一个热补丁方案,将修过过的*.java和资源文件分别打成dex和pack,然后通过socket传输到手机上,在运行期动态加载生效。具体可以阅读Freeline - Android平台上的秒级编译方案。

类似的热补丁方案的开源实现有Nuwa,以及未开源的QQ空间的超级补丁包。蚂蚁聚宝在线上也采用类似的方案来实现热补丁,以及A/B test。

Gradle是如何构建Android工程的?

每个在Android Studio中新建的Android工程,在根目录下都会有个build.gradle文件,定义了buildscript,如下:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.0'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

其中,com.android.tools.build:gradle:2.2.0就是Google官方提供的Gradle plugin,专门用来处理Android工程的构建流程,插件里声明了许多我们经常可以在Gradle Console中看到的task。对于Gradle Task来说,他通常都会有input和output,并且每个task前后都会有依赖。整个编译流程就像工厂流水线一样,从代码源文件开始,逐渐装配,最终生成“产品”apk。我们常见的Gradle编译任务:assemble,其英文本意就是工业上装配的意思。

Android Gradle Plugin本身也是Android开源代码的一部分,可以在线浏览源代码[需自带梯子],目前最新的版本为2.2.0,在线浏览的链接:https://android.googlesource.com/platform/tools/base/+/gradle_2.2.0

了解到以上这些定义之后,我们就可以知道要对Android的构建流程或者其产物做修改,其实就是要去hook这些构建任务,来修改他们的input或者output,从而达到我们想要的目的。

Freeline全量编译流程

Freeline定制了自己的全量和增量的编译流程。当Freeline监测到build.gradle或者AndroidManifest.xml变化了,会自动进入全量编译。

下图是Freeline的全量编译流程图:

全量编译流程拆解:

  • generate-file-stat:生成当前工程java文件和资源文件的修改时间与文件大小的缓存,便于后面进行对比监测文件是否修改
  • read-project-info:根据build.gradle的配置,预先生成项目描述文件,缓存在~/.freeline/cache
  • gradle-full-build-with-freeline:执行gradle命令对工程进行编译
  • clean-all-cache:清除之前freeline编译遗留的所有缓存文件
  • install-apk:安装生成的apk到设备上
  • build-base-res:使用FreelineAapt打出基础资源包
  • generate-pro-info:生成工程的依赖信息并缓存
  • append-file-stat:检查是否有新增的module,如果有的话将新增的module的文件状态添加到generate-file-stat生成的缓存文件中

如何绕过verify校验?

Freeline在代码增量上采用了DexPathList植入dex的方案,已有不少文章有过相关介绍。如何绕过校验防止出现运行期crash有两种方案,一种是编译期植入代码,另一种是hook绕过。第二种方案的实现可以参考这篇文章QFix探索之路——手Q热补丁轻量级方案,Github上也有开源的实现QFix。Freeline目前采用的是第一种方案,在编译期植入另外一个dex的类。

上面已经讲到Gradle的构建流程是由一个个的task按照依赖顺序进行执行的,因此我们只要能够找到相应的task入口去hook所有javac编译生成的class文件与jar文件,并对其做出相应的修改,就可以做到运行期绕过校验。

在Android Gradle Plugin 1.5.0以前,根据是否开启multiDex,插入的task会有所变化,如图所示:

在1.5版本以后,因为引入了Transform API的概念,所以task也有了较大的变化。不仅如此,minSdkVersion是否是5.0以前的,也会影响构建流程的task,原因是Google允许开发者在开发时,通过productFlavor设置最低sdk版本为21,以此来减少编译时间(主要是减少merge dex消耗的时间),具体可以参考这个链接:https://developer.android.com/studio/build/multidex.html#dev-build

因而,在1.5版本以后,Freeline会如图这样影响构建流程:

注意,以上流程为不开启混淆的情况。Freeline目前只支持debug buildType,并且不支持混淆。

对task进行hook之后,我们植入的hackClassesBeforeDex会对每个拿到的class或者jar通过ASM做代码注入。ASM是一个通用的Java字节码操作与分析框架。它可以直接以二进制的形式,直接修改现用的class文件。

Freeline在每个类(不继承Application)的构造函数注入了如下一段代码,其中ClassVerifier这个类来自一个独立的dex。

通过ASM的API,可以很简单地实现类的修改:

题外话,其实利用ASM还可以做非常多的额外的工作,包括各种编译期的代码生成,ASM的原理也让它不需要生成新的java文件再重新编译,而是直接修改已经存在的class文件。

当然,也有很多人会问不熟悉字节码的话,是不是学习的曲线会很大?其实不然,ASM已经提供给你现成的工具jar包,你可以利用这个工具,直接从class文件dump出ASM的代码,官方也提供了相应的问题说明:http://asm.ow2.org/doc/faq.html#Q10

举个简单的例子:

通过命令行工具,我们可以将生成上述Java代码对应的ASM生成代码。

依赖查找

Freeline的增量编译过程中,需要添加完整的依赖路径,这里的依赖就包括了Jar依赖以及资源路径依赖。同样,我们从构建任务入手。

Jar依赖

上文我们提到了Freeline会去hook编译流程,植入代码,实际上在那个步骤中我们就会拿到所有的Jar文件,只要将他们都存入List中,在编译流程结束后存入文件缓存即可,可以轻松地解决Jar依赖查找的问题。

资源依赖

通过查找源码,我们会发现在每个module的构建任务中,会有一个mergeResources的任务。实际上Android在每个module的编译流程中,会将module的资源与module依赖的aar的资源,合并到一起,并打出新的aar或者最终的apk。因此,我们也可以通过hook这个mergeResources的任务,拿到所有的资源依赖路径。

这里也有一种特殊情况需要处理,如果module没有添加任何依赖,那么这个module是不会存在mergeResources这个任务的。但是这个module同时也有可能存在一些编译期生成的资源,比如RenderScript会在编译期生成raw资源,存在build/generated/res/rs这个路径,要注意对这种case进行处理,以免后面出现编译错误。

APT增量编译

Freeline开源后被问及最多的问题是:什么时候能够支持ButterKnife/AndroidAnnotation呢?

使用各类注解库的时候,通常都会需要依赖android-apt这个Gradle插件。android-apt会在编译期对javac编译过程加入相关的APT参数,使得在Gradle的构建过程中能够动态生成代码并加入编译流程。因此,Freeline需要做的就是在全量编译流程中,去获取到APT参数,然后加入到javac的增量编译过程中即可。

android-apt的源码开放在bitbucket上:https://bitbucket.org/hvisser/android-apt 。核心代码不过百行,主要的处理逻辑在于hook编译流程以及APT参数的拼接。

根据android-apt的逻辑,Freeline可以从javac编译任务中提取相关的APT参数,并保存到配置文件中。然后在增量编译的过程中,在javac过程里植入相应的参数即可。

注:在Android Gradle 2.2+开始,Android官方的Gradle插件终于提供了APT支持了。具体可以参考这篇博客。Freeline目前只支持了android-apt插件,后续也会加入对官方APT插件的支持。

retrolambda的增量编译

跟解决APT的增量编译一样,我们也首先来翻下retrolambda的Gradle插件源码。retrolambda的原理是将JDK8编译出来的代码,翻译到低版本的Java字节码,使得开发者可以使用lambda表达式写出能够在Android设备上运行的代码。实际上我们不需要理解具体的工作原理,只需要弄清楚其Gradle插件的执行流程即可。

跟踪一下插件代码的执行流程,我们发现最后进入了RetrolambdaExec.groovy这个类,实际上还是构造了一个可执行命令,并在javac编译流程结束后执行,如图。

因此,我们要做的其实也非常简单,在Freeline的增量编译流程中插入一个retrolambda的任务即可,模仿其构造参数的方法,实现一个python版本的简易插件,具体代码不再展开。

注:Freeline目前还不支持启用jack进行编译。

TODO

Freeline本质上是一个hack方案,所以还是会存在各种潜在的兼容性问题。所以,Freeline接下来还是会持续解决这些兼容性问题。

开源至今,Freeline已有来自BAT、新美大等各大公司的几十款产品接入,从大家的反馈来看,还是非常显著地提高了Android工程师们的开发效率的~

最后,欢迎感兴趣的团队接入使用,如果你也喜欢Freeline的话,欢迎给我们的项目加个star:https://github.com/alibaba/freeline

时间: 2024-10-29 01:03:06

Android秒级编译工具Freeline新特性支持!的相关文章

浅谈蚂蚁聚宝Android秒级编译

本文PPT来自蚂蚁金服高级技术专家栾砚强于10月14日在2016年杭州云栖上发表的演讲,分享主题为<蚂蚁聚宝Android秒级编译介绍--Freeline>. 传统的Android应用构建在Buck.LayoutCast.Instant Run上均存在一些弊端.例如,入侵性强,接入成本高,需要安装全量包,以子任务为单位做增量资源全量替换,没有实现增量,资源不支持新增,没有利用缓存或者并发, 不支持4.x等等.今天,蚂蚁金服高级技术专家栾砚强向我们介绍蚂蚁聚宝Android秒级编译. Freel

Freeline - Android平台上的秒级编译方案

Freeline 技术揭秘 Freeline是什么? Freeline是蚂蚁金服旗下一站式理财平台蚂蚁聚宝团队15年10月在Android平台上的量身定做的一个基于动态替换的编译方案,5月阿里集团内部开源,稳定性方面:完善的基线对齐,进程级别异常隔离机制.性能方面:内部采用了类似Facebook的开源工具buck的多工程多任务并发思想:端口扫描,代码扫描,并发编译,并发dx,并发merge dex等策略,在多核机器上有明显加速效果,另外在class及dex,resources层面作了相应缓存策略

Eclipse 3.2 Java开发工具的新特性

Eclipse是一个流行的针对Java编程的集成开发环境(IDE).它还可以用作编写其他语言(比如C++和Ruby)的环境,合并各种种类工具的框架,以及创建桌面或服务器应用程序的富客户端平台.如今,Eclipse开源社区拥有数十个项目,其范围从商务智能到社会网络等各个方面.Eclipse同时也是管理这些项目的非赢利性组织的名称.(而且,尽管我相当肯定它并非地板蜡,还是存在Eclipse汽车.足球队和口香糖.) Eclipse version 3.2在Eclipse Callisto 发行历史上具

谷歌发布Android 4.2 详解果冻豆新特性

Android 4.2是谷歌新一代移动操作系统,它沿用了4.1版"果冻豆"(Jelly Bean)这一名称,与Android 4.1相似性很高,但仍在细节之后做了一些改进与升级,比较重要的包括:Photo Sphere全景拍照:键盘手势输入:Miracast无线显示共享:手势放大缩小屏幕,以及为盲人用户设计的语音输出和手势模式导航功能等. (谷歌发布Android 4.2 支持全景拍照和手势输入) Android 4.2操作系统的亮点是支持行业标准的WiFi显示共享工具Miracast

Android 4.2 Jelly Bean的新特性

新的系统保留了原来Jelly Bean 名字,整体用户体验并没有太大的改动,不过一些新的功能还是值得大家关注的. Google Now 更新 在新的Google Now 中,它可以索引你邮件里关于机票.订餐.酒店.包裹.入场券等信息,在合适的时间提醒你.不过这些功能需要用户授权,如果你有所担心,那么这些新的功能对你来说就没有任何意义了.同时Google Now也增加了电影.音乐会.股票以及新闻的显示. 键盘手势输入功能 如果你使用过Swype 输入法又或者看过SwiftKey Flow 的视频,

从iOS6看Android 5.0最值得期待新特性

从iOS6看Android 5.0值得期待特性从iOS6看Android 5.0值得期待特性一边谷歌Android 4.0的推广与用户接受度并未大范围开花,一边苹果风生水起的推出着包含200多项更新以及众多中文本地化服务的iOS 6,而且据目前的消息来看或许两家都将二者最新系统的发布日期定在了秋季,看来一场针锋相对的考验看来势不可挡.iOS 6的测试版已经让正式版初见端倪,但是Android 5.0果冻豆还依然悄无声息,在试用iOS 6测试版的同时,我们不如也猜想一下所期待的Android 5.

可能是最早的学习Android N新特性的文章

可能是最早的学习Android N新特性的文章 Google在今天放出了Android N开发者预览版.Android N支持Nexus6及以上的设备.5太子Nexus5不再得到更新. Android N相比于Android 6.0增加了哪些新的功能呢,开发者要关心哪些API呢,通过本篇文章,将一一得到答案. Android N的新特性 分屏多窗口(Multi-Window Support) 还在为看美剧的时候,收到微信消息而在两个应用间来回切换而烦恼吗.在Android N上,再也不用烦恼这个

Android Studio3.0新特性及安装图文教程

Android Studio是Android的官方IDE.它是专为Android而打造,可以加快您的开发速度,帮助您为每款Android设备构建最优应用. 它提供专为Android开发者量身定制的工具,其中包括丰富的代码编辑.调试.测试和性能分析工具. 一.Android Studio3.0新特性 (1).核心IDE更改 我们将基础IDE从IntelliJ 2016.2升级到2017.1.2,在2016.3和 2017.1中增加了许多新功能, 包括参数提示,语义突出显示,搜索中的即时结果等等.

Android中编译工具链的改动----LLVM份量的增加

作者:史宁宁 -----------------------------------------------------------------------------------转载请注明出处---------------------------------------------------------------------------------        最近,Android中的编译工具链发生了改动,这个改动是Android的runtime(也可以说是VM,这两种说法在Google