天天看点

Freeline:极速编译方案的开源之路

在”阿里开源项目最佳实践“上,蚂蚁金服客户端开发工程师黄咏分享了freeline整个的开源历程和变化,他从不同的角度讲述了freeline整个技术底层的原理,以及编译加速方案的对比,并分享了freeline整个开源以来的收获和体会。

以下内容根据现场分享和幻灯片整理而成。

freeline是非常快速的编译工具,其诞生主要为了迎合现在工程的需要,实现更好的动态化。freeline最早诞生之初主要是为了支持蚂蚁聚宝的应用架构(mpaas,插件化架构)的增量编译,它于2016年8月在alibaba github上开源,目前为止已经累计了3244个star,目前是alibaba github下排行前十的开源项目,已有上千款应用接入使用freeline,可能是东半球用户最多的第三方编译工具。

为什么选择freeline?

目前,市面上类似freeline的工具有很多,如google官方的instant run、facebook-buck、jrebel for android等,那么freeline和这些工具相比究竟有什么优势呢?下面来一探究竟。

<b>instant run</b>

<b> </b>

Freeline:极速编译方案的开源之路

首先介绍一下google官方的instant run,其优点是android studio随身携带,下载之后,默认打开全部功能,零配置,相对其他方案最为稳定,基本无侵入性影响。instant run可以在1s或2s之内完成所有的编译效果。它的缺点是:首先对增量编译的支持有局限性,无法使所有的代码修改都支持增量编译,尤其是跨模块修改。比如现在一些大型的android工程,不会是单个模块,而是数十个以上,如果工程师跨越了2个或3个module来修改,同时进行编译,一般来说instant run只进行全量编译,因为目前不支持这样的修改情况;其次,修改java文件会对整个application进行重启,比如说要修改一个页面,可能是4-5个层级,进入一个较深的层级后,使用instant run就有可能使app回到最初始的页面,又要重新花4-5个步骤,进入到深层级;另外,instant run在asm植入时,植入一段自己的代码,当增量修改之后,无法进行debug,带来调试困难;instant run无法支持复杂的工程结构(主要指非常复杂,十几个模块以上的工程),也不支持kotlin与jack。

<b>facebook–buck/uber-</b><b>o</b><b>k</b><b>b</b><b>uck</b>

<b></b>

Freeline:极速编译方案的开源之路

 

facebook-buck是facebook出品的,其内部统一使用的构建系统。facebook整个后端的服务端工程包括ios工程或安卓工程,基本上都统一由buck作为构建系统来统一构建,但使用buck要做一些额外的系统配置,这对于几乎没有接触过buck的工程师来说,门槛相对较高。okbuck是一个帮助gradle工程快速集成buck的开源工具,目前由uber维护。buck一个核心的出发点是多线程开发编译,充分利用缓存,buck通过对一个模块拆分,拆分成各种小module,近似达到增量编译效果、加速效果。而且其效果非常好,一般十几秒就可以完成编译。并且,buck也已经支持目前非常流行的retrolambda表达式的插件。

当然,buck也有其局限性。对于有历史包袱的大型工程接入,想要接触buck,需要较高的成本。构建过程与gradle不同,即使是在okbuck帮助的情况下,很多gradle的东西都需要工程师做专门的适配,才能在buck上面使用,有较高的学习曲线(国内基本没有太多相关资料),也无法迅速用上社区最新的技术。而且,buck需要重新安装apk,在安卓7.0以前,一个30m左右的apk安装时间甚至可以达到30s以上,虽然编译过程可能就十几秒,但加上整个安装过程,包括重新进入页面的过程,可能需要1分钟的时间等待编译重新开始与调试。buck不支持windows。一些国内的工程师会有所顾忌,没有办法迅速接入;它不支持kotlin,这也是一些使用新技术公司所担忧的。

<b>jrebel for android</b>

Freeline:极速编译方案的开源之路

jrebel是一家来自国外开发公司zeroround的开发工具,是比instant run还早的增量编译工具。zeroround公司最早做jvm热部署,支持spring以及ssh框架,有大量的实践积累,做了很多优化与性能。如果要找一个instant run的替代品,jrebel是一个很好的选择。它是一个几乎零配置的工程,只需要安装一个插件,即可立刻运行,zeroround公司在这方面花了非常大的精力,支持retrolambda与大量流行的android开发组件库。原理为字节码层面的动态加载,所以它理论上支持kotlin、groovy等各种基于jvm的语言。

