关于android:Android-增量编译

7次阅读

共计 5650 个字符,预计需要花费 15 分钟才能阅读完成。

增量编译简介

增量编译是绝对全量编译而言的。所谓增量编译,是指当源程序的部分产生变更后进从新编译的工作只限于批改的局部及与之相干局部的内容,而不须要对全副代码进行编译。增量编译对软件开发,尤其是在调试期,能够大大缩短编译工夫,进步编译效率。

而全量编译指的是,当用户源程序被部分批改后从新编译代码会波及全副源代码,并不只限于部分批改及其相干局部。换句话说,无论批改了什么,全量编译都将进行一次全新的残缺的编译,并不基于上一次的编译根底。

一般来说,在软件开发中,全量编译用于版本的构建与公布,比拟消耗工夫和资源。而处于调试阶段的程序,个别都采纳增量编译,这样对于问题的定位和解决都比省时省力。在 Android 开发中,随着工程代码量收缩,编译耗时也越来越长,拖慢了开发效率,因而 Android 官网推出了 Instant Run 和 Apply Changes 等增量更新的计划。

Instant Run 简介

Instant Run 是 Android Studio 2.0 版本推出的一个增量编译性能,应用 Instant Run 性能时,须要在 build.gradle 文件中将 minSdkVersion 设置为 15 或以上时,并且为另外获得最佳性能,能够将 minSdkVersion 设置为 21 或更高。

之前在 Android Studio 3.0 版本,gradle 为 2.14.1 的版本中做过一个测试,编译一个简略的 Demo 我的项目从之前的 10 秒升高到大略 2、3 秒。默认状况下,Instant Run 是敞开的,如果要开启 Instant Run,能够在 Settings 中关上 Instant Run,如需所示。

不过,Android Studio 在 3.5 版本废除了 Instant Run,并应用 HotSwap 代替了 Instant Run,如下图所示。

对于 Instant Run 的一些原理方面的内容,能够参考我之前的文章介绍:深刻了解 Android Instant Run 运行机制。

Apply Changes

在 Android Studio 3.5 及其以上版本,官网提供了 Apply Changes,应用 Apply Changes 时,须要满足以下两个条件:

  • apk 必须是 debug 包;
  • Android 8.0 及以上的手机上运行

当咱们应用 Android Studio 运行我的项目后,会在菜单栏看见 3 个按钮,别离用来管制利用重启,如下图所示。

如上图所示,从左到右的按钮别离示意【Run】、【Apply Changes】和【Apply Code Changes】。

  • Run:将部署所有的变动并重启利用。
  • Apply Changes:将尝试利用变动的资源和代码,并仅重启 Activity 而不须要重启整个利用。
  • Apply Code Changes:将尝试在不重启操作的状况下利用变动的代码,如果只有代码批改,能够应用此按钮来使代码失效。

不过,因为 Apply Changes 仅反对在 Android 8.0 或者更高版本的手机上运行,并且实际操作时在工程中带来的提速成果也不显著。

Freeline

除了官网的计划外,阿里巴巴客户端团队还基于动静替换研发了一款针对 Android 平台的增量编译工具,它能够充分利用缓存文件,在几秒钟内迅速地对代码的改变进行编译并部署到设施上,无效地缩小了日常开发中的大量从新编译与装置的耗时。

性能方面:外部采纳了相似 Facebook 的开源工具 buck 的多工程多任务并发思维:端口扫描,代码扫描,并发编译,并发 dx,并发 merge dex 等策略,在多核机器上有显著减速成果,另外在 class 及 dex,resources 层面作了相应缓存策略,做到真正增量开发,另外引入并优化 buck 的局部减速组件 dx,DexMerger,资源编译方面,深刻革新了 Aapt 资源编译流程,当资源产生扭转时候,秒级实现增量包编译,其中增量包 仅含最小的变更汇合(10Kb~数百 Kb 内),前期也被使用到线上进行资源 / 代码动静替换。相比目前 instant- run,buck,layoutcast 等计划快数倍速度。

不过,Freeline 同样存在着一些不可漠视的问题。首先是不反对 Kotlin,这在 Kotlin 曾经被谷歌官宣为 Android 开发首选语言的明天,是比拟致命的。另外,不反对删除带 id 的资源,否则可能导致资源编译流程出错。

另外一个潜在的问题是,为了确保编译速度,Freeline 是就义了一部分正确性的。例如,在改变私有动态常量的时候,只会编译对应的类文件,而援用到该常量的其余类,并不会参加编译的。因为常量内联优化的存在,就可能导致这些类在运行时,应用的依然是旧的值,进而呈现改变不失效的问题。

