@get:Optional
abstract val versionName: Property<String?>
//版本号
@get:Input
@get:Optionalabstract
abstract val versionCode: Property<Int?>
//父类NonIncrementalTask的唯一抽象方法,也就是BuildConfig的主要逻辑处理方法
override fun doTaskAction() {
//获取类里面的属性包括一些自定义的属性
val buildConfigData = BuildConfigData.Builder()
.setBuildConfigPackageName(buildConfigPackageName.get())
.apply {
//此处省略了BUILD_TYPE、FLAVOR、DEBUG等属性的获取,思路是一样的
if (hasVersionInfo.get()) {
versionCode.orNull?.let {
addIntField(“VERSION_CODE”, it)
addStringField(“VERSION_NAME”, “${versionName.getOrElse(”“)}”)
}
}
//
val generator: GeneratedCodeFileCreator =
if (bytecodeOutputFile.isPresent) {
//创建一个JVM字节码BuildConfig,Kotlin版本进行了改造
BuildConfigByteCodeGenerator(byteCodeBuildConfigData)
} else {
//创建一个java文件的BuildConfig,java版本的GenerateBuildConfig一直是这种方案
BuildConfigGenerator(sourceCodeBuildConfigData)
}
}
//调用内部实现类,用JavaWriter创建 generator.generate()
}
}
复制代码
可以看到GenerateBuildConfig已经改成了Kotlin,同时其他的系统Task也都变成了Kotlin版本。看来谷歌也是下了血本了。Kotlin的相关知识比如协程、suspend、非阻塞式挂起函数、扩展函数、泛型也会写一些文章欢迎点赞关注,给作者一些动力。言归正常可以看到GenerateBuildConfig继承了NonIncrementalTask,这个父类也是Kotlin版本改造后才有的基本上其他的系统Task也都继承于这个类。主要作用是一个增量编译处理类。内部有一个抽象方法doTaskAction,也就是GenerateBuildConfig里面的主要逻辑实现方法。同时还有个cleanUpTaskOutputs方法在doTaskAction之前调用,主要作用于确保在任务运行之前删除任务输出。
生成Java类的主要逻辑流程:
doTaskAction–>buildConfigData -->BuildConfigGenerator–>JavaWriter
复制代码
生成字节码类的主要逻辑流程:
doTaskAction–>buildConfigData -->BuildConfigByteCodeGenerator–>ClassWriter
复制代码
主要流程拆分
-
生成buildConfigData类,这是一个Builder的设计模式
-
添加一些默认的属性比如:BUILD_TYPE、FLAVOR、DEBUG等
-
isPresent则生成BuildConfigByteCodeGenerator否则生成BuildConfigGenerator
-
如果是BuildConfigGenerator则通过items.get()添加自定义的属性
-
调用generate生成具体实现类内部用JavaWriter or ClassWriter实现
系统其他Task、对应实现类和作用
[](()三、编译耗时检测
[](()1、gradlew命令
对于较大的项目或者实现大量自定义Transfrom-API 项目,可能需要深入了解构建流程才能找到瓶颈。为此,可以剖析 Gradle 执行构建生命周期的每个阶段和每个构建任务所需的时间。
如需生成和查看构建性能剖析报告,请按以下步骤操作:
1、打开项目根目录下的命令行终端。
2、输入以下命令,可以先执行Claen。因为如果某个任务的输入内容(例如源代码)未发生更改,Gradle 就会跳过它。因此输入内容未发生更改的第二个 build 始终会以更快的速度运行,因为任务不会重复运行。在 build 之前运行 clean
任务可以确保您能够剖析完整的构建流程。
//mac
gradlew clean
//window
gradle clean
复制代码
3、执行完Clean后可以根据需要分析的构建环境执行以下命令
//mac
gradlew assembleDebug --profile
//window
gradle assembleDebug --profile
复制代码
4、构建完成后,可以在项目的根目录下的/build/reports/profile/
目录找到对应的html报告
5、可以查看报告中的每个标签页以了解您的构建,例如,Task Execution 标签页显示了 Gradle 执行各个构建任务所花费的时间。这里需要注意的地方是,Summary的task Execution是每个模块累计相加,实际上多个模块都是并行的。
Summary:构建时间概要
Configuration:配置时间
DependencyResolution:依赖解析花费的时间
TaskExecution:每个任务执行的时间
[](()2、自定义Gradle生命周期实现方法
可以看到在每次的运行构建编译后会对每个gradleTask进行耗时的打印,因此可以针对耗时任务严重的Task做针对性的优化处理还可以针对耗时超过一定时间的任务做监控,如果触发了临界值就会做报警处理这样就保证了以后的Task一直处于较低的耗时,因为内容比较多这个监控方案第二章的时候会详细讲解。
其他生命周期的方法以省略,具体代码如下:
import java.util.concurrent.TimeUnitclass
TimingsListener implements TaskExecutionListener, BuildListener {
private long startTime
private timings = []
@Override
void beforeExecute(Task task) {
startTime = System.nanoTime()
}
@Override
void afterExecute(Task task, TaskState taskState) {
def ms = TimeUnit.MILLISECONDS.convert(
System.nanoTime() - startTime, TimeUnit.NANOSECONDS);
timings.add([ms, task.path])
task.project.logger.warn “${task.path} took ${ms}ms”
}
@Override
void buildFinished(BuildResult result) {
println “Task timings:”
for (timing in timings) {
if (timing[0] >= 50) {
printf “%7sms %s\n”, timing
}
}
}
}
gradle.addListener new TimingsListener()
复制代码
[](()四、编译优化常规方案
俗话说的好“预先善其事,必先利其器”、“磨刀不误砍柴工” 、“先谋而后动”等。大致意思那就是先把需要用到的工具进阶升级下才能打怪更加的无伤或者在打怪前先计划好何时动手,何时使用必杀技等。根据以上结论就有了以下几种编译速度的优化方案:
[](()1、使用最新版本工具
谷歌也一直很值开发中的痛楚,同时自己也改造了系统的Gradle Task和出了一些针对构建速度的Studio工具比如:Instant Run、Apply Changes。Instant Run这个技术是基于 Transfrom-API 技术,Transfrom-API 业界好多的热修复框架也是基于这个思想来实现的但是由于诟病太多在 Android Studio 3.5 Instant Run 就被废弃了。后来又出了Apply Changes它依赖的是 Android 8.0 开始虚拟机支持的特殊指令 (JVMTI) 来进行类的替换。这两个工具后面的深度编译速度优化章节会详细的介绍就不再这里陈诉了,回归正题。
几乎每次更新时,Android 工具都会有一定构建方面的优化所以说我们可以把以下工具升级到最新的版本:
-
[Androi 《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》无偿开源 徽信搜索公众号【编程进阶路】 d Studio 和 SDK 工具](()
-
[Android Plugin for Gradle](()
[](()2、Debug环境只编译需要的资源
避免编译不必要的资源
避免编译和打包不测试的资源(例如,其他语言本地化和屏幕密度资源)。为此,您可以仅为“dev”或者“debug”的版本指定一个语言资源和屏幕密度,如下面的示例中所示:
android {
…
productFlavors {
debug {
…
//在debug环境编译时只会处理中文的语言和xxhdpi的资源图片
//这样就减少了打包的第一步AAPT的资源合并的流程,
resConfigs “zh”, “xxhdpi”
}
…
}
}
复制代码
对调试 build 停用 Crashlytics
如果您不需要运行 [Crashlytics 报告]((),请按如下方法停用该插件,以提高调试 build 的构建速度:
android {
…
buildTypes {
debug {
ext.enableCrashlytics = false
}
}
复制代码
禁止自动生成 build ID
如果想要将 Crashlytics 用于调试 build,可以通过阻止 Crashlytics 在每次构建过程中使用唯一 build ID 更新应用资源,提高增量构建的速度。由于此 build ID 存储在清单引用的资源文件中,因此禁止自动生成 build ID 还可以[将 Apply Changes 和 Crashlytics 一起用于](()调试 build。如果需要阻止 Crashlytics 自动更新其 build ID可以配置如下:
android {
…
buildTypes {
debug {
ext.alwaysUpdateBuildId = false
}
}
复制代码
[](()3、版本将图片转换为 WebP
[WebP](() 是一种既可以提供有损压缩(像 JPEG 一样)也可以提供透明度(像 PNG 一样)的图片文件格式,不过与 JPEG 或 PNG 相比,这种格式可以提供更好的压缩。减小图片文件大小可以加快构建速度(无需在构建时进行压缩),尤其是当应用使用大量图片资源时。不过,在解压缩 WebP 图片时,能会注意到设备的 CPU 使用率有小幅上升。通过使用 Android Studio,您可以轻松地将图片转换为 WebP 格式。步骤如下:
-
右键点击某个图片文件或包含一些图片文件的文件夹,然后点击 Convert to WebP。
-
Converting Images to WebP 对话框随即打开。默认设置取决于当前模块的
minSdkVersion
设置。 -
点击 OK 以开始转换。如果要转换多张图片,只需一步即可完成转换操作,并且可以撤消转换操作以便一次性还原已转换的所有图片。
-
如果在上面选择了无损转换,系统会立即进行转换。图片会在原始位置进行转换。如果选择了有损转换,请继续执行下一步。
-
如果您选择了有损转换,并且选择在保存之前查看每张转换后图片的预览效果,那么 Android Studio 会在转换过程中显示每张图片,以便检查转换结果。
-
点击 Finish。图片会在原始位置进行转换。
左侧是原始 JPG 图片,右侧是有损编码 WebP 图片。对话框中显示了原始图片和转换后图片的文件大小。您可以向左或向右拖动滑块以更改质量设置,并能够立即看到编码图片的效果和文件大小。
[](()4、格式停用 PNG
如果无法(或者不想)将 PNG 图像转换为 WebP 格式,仍可以在每次构建应用时停用自动图片压缩,从而提高构建速度。如果使用的是 Android 插件 3.0.0 或更高版本,默认情况下仅针对“调试”构建类型停用 PNG 处理。如需针对其他构建类型停用此优化,请将以下代码添加到 build.gradle 文件中:
android {
…
buildTypes {
debug{
//禁用PNG压缩。
crunchPngs false
}
}
复制代码
[](()5、开启gradle缓存
构建缓存可以存储构建项目时 Android Plugin for Gradle 生成的特定输出(例如,未打包的 AAR 和经过 dex 预处理的远程依赖项)。使用缓存时,干净构建的速度会显著加快,因为构建系统在进行后续构建时可以直接重用这些缓存的文件,而无需重新创建。
#开启gradle缓存
org.gradle.caching=true
android.enableBuildCache=true
复制代码