该工具同样有一些缺陷,最大的问题是收费,且价格不菲,不过现在可以免费试用一年。且它不支持databinding,只有收费版才能debug,而且由于热部署功能以及基于字节码层面的动态加载,导致没有办法使用ide集成的debug插件,必须使用jrebel for android单独提供的插件进行debug功能。还有,crash后需要重新全量编译,单次全量编译、安装的速度非常慢。它不支持jack。

freeline

那么freeline与这几种工具相比,具体有哪些优势呢?

Freeline:极速编译方案的开源之路

目前来说freeline已经支持了绝大多数场景的增量编译和java文件的修改,这一点比instant run范围广。目前来说支持retrolambda与apt,部分支持databinding,因为本身开发不是使用databinding,所以只能说在自测的范围内已经支持了databinding绝大部分的feature。但是可能支持的不全,且它也处于持续的迭代中,这也是在后期迭代时注意的一些地方;另一个优点是支持so文件的动态替换,尤其是一些地图应用或出行应用,对于整个开发调试会非常有用,这也是freeline相当于目前几个开发工具不一样的地方;同时,freeline的增量资源是真正的增量资源,大小可以达到几十k,甚至几百k ,i/o很小,如果仔细看instant run打包的资源包,整个i/o传输会使用很长时间。比如说一个十几m的资源包,在不同的usb环境下,因为freeline真正做到资源包达到了增量的级别,所以会使传输速度达到7秒或8秒,整个增量传输非常快。它支持windows/linux/mac,从而覆盖了更广泛的用户,做的比jrebel好的一点就是appcrash后,仍然能够进行增量编译来修复,基本上不需要重新安装,因此,重新把代码修改好,运行freeline插件,就可以立刻完成整个增量的修复。

freeline也存在不足的地方,因为在做适配时,更多针对复杂的、十几个模块以上的工程,所以对于单module工程,基本上没有优势,不如直接就使用instant run。其次是hack方案的本质导致了存在无法避免的兼容性问题。另外,它不支持删除带id的资源,所以资源id删除时,可能需要重新运行freeline进行全量编译。它也不支持kotlin与jack。

freeline的原理

Freeline:极速编译方案的开源之路

freeline的本质是基于gradle构建系统上的一套hack解决方案。去年是热替换爆发的年度,而freeline其实是热替换方案在编译时的实际运用,实现实时热替换,即实现了整个增量编译的效果。hack方案本身就存在兼容性问题,从理念上,它充分利用多核性能,多级缓存,尽可能减少不必要的编译步骤。从上图可简单分析freeline过程,除了主进程,还会单独运行freeline进程,还有socket长链,通过freeline进程与主进程间的通信来完成整个patch过程,实现热替换,这也是其运行模型。

<b>freeline全量编译</b>

Freeline:极速编译方案的开源之路

上图是freeline整个全量编译的流程。从图中可以看出,中间有一个过程是gradle-full-build-with-freeline,freeline在整个gardle全量编译的过程中植入hack,在hack过程中收集了module之间的依赖关系,包括依赖文件存放在哪里,整个过程结束之后,还会进行build-base-res过程。如果只是看gradle的resource编译过程,gradle会将所有的resource进行merge,这就会导致在resource编译的时候,没有办法将compile出来的resource资源进一步加工编译,所以freeline在此基础上,进行build-base-res,进行全量资源包的构建,为后面的增量资源做准备。通过该图可以清楚看到相应的并发及依赖关系。

<b>freeline增量编译</b>

Freeline:极速编译方案的开源之路

接下来看freeline增量编译过程。从右图可以看出,中间的流水线下来之后,整个并发过程在多模块编译,能够做到最小粒度的编译单元。如果对比facebook的buck,会发现buck 的编译,整个构建单元基于module,这就意味着如果整个工程的module没有做好,buck不会达到速度提升十几倍的编译效果。所以如果不使用freeline而使用buck,就会对整个工程的架构模式进行深度拆分,拆分成多个比较细小的模块,freeline尽可能dex merge。dx过程非常耗时,gradle在原本的编译过程中,所有的module编译跟class编译是合并的过程,为了进行更好的加速效果,省掉了很多不必要的步骤,将所有的资源编译统一,单独做一个统一的增量资源编译。像整个databinding,基于新的增量过程,生成新的java文件。在资源编译过程中,动态修改整个r文件,让新增的资源id能够进入最新的id,传递给后面编译过程中的java module,之后再做一个merge-dex,来实现增量编译,最终完成sync同步,实现资源编译。

