内容简介:作者:版权声明:转载请注明出处。一直以来,Android 项目在构建速度是一大槽点,随着Android Studio 3.0 的大版本的升级使得多Module工程的构建速度加快很多。这主要依赖于 Android Plugin for Gradle 插件版本的升级,因此部分 API 发生了较大变化。本文主要是记录整个迁移过程以及聊一些常用的优化构建速度的建议,以供参考。
作者: solart
版权声明:转载请注明出处。
一直以来,Android 项目在构建速度是一大槽点,随着Android Studio 3.0 的大版本的升级使得多Module工程的构建速度加快很多。这主要依赖于 Android Plugin for Gradle 插件版本的升级,因此部分 API 发生了较大变化。本文主要是记录整个迁移过程以及聊一些常用的优化构建速度的建议,以供参考。
android studio 3.1.3; gradle-4.4; gradle plugin 3.1.3
1、Android SDK 构建系统
在正式讲述升级迁移之前,大家应该熟悉一下 Android SDK 的构建系统,Android 构建系统编译应用资源和源代码,然后将它们打包成可供开发人员测试、部署、签署和分发的 APK。Android Studio 使用 Gradle 这一高级构建 工具 包来自动化执行和管理构建流程,同时也允许开发人员定义灵活的自定义构建配置。每个构建配置均可自行定义一组代码和资源,同时对所有应用版本共有的部分加以重复利用。Android Plugin for Gradle 与这个构建工具包协作,共同提供专用于构建和测试 Android 应用的流程和可配置设置。
1.1 构建流程
如上图所示,典型 Android 应用模块的构建流程通常依循下列步骤:
-
编译器将我们的源代码转换成 DEX(Dalvik Executable) 文件(其中包括运行在 Android 设备上的字节码),将所有其他内容转换成已编译资源。
-
APK 打包器将 DEX 文件和已编译资源合并成单个 APK。不过,必须先签署 APK,才能将应用安装并部署到 Android 设备上。
-
APK 打包器使用调试或发布密钥库签署我们的 APK:
-
如果我们构建的是调试版本的应用(即专用于测试和分析的应用),打包器会使用调试密钥库签署我们的应用。Android Studio 自动使用调试密钥库配置新项目。
-
如果我们构建的是打算向外发布的发布版本应用,打包器会使用发布密钥库签署我们的应用。要创建发布密钥库,请阅读 在 Android Studio 中签署我们的应用 。
-
-
在生成最终 APK 之前,打包器会使用 zipalign 工具对应用进行优化,减少其在设备上运行时的内存占用。
构建流程结束时,我们可以获得可用来进行部署、测试的调试 APK,或者可用来发布给外部用户的发布 APK。
1.2 分析构建耗时
在构建项目时,分析构建流程的耗时有助于我们定向的优化构建脚本,我们可以通过以下方式来查看构建耗时:
- 清空构建,相当于初始化,这样可以确保能分析到完整的项目构建过程
./gradlew clean
- 使用下面命令执行构建,并且生成构建过程耗时报告
./gradlew --profile --recompile-scripts --offline --rerun-tasks assembleDebug
--profile --recompile-scripts --offline --rerun-tasks
-
执行完成后在项目
/build/reports/profile/
目录下查看分析报告,报告是网页形式,推荐使用chrome浏览器查看。
2、迁移到Android Plugin for Gradle 3.0.0+
使用 2.x 版本的 Android Plugin for Gradle 升级是必须要经历 的阵痛,相比升级后的构建效率这点代价还是非常值得的。
2.1 更新 Gradle 版本
新 Android 插件要求 Gradle 版本 4.1-rc-1
或更高版本, 如果我们正在使用 Android Studio 3.0 Beta 1 或更高版本打开现有项目,请按照提示操作,将现有项目自动更新到兼容版本的 Gradle。
要手动更新 Gradle,请更新 gradle-wrapper.properties
中的网址,本文以 4.4 的版本为例,如下所示:
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
2.2 应用插件
要手动更新的项目,请包含 Maven 存储区并在项目级 build.gradle
文件中更改插件版本,如下所示:
buildscript { repositories { ... // You need to add the following repository to download the // new plugin. google() } dependencies { classpath 'com.android.tools.build:gradle:3.1.3' ... } }
2.3 使用 FlavorDimensions 进行变体感知依赖项管理
插件 3.0.0+ 包含一项新的依赖机制,这项新的依赖机制官方中文版翻译为:变体感知依赖项管理,对应原文中 variant-aware dependency resolution
这个概念,官方对 FlavorDimensions
的翻译为 风味维度
,个人感觉是蜜汁微笑,可能翻译为 特征维度
更好理解一些,风味是什么鬼?
体会一下英文原文可能会更准确的理解这个拗口的概念。
Android plugin 3.0.0 and higher use variant-aware dependency resolution
to automatically match the variant of the producer to that of the consumer. That is, when publishing a module to another local module, the plugin no longer respects this property when determining which version Of the module to publish to the consumer.
抛开这些概念不说,升级后我们可能会遇到这样的问题:
All flavors must now belong to a named flavor dimension. Learn more at https://d.android.com/r/tools/flavorDimensions-missing-error-message.html
所有的特点现在必须属于一个已命名的特征维度。官网提供的解决方式是:
// Specifies a flavor dimension. flavorDimensions "color" productFlavors { red { // Assigns this product flavor to the 'color' flavor dimension. // This step is optional if you are using only one dimension. dimension "color" ... } blue { dimension "color" ... } }
在defaultConfig里面加入flavorDimensions,定义特征维度(也就是命名特征维度)。 然后在产品风味中指定所属的特征维度。
这里要多说一点的是定义一个特征维度是发挥不出功力的,如果定义两个以上的特征维度,这个概念很玄妙的功力才能体现得出来。
2.4 使用新依赖项配置
Android 插件 3.0.0 正在迁移到这些新依赖项配置。 要迁移我们的项目,只需更新我们的依赖项以使用新配置,而非已弃用配置,如下表中所列。
新配置 | 已弃用配置 | 行为 |
---|---|---|
implementation |
compile |
依赖项在编译时对模块可用,并且仅在运行时对模块的消费者可用。 对于大型多项目构建,使用 implementation 而不是 api / compile 可以 显著缩短构建时间 ,因为它可以减少构建系统需要重新编译的项目量。 大多数应用和测试模块都应使用此配置。 |
api |
compile |
依赖项在编译时对模块可用,并且在编译时和运行时还对模块的消费者可用。 此配置的行为类似于 compile (现在已弃用),一般情况下,我们应当仅在库模块中使用它。 应用模块应使用 implementation ,除非我们想要将其 API 公开给单独的测试模块。 |
compileOnly |
provided |
依赖项仅在编译时对模块可用,并且在编译或运行时对其消费者不可用。 此配置的行为类似于 provided (现在已弃用)。 |
runtimeOnly |
apk |
依赖项仅在运行时对模块及其消费者可用。 此配置的行为类似于 apk (现在已弃用)。 |
就像当前稳定版本的 Android 插件一样,上面的配置对风味或构建类型特定的依赖项可用。 例如,我们可以使用 api
让依赖项对所有变体可用,也可以使用 redApi
让其仅对模块的 red
变体可用。
对于 Gradle Plugin 2.x 版本的时期,一点代码的改动可能会引起整个工程的重新编译,这将是多么悲催,根本原因就是 gradle 压根不知道暴露的接口可以通过一个接一个的依赖传递影响整个工程。之前使用大家都是通过 compile
来管理依赖项,升级后 build.gradle 需要将废弃的 compile 的替换为 implementation
或 api
。
Android Gradle plugin 3.0 依赖项优化解决方案
最新版的Gradle plugin需要我们指出一个module的接口是否对外暴露其依赖lib的接口。基于此,可以让项目构建时,gradle可以判断哪个需要重新编译。因此,老版本的构建关键字compile被废弃了,而是改成了这两个:
- api:同compile作用一样,即认为本module将会泄露其依赖的module的内容。
- implementation:本module不会通过自身的接口向外部暴露其依赖module的内容。
由此,我们可以明确的告诉gradle去重新编译一个module,若是这个使用的module的接口发生变化的话。
dependencies { // 当 foo 接口发生变化时,需要重新编译本 module 以及所有使用本 module 的 module api project(':foo') // 仅当 camera 发生变化时,重新编译本module implementation project(':camera') }
迁移指南
理论上,我们可以将原来工程中的compile完全替换为现在的api,但是一旦依赖发生变化,将会使所有的module重新编译,造成编译过长。
所以更好的方式就是使用implementation来进行依赖,这会大大改善工程的构建时间。只有我们明确要向外部暴露所依赖lib的接口时,才需要使用api依赖,整体来说,会减少很多重新编译。这一点,在官方指南中说的比较明确。
3、优化构建速度
长构建时间会降低开发效率,这就需要通过一些辅助手段以及对项目依赖的梳理来改善这个问题。通常来说,我们只需要经过以上的升级,善用 implementation
已经可以明显提高编译速度,但仍然可能通过以下的手段进一步提升编译速度。
3.1 保持工具处于最新状态
Android 工具几乎在每一次更新中都会获得构建优化和新功能,要充分利用最新优化,请保持以下工具处于最新状态:
####3.2 避免编译不必要的资源
避免编译和打包我们没有测试的资源(例如其他语言本和屏幕密度资源)。为此,我们可以仅为 开发版
指定一个语言资源和屏幕密度,如下面的示例中所示:
android { ... productFlavors { dev { ... // The following configuration limits the "dev" flavor to using // English stringresources and xxhdpi screen-density resources. resConfigs "en", "xxhdpi" } ... } }
3.3 将静态构建配置值与调试构建结合使用
始终为进入 manifest 文件
的属性使用静态/硬编码值,或者为我们的调试构建类型使用资源文件。如果我们的 manifest 文件
或应用资源中的值需要随着每一个构建更新,Instant Run 将无法执行代码交换,它必须构建和安装新的 APK。
例如,在我们每次想要运行更改时,使用动态版本代码、版本名称、资源或任何其他可以更改 manifest 文件的构建逻辑都需要一个完整的 APK 构建 - 即使实际更改仅需要一个热交换,也是如此。如果我们的构建配置需要此类动态属性,那么将其隔离到我们的发布构建变体中并让值对我们的调试构建保持静态,如下面的 build.gradle
文件所示。
int MILLIS_IN_MINUTE = 1000 * 60 int minutesSinceEpoch = System.currentTimeMillis() / MILLIS_IN_MINUTE android { ... defaultConfig { // Making either of these two values dynamic in the defaultConfig will // require a full APK build and reinstallation because the AndroidManifest.xml // must be updated (which is not supported by Instant Run). versionCode 1 versionName "1.0" ... } // The defaultConfig values above are fixed, so your incremental builds don't // need to rebuild the manifest (and therefore the whole APK, slowing build times). // But for release builds, it's okay. So the following script iterates through // all the known variants, finds those that are "release" build types, and // changes those properties to something dynamic. applicationVariants.all { variant -> if (variant.buildType.name == "release") { variant.mergedFlavor.versionCode = minutesSinceEpoch; variant.mergedFlavor.versionName = minutesSinceEpoch + "-" + variant.flavorName; } } }
3.4 使用静态依赖项版本
在 build.gradle
文件中声明依赖项时,我们应当避免在结尾将版本号与加号一起使用,例如 'com.android.tools.build:gradle:2.+'
。使用动态版本号可能导致意外版本更新和难以解析版本差异,并因 Gradle 检查有无更新而减慢构建速度。我们应改为使用静态/硬编码版本号。
3.5 配置 dexOptions 和启用库预 dexing
Android 插件提供了 dexOptions
代码块,因此,我们可以配置以下 DEX 构建属性,这样可能会加快构建速度:
preDexLibraries maxProcessCount javaMaxHeapSize
例如:
android { ... dexOptions { preDexLibraries true maxProcessCount 8 // Instead of setting the heap size for the DEX process, increase Gradle's // heap size to enable dex-in-process. To learm more, read the next section. // javaMaxHeapSize "2048m" } }
我们应当递增这些设置的值来试验它们并通过 分析我们的构建 观察效果。如果我们向进程分配过多的资源,性能可能会下降。
注:如果 Gradle 后台进程已在运行,我们需要先停止此进程,然后使用新设置进行初始化。我们可以选择 View > Tool Windows > Terminal 并输入 gradlew --stop
命令来终止 Gradle 后台程序
3.6 将图像转换成 WebP
WebP 是一种既可以提供有损压缩(像 JPEG 一样)也可以提供透明度(像 PNG 一样)的图片文件格式,不过与 JPEG 或 PNG 相比,这种格式可以提供更好的压缩。降低图片文件大小可以加快构建的速度(无需执行构建时压缩),尤其是在我们的应用使用大量图像资源时,更是如此。不过,在对 WebP 图像进行解压缩时,我们可能会注意到设备的 CPU 使用率有小幅上升。使用 Android Studio 时,我们可以轻松地 将图像转换成 WebP 。
3.7 停用 PNG 处理
如果我们无法(或者不想) 将 PNG 图像转换成 WebP ,仍可以通过在每次构建应用时停用自动图像压缩的方式加快构建速度。要停用此优化,请将以下代码添加到我们的 build.gradle 文件中:
android { ... aaptOptions { cruncherEnabled false } }
在构建发布版本的应用时,我们需要将此属性手动设置为 true
。
希望以上的内容对你能有些帮助。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Firefox浏览器UI 已迁移至使用 Web Components 构建
- 银行核心海量数据无损迁移:TDSQL数据库多源异构迁移方案
- 再无需从头训练迁移学习模型!亚马逊开源迁移学习数据库 Xfer
- Spring Cloud Alibaba迁移指南(一):一行代码从 Hystrix 迁移到 Sentinel
- Spring Cloud Alibaba迁移指南1:零代码从Eureka迁移到Nacos 原 荐
- Spring Cloud Alibaba迁移指南2:一行代码从Hystrix迁移到Sentinel 原 荐
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。