關于 Gradle 的基本知識,前面章節已經講的差不多了。那么,我們現在來牛刀小試一下,看看 Gradle 有什么用武之地。
我們在將 Android 應用程序打包成 apk 包時,有時會發現整個 build 過程特別長,短則 1、2 分鐘,長則大幾分鐘甚至更長,特別是你要進行調試時,漫長的等待會讓人很焦躁。我們在控制臺可以看到整個打包過程包含很多個 task ,那么到底是哪些 task 的執行花費了大量時間內?
Gradle 提供了很多構建生命周期鉤子函數,我們可以用 TaskExecutionListener 來監聽整個構建過程中 task 的執行:
public interface TaskExecutionListener {
void beforeExecute(Task task);
void afterExecute(Task task, TaskState taskState);
}
在每個 task 執行前先搜集其相關信息,記錄該 task 執行的開始時間等,在 task 執行完成后,記錄其執行結束時間,這樣就能統計出該 task 的執行時長。
接著,我們可以用 BuildListener 來監聽整個構建是否完成,在構建完成后,輸出所有執行過的 task 信息,以及每個 task 的執行時長:
public interface BuildListener {
void buildStarted(Gradle gradle);
void settingsEvaluated(Settings settings);
void projectsLoaded(Gradle gradle);
void projectsEvaluated(Gradle gradle);
void buildFinished(BuildResult buildResult);
}
在 buildFinished 方法中,監聽構建完成以及成功與否。為了方便使用,考慮做成一個 Gradle 插件,關于插件的制作,這里不贅述了,網上有很多關于 Gradle 插件制作的教程。
不多說了,直接上代碼,核心只有一個插件類:
class BuildTimeCostPlugin implements Plugin<Project>{
//用來記錄 task 的執行時長等信息
Map<String, TaskExecTimeInfo> timeCostMap = new HashMap<>()
//用來按順序記錄執行的 task 名稱
List<String> taskPathList = new ArrayList<>()
@Override
void apply(Project project) {
//監聽每個task的執行
project.getGradle().addListener(new TaskExecutionListener() {
@Override
void beforeExecute(Task task) {
//task開始執行之前搜集task的信息
TaskExecTimeInfo timeInfo = new TaskExecTimeInfo()
//記錄開始時間
timeInfo.start = System.currentTimeMillis()
timeInfo.path = task.getPath()
timeCostMap.put(task.getPath(), timeInfo)
taskPathList.add(task.getPath())
}
@Override
void afterExecute(Task task, TaskState taskState) {
//task執行完之后,記錄結束時的時間
TaskExecTimeInfo timeInfo = timeCostMap.get(task.getPath())
timeInfo.end = System.currentTimeMillis()
//計算該 task 的執行時長
timeInfo.total = timeInfo.end - timeInfo.start
}
})
//編譯結束之后:
project.getGradle().addBuildListener(new BuildListener() {
@Override
void buildStarted(Gradle gradle) {
}
@Override
void settingsEvaluated(Settings settings) {
}
@Override
void projectsLoaded(Gradle gradle) {
}
@Override
void projectsEvaluated(Gradle gradle) {
}
@Override
void buildFinished(BuildResult buildResult) {
println "---------------------------------------"
println "---------------------------------------"
println "build finished, now println all task execution time:"
//按 task 執行順序打印出執行時長信息
for (String path : taskPathList) {
long t = timeCostMap.get(path).total
if (t >= timeCostExt.threshold) {
println("${path} [${t}ms]")
}
} println "---------------------------------------"
println "---------------------------------------"
}
})
}
//關于 task 的執行信息
class TaskExecTimeInfo {
long total //task執行總時長
String path
long start //task 執行開始時間
long end //task 結束時間
}
}
接下來,我們創建一個測試工程,使用這個插件,執行 “assembleDebug” 這個構建任務,打一個 debug 的測試包出來,構建完成之后,可以在 Gradle Console 里看到本次構建里所有 task 的執行時長信息:
---------------------------------------
---------------------------------------
build finished, now println all task execution time:
:app:preBuild [1ms]
:app:preDebugBuild [72ms]
:app:compileDebugAidl [8ms]
:app:compileDebugRenderscript [9ms]
:app:checkDebugManifest [2ms]
:app:generateDebugBuildConfig [3ms]
:app:prepareLintJar [2ms]
:app:generateDebugResValues [1ms]
:app:generateDebugResources [0ms]
:app:mergeDebugResources [60ms]
:app:createDebugCompatibleScreenManifests [2ms]
:app:processDebugManifest [9ms]
:app:splitsDiscoveryTaskDebug [1ms]
:app:processDebugResources [15ms]
:app:generateDebugSources [1ms]
:app:javaPreCompileDebug [55ms]
:app:compileDebugJavaWithJavac [2038ms]
:app:compileDebugNdk [23ms]
:app:compileDebugSources [1ms]
:app:mergeDebugShaders [21ms]
:app:compileDebugShaders [12ms]
:app:generateDebugAssets [0ms]
:app:mergeDebugAssets [55ms]
:app:transformClassesWithDexBuilderForDebug [1216ms]
:app:transformDexArchiveWithExternalLibsDexMergerForDebug [1871ms]
:app:transformDexArchiveWithDexMergerForDebug [273ms]
:app:mergeDebugJniLibFolders [10ms]
:app:transformNativeLibsWithMergeJniLibsForDebug [451ms]
:app:transformNativeLibsWithStripDebugSymbolForDebug [9ms]
:app:processDebugJavaRes [10ms]
:app:transformResourcesWithMergeJavaResForDebug [266ms]
:app:validateSigningDebug [12ms]
:app:packageDebug [590ms]
:app:assembleDebug [1ms]
---------------------------------------
---------------------------------------
從上面可以看到 compileDebugJavaWithJavac 這個 task 執行時長為 2038ms,將近有2秒鐘,是這里面執行時長最長的一個。由于 Gradle 支持增量構建,再次構建的時間可能就不一樣了。
上面這個插件,能不能只打印出執行時長超過 1000ms 的任務呢?結果能不能排序后輸出呢?我們繼續做點優化,這就需要前面介紹的 Extension 相關知識了。
先創建一個 Extension 類,代碼如下:
class BuildTimeCostExtension {
//task執行時間超過該值才會統計
int threshold
//是否按照task執行時長進行排序,true-表示從大到小進行排序,false-表示不排序
boolean sorted
void threshold(int threshold) {
this.threshold = threshold
}
void sorted(boolean sorted) {
this.sorted = sorted
}
}
修改插件類,在插件里創建自定義 Extension:
@Override
void apply(Project project) {
//創建一個 Extension,配置輸出結果
final BuildTimeCostExtension timeCostExt = project.getExtensions().create("taskExecTime", BuildTimeCostExtension)
......
......
}
修改 task 執行時長輸出結果的代碼,根據配置來輸出不同的結果:
@Override
void buildFinished(BuildResult buildResult) {
println "---------------------------------------"
println "---------------------------------------"
println "build finished, now println all task execution time:"
if (timeCostExt.sorted) {
//進行排序
List<TaskExecTimeInfo> list = new ArrayList<>()
for (Map.Entry<String, TaskExecTimeInfo> entry : timeCostMap) {
list.add(entry.value)
}
Collections.sort(list, new Comparator<TaskExecTimeInfo>() {
@Override
int compare(TaskExecTimeInfo t1, TaskExecTimeInfo t2) {
return t2.total - t1.total
}
})
for (TaskExecTimeInfo timeInfo : list) {
long t = timeInfo.total
if (t >= timeCostExt.threshold) {
println("${timeInfo.path} [${t}ms]")
}
}
} else {
//按 task 執行順序打印出執行時長信息
for (String path : taskPathList) {
long t = timeCostMap.get(path).total
if (t >= timeCostExt.threshold) {
println("${path} [${t}ms]")
}
}
}
println "---------------------------------------"
println "---------------------------------------"
}
在 build.gradle 里增加配置:
taskExecTime {
threshold 100
sorted true
}
再來看看輸出結果:
---------------------------------------
---------------------------------------
build finished, now println all task execution time:
:app:mergeDebugResources [1109ms]
:app:transformDexArchiveWithExternalLibsDexMergerForDebug [644ms]
:app:processDebugResources [503ms]
:app:transformClassesWithDexBuilderForDebug [464ms]
:app:packageDebug [341ms]
:app:compileDebugJavaWithJavac [329ms]
:app:transformDexArchiveWithDexMergerForDebug [170ms]
:app:transformResourcesWithMergeJavaResForDebug [122ms]
:app:transformNativeLibsWithMergeJniLibsForDebug [122ms]
---------------------------------------
---------------------------------------
系列文章
Android Gradle學習(一):Gradle基礎入門
Android Gradle學習(二):如何創建Task
Android Gradle學習(三):Task進階學習
Android Gradle學習(四):Project詳解
Android Gradle學習(五):Extension詳解
Android Gradle學習(六):NamedDomainObjectContainer詳解
Android Gradle學習(七):Gradle構建生命周期
Android Gradle學習(八):統計Task執行時長
Android Gradle學習(九):一些有用的小技巧