亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > Android > 正文

關于gradle你應該知道的一些小事

2019-10-21 21:37:49
字體:
來源:轉載
供稿:網友

前言

gradle的定義(來自維基百科)

Gradle是一個基于Apache Ant和Apache Maven概念的項目自動化建構工具。它使用一種基于Groovy的特定領域語言來聲明項目設置,而不是傳統的XML。當前其支持的語言限于Java、Groovy和Scala,計劃未來將支持更多的語言。

通俗的理解:gradle是一種構建工具,我們可以用他來對多工程進行各種管理(依賴,打包,部署,發布,各種渠道的差異管理);

有些時候,我們會有一些個性化的構建需求,比如我們引入了第三方庫,或者我們想要在通用構建過程中做一些其他的事情,這時我們就要自己在系統默認構建規則上做一些修改。這時候我們就要自己向Gradle”下命令“了,這時候我們就需要用Gradle能聽懂的話了,也就是Groovy。

我們在開頭處提到“Gradle是一種構建工具”。實際上,當我們想要更靈活的構建過程時,Gradle就成為了一個編程框架——我們可以通過編程讓構建過程按我們的意愿進行。也就是說,當我們把Gradle作為構建工具使用時,我們只需要掌握它的配置腳本的基本寫法就OK了;而當我們需要對構建流程進行高度定制時,就務必要掌握Groovy等相關知識了。

遭遇的問題

我們在實時多項目構建的時候經常遇到以下這些問題:

1、同時依賴了不同版本的某個庫,編譯時出現duplicate class錯誤;

2、gradle 不同版本api報錯;

3、不會寫gradle配置,看不懂gradle語法,不知道從何學起;

4、對編譯過程中gradle的報錯無從下手;

等等…

我們接下來將從實際項目出發一步一步來學習gradle的這些事,本文主旨在于學習gradle的思路,深度細節將會忽略;

揭開Gradle的面紗

一、理解打包命令 gradle clean assembleDebug/assembleRelease

以上這條命令可以分解為三個部分,gradle,clean, assembleDebug;實際上就和我們執行腳本一樣,gradle是執行器,而clean 和 assembleDebug是入參, 在這里它們兩個代表不同的task,就類似gradle task1 task2 這樣。

二、什么是task?

在build.gradle寫上

task task1 { println "===>task 1"}task task2 { println "===>task 2"}

這樣就定義了兩個task;當我們執行gradle task1 task2 -q的時候(-q是設置日志級別),理論上會看到日志輸出:

===>task 1
===>task 2

task的關系有dependsOn,mustRunAfter等等,由于項目中用的比較少這里先跳過這部分;

這里我們簡單講一下閉包的概念:

閉包在groovy中是一個處于代碼上下文中的開放的,匿名代碼塊。它可以訪問到其外部的變量或方法,
更詳細的請自行google

然而,當我們在項目里執行gradle task1 task2 -q的時候,我們發現輸出是這樣的:

 SeeyouClient git:(SeeyouClient-dev) ? gradle task1 task2 -q