整个过程中也有一些缺陷,如果依靠全量生成classmap,就没有办法在增量过程中实现。因为在增量过程中,拿不到全量的class依赖,所以这是freeline不能支持的一个地方。如果要让路由组件支持freeline编译,需要对路由资源进行单独的拓展开发,让freeline作为调用,才能实现整个freeline的拓展实现。

未来规划

接下来谈一下freeline未来的规划进展。首先关注android gradle plugin的最新动态:

Freeline:极速编译方案的开源之路

去年google推出了android gradle plugin2.2.0版本时说,如果将之前版本中的instant run关闭的话,那这个版本的instant run是值得打开的。因为这个版本对其做了非常大的优化和改进,包括对多module工程的支持,得到了比较高的提升,但编译速度不是特别理想。从上表的对比可以看出,针对于100个module的工程进行的测试,2.2.0版本在configuration阶段接近2min多,仅仅是改变一个java文件也需要这么久,如果使用freeline没有那么大的耗时。因为如果没有工程的变更,configuration等过程可能就全部跳过了。google官方推出的2.3版本,能够缩小到9s,2.5版本里面号称可以做到2.5s的configuration时间,而且是6.4s之内完成一行java文件的变更,立刻生效,这是非常大的性能改进,所以这也是freeline未来继续跟进关注的技术进展,来看是否可以回馈到freeline的开发过程当中,实现更好的开发过程。

Freeline:极速编译方案的开源之路

freeline同时也会跟进google的aapt2的进展。整个看lsp源码会发现,google很多源码其实是采用了aapt2编译,aapt2真正做到了类似于c++中的模块化编译,单元编译,它将每个module自带的资源编译成一个单独的resource包,最终将所有编译出来的resource包通过一个link命令,编译成最终的apk。这其实预示着,如果接触了aapt2的工程,对于有上百个module的工程,如果只有一个module变更,只需要使用aapt2编译单一的module,然后对这100个module使用link命令,就可以实现几乎是一个增量的效果。因为编译的耗时过程只进行一次就可以达到新的资源包,这是目前aapt2整个技术的实现。aapt2在整个6.0或7.0的源码里面,做了一个很大的变化。我们目前已经实现了使用aapt2打包资源包的功能,之后也会看整个aapt2是否值得我们去接入实现。

Freeline:极速编译方案的开源之路

freeline后期也会对最近比较流行的kotlin、jack等做跟进,希望未来可以对这两个工具做更好的支持,社区开发等很多新的项目都在使用kotlin,包括阿里内网。jack也是google前几年推出的。希望freeline在未来的迭代中能够支持这两个编译工具。

如何集成?

Freeline:极速编译方案的开源之路

现在freeline的集成非常简单,直接搜索freeline,就会有一个插件,一键安装后就会自动配置集成整个环境。如果工程比较复杂,有几十个module,需要特殊配置,也可以到官网上参考文档。freeline以后也会继续向一键集成与无缝集成发展。

freeline的开源历程

最后介绍一下freeline的整个开源历程:

(1)freeline始于2015年10月,最初是一个3000多行的python文件,仅支持基于maven构建的mpsss框架工程,最长可达30分钟的编译时间,后来缩短为10s增量编译,工程效能达到质的飞跃。

(2)后来在2016年5月进行重构,能支gradle构建的android工程,并在阿里集团内部开源。紧接着在2016年8月实现开源,当天登上了github trending总榜。

(3)在2016年12月,freeline又实现了发展,获得多个技术公众号的推荐与转载,连续一个月出现在github java trending榜单上,社区内开始有多篇在讨论freeline的技术文章。我们得到了apt、retrolambda与databinding的支持,并进行持续的兼容性提升,推出了ide插件,不断满足社区的需求。

继续阅读