Android 编译打包流程

对于 Android 是如何从源码到安装包的过程,能够参考 Android 官网给的一幅图,次要会经验编译、链接和签名等操作。

下面展现的是 Android 源码编译成安装包的过程,而增量更新的残缺的流程是:【批改代码】->【编译工程】->【装置 APK】->【运行验证】。

对于编译阶段,首先是收集工程中的所有资源文件进行编译,失去资源包以及资源索引类。随后资源索引类会追随工程的所有源代码文件一起被编译为字节码文件,并且字节码文件还须要被进一步编译为 Dex 文件,这样能力被 Android 虚拟机所辨认。

Android 的编译打包会分为以下几个阶段:

  • R 文件的生成 :R 文件记录了每个资源的 ID,之后要参加到 java 的编译过程,R 文件是由 aapt(Android Asset Package Tool)生成。
  • Java(Kotlin)源代码 :咱们晓得有时 app 开发中会跨过程通信,这时能够通过 aidl 的形式定义接口,aidl 工具能够依据 aidl 文件生成对应的 java 文件。之后 R 文件、aidl 相干 java 文件、src 中的 java 文件通过编译生成 .class 文件。
  • dex 生成 :编译后的.class 会又由 dex 工具打包成 dex 文件,其中,Android 增量打包工具 freeline 中用到了 Buck 中提取的 dex 工具,freeline 给出的数据是比原生的 dex 工具快了 40%

资源文件编译

  • aapt(Android Asset Package Tool):aapt 工具对 app 中的资源文件进行打包和归档。

下图残缺的演示了 Android 编译期和运行期的整个步骤。

增量编译原理

Android 增量编译分为代码增量和资源增量,Android 晚期的 Instant Run 计划在资源上并不是增量的,而是把整个利用的资源打成资源包,推送至手机的,因而效率极低。

代码编译

谷歌在反对 multidex 之后(即典型的 65535 问题),Android 打包后会存在多个 dex 文件,运行时加载类时,会从一个 dexList 顺次查找,找到则返回,利用这个原理能够把增量的代码打包成 dex 文件,插入到 dexList 的前边,这样就能够实现类的替换。

对于代码的增量编译须要思考两个次要的问题,即获取改变文件并进行编译、对依赖的代码进行编译。对于代码的增量编译,能够参考 QQ 音乐的增量编译计划:QQ 音乐 Android 编译提速之路

资源编译

资源编译与代码增量是相似的,即先收集被改变的资源,而后进行编译。Android 的资源编译次要应用的是 aapt 或者 aapt2。一般来说当初都是应用 aapt2 来进行资源的打包编译,因为 aapt 工具是不反对单个资源编译的。

aapt2(Android 资源打包工具)是 Android Studio 和 Android Gradle 插件应用它来编译和打包利用的资源构建工具。aapt2 会解析资源、为资源编制索引,并将资源编译为针对 Android 平台进行过优化的二进制格局。

应用 aapt2 进行资源打包编译时,分为编译(compile)与链接(link)两步,在编译阶段,负责将单个或者多个资源编译为二进制文件;链接阶段,则负责合并所有二进制文件再打包。

对于资源的增量编译,能够参考 QQ 音乐的增量编译计划:QQ 音乐 Android 编译提速之路

有赞 Android 编译计划 Savitar

当我的项目通过屡次迭代之后,就会遇到各种各样的问题,而编译慢是每个成熟 Android 团队都无奈回避的问题。在之前有赞批发 Android 团队的技术分享中,整个 Android 我的项目有 25 个业务模块,领有 45W+ 行源代码(Java + Kotlin)以及多个构建 Flavor。小伙伴在进行需要开发时,均匀的增量编译构建工夫达到了两分钟,再加上一些 Gradle 配置与 APK 装置过程,基本上验证一行代码的批改须要近三分钟(MacBook Pro 13-inch, 2016, i5-8G),这样的状况大大降低了团队的开发效率。

在 Savitar 诞生之前,咱们尝试了社区中一些成熟的解决方案,如 BUCK、Freeline、InstantRun 等出名框架。不过调研下来,都或多或少的存在一些问题。

比方 FaceBook 的 BUCK 框架,本身有弱小的构建零碎,通过增量构建缓存机制,能够无效晋升编译的速度,然而其应用和配置过于简单,对于工程的入侵比拟大,且对于一些 Databinding、Kotlin 等 Android 的个性反对还有欠缺。

