關(guān)于 Gradle 的基本知識(shí)渗稍,前面章節(jié)已經(jīng)講的差不多了。那么,我們現(xiàn)在來(lái)牛刀小試一下诈嘿,看看 Gradle 有什么用武之地。
我們?cè)趯?Android 應(yīng)用程序打包成 apk 包時(shí)削葱,有時(shí)會(huì)發(fā)現(xiàn)整個(gè) build 過(guò)程特別長(zhǎng)奖亚,短則 1、2 分鐘析砸,長(zhǎng)則大幾分鐘甚至更長(zhǎng)昔字,特別是你要進(jìn)行調(diào)試時(shí),漫長(zhǎng)的等待會(huì)讓人很焦躁。我們?cè)诳刂婆_(tái)可以看到整個(gè)打包過(guò)程包含很多個(gè) task 作郭,那么到底是哪些 task 的執(zhí)行花費(fèi)了大量時(shí)間內(nèi)陨囊?
Gradle 提供了很多構(gòu)建生命周期鉤子函數(shù),我們可以用 TaskExecutionListener 來(lái)監(jiān)聽(tīng)整個(gè)構(gòu)建過(guò)程中 task 的執(zhí)行:
public interface TaskExecutionListener {
void beforeExecute(Task task);
void afterExecute(Task task, TaskState taskState);
}
在每個(gè) task 執(zhí)行前先搜集其相關(guān)信息夹攒,記錄該 task 執(zhí)行的開(kāi)始時(shí)間等蜘醋,在 task 執(zhí)行完成后,記錄其執(zhí)行結(jié)束時(shí)間咏尝,這樣就能統(tǒng)計(jì)出該 task 的執(zhí)行時(shí)長(zhǎng)压语。
接著,我們可以用 BuildListener 來(lái)監(jiān)聽(tīng)整個(gè)構(gòu)建是否完成编检,在構(gòu)建完成后胎食,輸出所有執(zhí)行過(guò)的 task 信息,以及每個(gè) task 的執(zhí)行時(shí)長(zhǎng):
public interface BuildListener {
void buildStarted(Gradle gradle);
void settingsEvaluated(Settings settings);
void projectsLoaded(Gradle gradle);
void projectsEvaluated(Gradle gradle);
void buildFinished(BuildResult buildResult);
}
在 buildFinished 方法中允懂,監(jiān)聽(tīng)構(gòu)建完成以及成功與否厕怜。為了方便使用,考慮做成一個(gè) Gradle 插件蕾总,關(guān)于插件的制作粥航,這里不贅述了,網(wǎng)上有很多關(guān)于 Gradle 插件制作的教程谤专。
不多說(shuō)了躁锡,直接上代碼,核心只有一個(gè)插件類:
class BuildTimeCostPlugin implements Plugin<Project>{
//用來(lái)記錄 task 的執(zhí)行時(shí)長(zhǎng)等信息
Map<String, TaskExecTimeInfo> timeCostMap = new HashMap<>()
//用來(lái)按順序記錄執(zhí)行的 task 名稱
List<String> taskPathList = new ArrayList<>()
@Override
void apply(Project project) {
//監(jiān)聽(tīng)每個(gè)task的執(zhí)行
project.getGradle().addListener(new TaskExecutionListener() {
@Override
void beforeExecute(Task task) {
//task開(kāi)始執(zhí)行之前搜集task的信息
TaskExecTimeInfo timeInfo = new TaskExecTimeInfo()
//記錄開(kāi)始時(shí)間
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執(zhí)行完之后置侍,記錄結(jié)束時(shí)的時(shí)間
TaskExecTimeInfo timeInfo = timeCostMap.get(task.getPath())
timeInfo.end = System.currentTimeMillis()
//計(jì)算該 task 的執(zhí)行時(shí)長(zhǎng)
timeInfo.total = timeInfo.end - timeInfo.start
}
})
//編譯結(jié)束之后:
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 執(zhí)行順序打印出執(zhí)行時(shí)長(zhǎng)信息
for (String path : taskPathList) {
long t = timeCostMap.get(path).total
if (t >= timeCostExt.threshold) {
println("${path} [${t}ms]")
}
} println "---------------------------------------"
println "---------------------------------------"
}
})
}
//關(guān)于 task 的執(zhí)行信息
class TaskExecTimeInfo {
long total //task執(zhí)行總時(shí)長(zhǎng)
String path
long start //task 執(zhí)行開(kāi)始時(shí)間
long end //task 結(jié)束時(shí)間
}
}
接下來(lái)映之,我們創(chuàng)建一個(gè)測(cè)試工程,使用這個(gè)插件蜡坊,執(zhí)行 “assembleDebug” 這個(gè)構(gòu)建任務(wù)杠输,打一個(gè) debug 的測(cè)試包出來(lái),構(gòu)建完成之后秕衙,可以在 Gradle Console 里看到本次構(gòu)建里所有 task 的執(zhí)行時(shí)長(zhǎng)信息:
---------------------------------------
---------------------------------------
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 這個(gè) task 執(zhí)行時(shí)長(zhǎng)為 2038ms蠢甲,將近有2秒鐘,是這里面執(zhí)行時(shí)長(zhǎng)最長(zhǎng)的一個(gè)据忘。由于 Gradle 支持增量構(gòu)建鹦牛,再次構(gòu)建的時(shí)間可能就不一樣了。
上面這個(gè)插件勇吊,能不能只打印出執(zhí)行時(shí)長(zhǎng)超過(guò) 1000ms 的任務(wù)呢曼追?結(jié)果能不能排序后輸出呢?我們繼續(xù)做點(diǎn)優(yōu)化汉规,這就需要前面介紹的 Extension 相關(guān)知識(shí)了礼殊。
先創(chuàng)建一個(gè) Extension 類,代碼如下:
class BuildTimeCostExtension {
//task執(zhí)行時(shí)間超過(guò)該值才會(huì)統(tǒng)計(jì)
int threshold
//是否按照task執(zhí)行時(shí)長(zhǎng)進(jìn)行排序,true-表示從大到小進(jìn)行排序晶伦,false-表示不排序
boolean sorted
void threshold(int threshold) {
this.threshold = threshold
}
void sorted(boolean sorted) {
this.sorted = sorted
}
}
修改插件類碟狞,在插件里創(chuàng)建自定義 Extension:
@Override
void apply(Project project) {
//創(chuàng)建一個(gè) Extension,配置輸出結(jié)果
final BuildTimeCostExtension timeCostExt = project.getExtensions().create("taskExecTime", BuildTimeCostExtension)
......
......
}
修改 task 執(zhí)行時(shí)長(zhǎng)輸出結(jié)果的代碼婚陪,根據(jù)配置來(lái)輸出不同的結(jié)果:
@Override
void buildFinished(BuildResult buildResult) {
println "---------------------------------------"
println "---------------------------------------"
println "build finished, now println all task execution time:"
if (timeCostExt.sorted) {
//進(jìn)行排序
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 執(zhí)行順序打印出執(zhí)行時(shí)長(zhǎng)信息
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
}
再來(lái)看看輸出結(jié)果:
---------------------------------------
---------------------------------------
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學(xué)習(xí)(一):Gradle基礎(chǔ)入門(mén)
Android Gradle學(xué)習(xí)(二):如何創(chuàng)建Task
Android Gradle學(xué)習(xí)(三):Task進(jìn)階學(xué)習(xí)
Android Gradle學(xué)習(xí)(四):Project詳解
Android Gradle學(xué)習(xí)(五):Extension詳解
Android Gradle學(xué)習(xí)(六):NamedDomainObjectContainer詳解
Android Gradle學(xué)習(xí)(七):Gradle構(gòu)建生命周期
Android Gradle學(xué)習(xí)(八):統(tǒng)計(jì)Task執(zhí)行時(shí)長(zhǎng)
Android Gradle學(xué)習(xí)(九):一些有用的小技巧