doPackage value:False
Configuration 'compile' in project ':app' is deprecated. Use 'implementation' instead.
==============anna apply start==================
configuration do:
include
**  onClick  
**  onItemClick  
**  onCheckedChanged  
**  onItemSelected  
**  onSwitchButtonCheck  
**  onItemLongClick  
**  onLongClick  
**  onPullRefresh  
**  OnRefresh  
configuration do:
exclude
org/conscrypt/  
configuration do:
exceptions
java/lang/Exception  
java/lang/NullPointerException  
configuration do:
switch
custom  
need inject=false
==============anna apply end==================
Configuration 'provided' in project ':app' is deprecated. Use 'compileOnly' instead.
Configuration 'debugCompile' in project ':app' is deprecated. Use 'debugImplementation' instead.
===>task 1
===>task 2
DexKnife: Processing Variant
DexKnife: processSplitDex true
DexKnife: processing Task
----------------------tinker build warning ------------------------------------
tinker auto operation: 
excluding annotation processor and source template from app packaging. Enable dx jumboMode to reduce package size.
enable dx jumboMode to reduce package size.
disable preDexLibraries to prevent ClassDefNotFoundException when your app is booting.
disable archive dex mode so far for keeping dex apply.
tinker will change your build configs:
we will add TINKER_ID=117 in your build output manifest file build/intermediates/manifests/full/*
if minifyEnabled is true
you will find the gen proguard rule file at build/intermediates/tinker_intermediates/tinker_proguard.pro
and we will help you to put it in the proguardFiles.
if multiDexEnabled is true
you will find the gen multiDexKeepProguard file at build/intermediates/tinker_intermediates/tinker_multidexkeep.pro
and we will help you to put it in the MultiDexKeepProguardFile.
if applyResourceMapping file is exist
we will build app apk with resource R.txt file
if resources.arsc has changed, you should use applyResource mode to build the new apk!
-----------------------------------------------------------------
Task spend time:

這是為什么呢?原因是gradle具有自己的生命周期:

初始化階段:負責判斷有多少個Projects參與構建:
 先執行settings.gradle
配置階段:負責對初始化階段創建的Projects完成配置:
 比如添加Task,修改Task的行為,閉包的內容會被執行,執行build.gradle的內容;
執行階段:根據配置階段的配置執行任務:
 執行task對應的內容,如doLast,doFirst之類的

因此gradle task1 task2 -q的輸出日志就可以理解了,其實按照task1和task2的寫法,執行gradle task1 task2 -q和gradle -q實際上效果是一樣的。

三、gradle clean assmebleDebug到底做了什么?(源碼追蹤和依賴分析出編譯流程)

1、打開gradle-4.5.1/bin/gradle文件可以看到執行了代碼:

 eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "/"-Dorg.gradle.appname=$APP_BASE_NAME/"" -classpath "/"$CLASSPATH/"" org.gradle.launcher.GradleMain "$APP_ARGS"

 最終調用exec "$JAVACMD" "$@"來執行;所以入口就是:org.gradle.launcher.GradleMain

 具體細節可以參照:https://blog.csdn.net/yanbober/article/details/60584621

2,最終會調用DefaultGradleLauncher里,我們可以很明確的看到它的生命周期:

這邊最需要注意的時候,當我們只執行gradle -q這樣的時候,實際上每一次都會執行到TaskGraph的階段;也就是所有的tasks都已經梳理完成;

public class DefaultGradleLauncher implements GradleLauncher { //(這里是4.5.1的版本,生命周期更細致化) private enum Stage {  Load, LoadBuild, Configure, TaskGraph, Build, Finished } //2.14.1的版本則是:  private enum Stage {  Load, Configure, Build }//核心方法private void doBuildStages(Stage upTo) {  try {   loadSettings();   if (upTo == Stage.Load) {    return;   }   configureBuild();   if (upTo == Stage.Configure) {    return;   }   constructTaskGraph();   if (upTo == Stage.TaskGraph) {    return;   }   runTasks();   finishBuild();  } catch (Throwable t) {   Throwable failure = exceptionAnalyser.transform(t);   finishBuild(new BuildResult(upTo.name(), gradle, failure));   throw new ReportedException(failure);  } }  //調用時機  @Override public SettingsInternal getLoadedSettings() {  doBuildStages(Stage.Load);  return settings; } @Override public GradleInternal getConfiguredBuild() {  doBuildStages(Stage.Configure);  return gradle; } public GradleInternal executeTasks() {  doBuildStages(Stage.Build);  return gradle; }

四、知道編譯流程后有什么用呢?

1、我們經常在app/build.gradle看到這樣的代碼:

project.afterEvaluate {...}android.applicationVariants.all {...}gradle.addListener(new TaskListener())apply from '../mvn.gradle'...

這里我們介紹幾個概念:

project

對應gradle源碼的Project.java(按住control點擊project會自動跳轉),里邊提供一些對外的方法,如afterEvalute,beforeEvalue; 在理解編譯流程后,才能靈活的使用這些api;

android

對應gradle插件的AppExtension.java文件,提供了一些對外的參數和方法,我們可以使用android.xxx來訪問app/build.gradle里的任意參數和方法;

gradle

對應的gradle源碼里的Gradle.java對象,也是提供了一系列的方法給外部使用;

那么接下來假設我們有這樣一個需求:找到一個叫cleanBuildCache的task,找到之后添加一個action,打印一行字; 要實現這個需求,首先我們如何遍歷這個app的所有task:

有很多種寫法:

gradle.getTaskGraph().whenReady { project.tasks.each {  task->   println "taskName:"+task.getName() }}project.afterEvaluate { project.tasks.each {  task->   println "taskName:"+task.getName() }}

執行gradle -q 感受一下。

接下看看如何添加action

project.afterEvaluate { project.tasks.each {  task->   // println "taskName:"+task.getName()   if(task.getName().equals("cleanBuildCache")){    println "find cleanBuildCache!!!!!!"    List<Action<? super Task>> list = new ArrayList<>()    list.add(new Action<Task>() {     @Override     void execute(Task task1) {      println 'excute cleanBuildCache action !!!!!!'     }    })    task.setActions(list)   } }}

執行gradle cleanBuildCache感受一下,

你會看到‘excute cleanBuildCache action !!!!!!'的打印字樣;

那為什么一定要放在afterEvaluate之后呢,因為這樣tasksGrap完成才有那么多task讓你遍歷,這就是理解生命周期所帶來的好處。

2、現在回顧我們之前主app寫的代碼:

processProductDebugManifest;project.tasks.each {  task->   if(task.getName().equals("processZroTestDebugManifest")){    println '!!!!!find processZroTestDebugManifest task:'    task.outputs.files.each {     file ->      println 'file.getAbsolutePath():'+ file.getAbsolutePath()      //file.getAbsolutePath():/Users/mu/MeiyouCode/PeriodProject/SeeyouClient/app/build/intermediates/manifests/instant-run/zroTest/debug      //file.getAbsolutePath():/Users/mu/MeiyouCode/PeriodProject/SeeyouClient/app/build/intermediates/manifests/full/zroTest/debug      //file.getAbsolutePath():/Users/mu/MeiyouCode/PeriodProject/SeeyouClient/app/build/outputs/logs/manifest-merger-zroTest-debug-report.txt    }    task.doLast {     println '!!!!!excute processZroTestDebugManifest task'     def dated = new Date().format("MMdd HH:mm")     def manifestFile = "${buildDir}/intermediates/manifests/full/zroTest/debug/AndroidManifest.xml"     def updatedContent = new File(manifestFile).getText('UTF-8')       .replaceAll("MY_APP_PKGNAME", "${MY_APP_PKGNAME}")       .replaceAll("MY_JPUSH_APPKEY", "${JPUSH_APPKEY}")       .replaceAll("MY_HUAWEI_APPKEY", "${HUAWEI_APPKEY}")       .replaceAll("MY_MEIZU_APPKEY", "${MEIZU_APPKEY}")       .replaceAll("MY_MEIZU_APPID", "${MEIZU_APPID}")     // .replaceAll("MY_XIAOMI_APPKEY", "${XIAOMI_APPKEY}")     // .replaceAll("MY_XIAOMI_APPID", "${XIAOMI_APPID}")     //.replaceAll("cn.jpush.android.service.PluginXiaomiPlatformsReceiver","com.meiyou.message.mipush.XiaomiReceiver")       .replaceAll("MY_APK_VERSION", "${archivesBaseName}-${dated}")     new File(manifestFile).write(updatedContent, 'UTF-8')    }   } }

實際上也可以寫成這樣(但是這樣因為變種不確定,寫死成zroTest/debug,所以還是用上面的方法比較好,直接替換所有的變種):

project.tasks.each {  task->   if(task.getName().equals("processZroTestDebugManifest")){    println '!!!!!find processZroTestDebugManifest task:'+task.outputs.files.each {     file ->      file.getAbsolutePath();    }    task.doLast {     println '!!!!!excute processZroTestDebugManifest task'     def dated = new Date().format("MMdd HH:mm")     def manifestFile = "${buildDir}/intermediates/manifests/full/zroTest/debug/AndroidManifest.xml"     def updatedContent = new File(manifestFile).getText('UTF-8')       .replaceAll("MY_APP_PKGNAME", "${MY_APP_PKGNAME}")       .replaceAll("MY_JPUSH_APPKEY", "${JPUSH_APPKEY}")       .replaceAll("MY_HUAWEI_APPKEY", "${HUAWEI_APPKEY}")       .replaceAll("MY_MEIZU_APPKEY", "${MEIZU_APPKEY}")       .replaceAll("MY_MEIZU_APPID", "${MEIZU_APPID}")     // .replaceAll("MY_XIAOMI_APPKEY", "${XIAOMI_APPKEY}")     // .replaceAll("MY_XIAOMI_APPID", "${XIAOMI_APPID}")     //.replaceAll("cn.jpush.android.service.PluginXiaomiPlatformsReceiver","com.meiyou.message.mipush.XiaomiReceiver")       .replaceAll("MY_APK_VERSION", "${archivesBaseName}-${dated}")     new File(manifestFile).write(updatedContent, 'UTF-8')    }   } }

3、如何知道某個task干了什么呢,比如processZroTestDebugManifest或者Clean:

這些是com.android.tools.build到源碼里尋找或者直接compile ‘com.android.tools.build:gradle:3.0.1'直接從依賴庫里看源碼; 或者直接下載源碼(大概30G左右):

$ mkdir gradle_2.3.0$ cd gradle_2.3.0$ repo init -u https://android.googlesource.com/platform/manifest -b gradle_2.3.0$ repo sync

大部分tasks都在com.android.build.gradle.tasks文件夾下,比如:ManifestProcessorTask和CleanBuildCache

具體可以:https://fucknmb.com/2017/06/01/Android-Gradle-Plugin源碼閱讀與編譯/

4、如何查找某個task的依賴呢,比如我想知道assmebleZroTestDebug執行后最終執行了哪些task;

1、編譯后打?。?/p>

gradle.addListener(new TaskListener())class TaskListener implements BuildListener,TaskExecutionListener { private List<String> tasks = new ArrayList<>(); @Override void buildStarted(Gradle gradle) { } @Override void settingsEvaluated(Settings settings) { } @Override void projectsLoaded(Gradle gradle) { } @Override void projectsEvaluated(Gradle gradle) { } @Override void buildFinished(BuildResult result) {  StringBuilder stringBuilder = new StringBuilder();  for(String taskName:tasks){   stringBuilder.append(taskName).append("/n")  }  println("任務列表:/n"+stringBuilder.toString()) } @Override void beforeExecute(Task task) { } @Override void afterExecute(Task task, TaskState state) {  //println("===>Task:"+task.getName())  tasks.add(task.getName()) }}

2、不用編譯直接打印;

void printTaskDependency(Task task, String divider) { divider += "-------" task.getTaskDependencies().getDependencies(task).any() {  println(divider+ it.getPath())  if (it.getPath().contains(":app")) {   printTaskDependency(it,divider)  } }}gradle.getTaskGraph().whenReady { project.tasks.all {  //println("!!!!!!!!!! it:"+it.getName()+"==>it.getPath:"+it.getPath())  if (it.getPath().equals(":app:assembleZroTestDebug")) {   //println(it.getPath())   printTaskDependency(it,"")  } }}

5、常用技能

1、gradle :app:dependencies > 1.txt 分析整個app的aar依賴

可以用于排查依賴庫異常的問題;

請注意?。簩こ桃蕾嚐o效;

2、productFlavors和buildType概念,組合成變種 如:

productFlavors { branchOne {  applicationId "com.example.branchOne"  buildConfigField "String", "CONFIG_ENDPOINT", "http://branchOne.com/android" } branchTwo {  applicationId "com.example.branchTwo"  buildConfigField "String", "CONFIG_ENDPOINT", "http://branchTwo.org" }}dependencies { compile 'com.android.support:support-v4:22.2.0' branchOneCompile 'com.android.support:appcompat-v7:22.2.0'//只為branchOne添加這個依賴}

3、排除依賴和強制使用某個版本和強制排除某個庫:

configurations.all {  resolutionStrategy {//   force 'org.javassist:javassist:3.18.2-GA'   // don't cache changing modules at all   cacheChangingModulesFor 0, 'seconds'//   //強制模塊使用指定版本號(防止其他模塊使用、跟主工程不匹配的版本:   forcedModules = [     "com.meiyou:peroid.base:${PERIOD_BASE_VERSION}",     'org.javassist:javassist:3.18.2-GA'//"org.javassist:javassist:3.20.0-GA"//     , 'com.google.guava:guava:18.0'//'com.google.guava:guava:19.0-rc2'//   ]   exclude group: 'com.squareup.okhttp3'   exclude group: 'com.google.code.findbugs', module: 'annotations'  } }

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VEVB武林網的支持。


注:相關教程知識閱讀請移步到Android開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美日韩激情视频| 97在线视频免费观看| 91久久精品日日躁夜夜躁国产| 亚洲女同精品视频| 精品久久久av| 久久久亚洲影院你懂的| 亚洲欧美国产日韩天堂区| 91精品视频观看| 91精品在线一区| 亚洲视频一区二区| 久久精品亚洲热| 亚洲天堂久久av| xxxxxxxxx欧美| 亚洲天堂精品在线| 欧美区在线播放| 午夜精品三级视频福利| 国语自产精品视频在线看抢先版图片| 日本91av在线播放| 欧美裸体xxxx| 最新国产精品亚洲| 亚洲午夜小视频| 黄色精品在线看| 亚洲最新在线视频| 久久久久久有精品国产| 国产精品自拍网| 欧美国产在线电影| 国模吧一区二区| 美女性感视频久久久| 日韩欧美中文字幕在线观看| 2020久久国产精品| 91色在线视频| 国产成人精品视| 亚洲欧美日韩另类| 亚洲人成电影网站色| 日韩精品在线观看一区二区| 欧美国产日韩视频| 日韩在线观看你懂的| 欧美日韩免费在线观看| 日韩欧美aaa| 国产丝袜一区视频在线观看| 红桃视频成人在线观看| 中文字幕亚洲一区二区三区五十路| 久久色免费在线视频| 日本欧美爱爱爱| 日韩中文字幕不卡视频| 日韩成人xxxx| 国产91在线播放九色快色| 成人中文字幕在线观看| 久久精品亚洲热| 亚洲国产精品大全| 精品中文视频在线| 亚洲电影免费观看高清| 成人免费淫片aa视频免费| 国产日韩精品一区二区| 亚洲欧美一区二区三区情侣bbw| 欧美精品一二区| 欧美高清激情视频| 97精品国产97久久久久久| 九九九热精品免费视频观看网站| 欧美老女人性生活| 国产精品美腿一区在线看| 日本三级韩国三级久久| 国产精品69av| 久久影视电视剧免费网站| 日韩电影大全免费观看2023年上| 国产精品美女av| 国内自拍欧美激情| 欧美做受高潮1| 国产精品久久久久久网站| 欧美在线视频免费观看| 欧美在线免费看| 国产精品视频资源| 亚洲欧洲美洲在线综合| 久久艳片www.17c.com| 韩国三级日本三级少妇99| 日韩av在线直播| 国产精品成人播放| 亚洲天堂2020| 精品日韩中文字幕| 久久99国产综合精品女同| 国产精品影院在线观看| 亚洲综合最新在线| 欧美诱惑福利视频| 一区二区三区视频在线| 伊人av综合网| 美女国内精品自产拍在线播放| 日韩精品欧美国产精品忘忧草| 欧美精品一区二区三区国产精品| 欧美激情精品久久久| 日韩中文字幕视频在线| 欧美激情二区三区| 国产不卡av在线| 亚洲男人天堂网站| 性色av一区二区三区红粉影视| 在线看欧美日韩| 亚洲在线免费观看| 亚洲福利视频免费观看| 欧美成人免费播放| 亚洲区在线播放| 91精品视频一区| 在线播放日韩专区| 日韩亚洲欧美成人| 中文字幕一区日韩电影| 68精品国产免费久久久久久婷婷| 久久免费成人精品视频| 久久精品视频免费播放| 久久亚洲成人精品| 亚洲自拍高清视频网站| 欧美大尺度激情区在线播放| 69影院欧美专区视频| 视频在线观看一区二区| 欧美二区乱c黑人| 国产精品自拍偷拍| 欧美极品少妇xxxxⅹ裸体艺术| 亚洲人成毛片在线播放| 91久久国产精品91久久性色| 55夜色66夜色国产精品视频| 青青久久av北条麻妃黑人| 欧美日韩亚洲一区二| 欧美一区二区三区免费观看| 欧美国产亚洲精品久久久8v| 国产美女被下药99| 亚洲精品wwww| 欧美成人激情图片网| 一本色道久久88综合日韩精品| 一区二区三区视频免费| 欧美一级电影久久| 国产精品中文久久久久久久| 亚洲精品久久视频| 亚洲精品一区二区在线| 欧美在线一区二区三区四| 97在线免费观看视频| 亚洲免费视频在线观看| 欧美精品videossex88| 亚洲free嫩bbb| 欧美成人精品一区| 8x海外华人永久免费日韩内陆视频| 色偷偷偷亚洲综合网另类| 青青久久av北条麻妃黑人| 亚洲精品成人av| 国产69精品久久久久99| 国产一区二区三区在线| 97视频国产在线| 国产精品一香蕉国产线看观看| 欧美国产精品va在线观看| 日韩成人激情视频| 久久久成人精品| 久久久亚洲网站| 国产中文欧美精品| 国产成人高潮免费观看精品| 国产视频精品一区二区三区| www.日韩av.com| 精品一区二区亚洲| 午夜精品久久久久久久99热浪潮| 美女国内精品自产拍在线播放| 日韩中文字幕在线免费观看| 久久久久国产一区二区三区| 欧美一区二区三区艳史| 欧美在线一级视频| 亚洲护士老师的毛茸茸最新章节| 4444欧美成人kkkk| 最新日韩中文字幕| 国产精品网红福利| 98精品国产高清在线xxxx天堂|