其次是阿里巴巴开源的 Freeline,Freeline 以其极快的部署速度闻名,但对咱们来说致命毛病是不反对 Kotlin。

InstantRun 是 Google 举荐的减速形式,领有最全面的支持性,但因为咱们是多过程的工程,并且 InstantRun 在编译时的一些筹备 Task 也会耗费一些工夫,在实际过程中发现减速并不显著。

计划实现

Savitar 是有赞 Android 团队增量编译提效计划,它可能无效缩小模块批改编译工夫,蕴含配套 IDE 插件,使用方便,具备如下一些显著的特点。

    • 反对 Java、Kotlin 的增量编译
    • 反对 layout、values、assets、images 等资源文件的增量编译
    • 提供 GUI 界面插件
    • 基于 Git 调试、多分支治理,能够依据理论状况更换分支

    结构设计

    如图所示,Savitar 整体分成四个局部:

    • GUI 插件局部:面向使用者的 GUI 界面,外部蕴含了可运行 Jar(以下简称 Runner)的自动更新、各种查看工作、编译脚本调用执行
    • Runner 局部:一个 Jar 包,蕴含 Savitar 外围逻辑代码,实现批改获取、脚本生成、编译执行等工作
    • 工程反对局部:一个 Gradle 插件,实现对工程信息的获取和产物加载代码的插入
    • 内部依赖局部:实现整个流程所须要的内部依赖程序

    下图演示了 Savitar 从代码批改到实现批改产物加载运行的残缺过程。

    能够发现,从代码批改到实现批改产物次要经验了以下几个步骤:

    1. 获取改变信息:获取代码和资源批改,是整个过程的前提
    2. 获取工程信息:获取以后工程的依赖信息,目录信息和 Git 信息,为后续编译做筹备
    3. 编译生成产物:进行代码、资源编译,生成 Dex 产物和 Apk 产物
    4. 重启加载产物:实现对编译产物的加载运行,实现整个减速过程

    对于 Savitar 是如何从获取改变信息到实现加载,能够参考 Android 增量编译提效计划 Savitar 的具体介绍。

    如何应用

    为了不便开发者应用 Savitar 实现增量更新,Savitar 开发了一款 IDE 插件,只须要一键触发就能够实现整个编译打包流程。首先,关上 Android Studio,而后顺次抉择【Preference】->【Plugin】搜寻 Savitar 并装置,如下图所示。

    装置实现后重启 IDE,而后在 Android Studio 中工具栏就会呈现 Savitar 的图标,如下图所示。

    点击图标后,能够在 Savitar Window 看到工具编译、打包、推送整个运行过程,蕴含错误信息,如下图所示。

    常见文件解答

    是否反对,如何反对 Kotlinx?

    如下所示,有上面一段代码:

    import kotlinx.android.synthetic.main.activity_main.*
    
    class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            buttonCpu.setOnClickListener {... // 点击事件}
        }
    }

    对于下面的代码,想必应用过 Kotlin 的 Android 同学并不会生疏,利用 Kotlinx 个性,能够在 .kt 代码中应用 Xml 中定义过组件 Id 间接获取 View 实例进行操作,极大缩小 UI 开发成本。

    然而下面代码中的 import 并不是一个一般的模式,这样的语法如果间接应用规范 kotlinc 进行编译,会呈现找不到 import 谬误。此时须要借助到 Kotlin 编译器插件,在 Kotlin 编译时传入 Kotlinx 对应插件的 Jar 地址和参数,就能够实现蕴含 Kotlinx 语法的文件编译。

    sh kotlinc  
    -Xplugin=lib/android-extensions-compiler.jar
    -P plugin:org.jetbrains.kotlin.android:package=${package_name}
    -P plugin:org.jetbrains.kotlin.android:variant='${flovar};${resource_package}'

    文档参考 Kotlin 编译器插件

    Kotlinc 环境变量

    在应用 Android Studio 开发过程中,Kotlin 编译所需的依赖包都是由 IDE 主动治理,然而 Savitar 是应用 Shell 实现,这样的状况上面就须要关怀这个编译工具的问题了。咱们将获取 Kotlin 编译依赖的逻辑放在 Savitar 运行环境检测逻辑中,在检测到没有依赖包的状况下会主动从内网服务器下载对应版本的库,实现 Kotlin 代码编译。

    参考:
    QQ 音乐 Android 编译提速之路
    Android 增量编译提效计划 Savitar

    正文完
     0