gradle中需要知道的事情

這篇文章主要介紹了gradle中需要知道的事情,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

創(chuàng)新互聯(lián)致力于互聯(lián)網(wǎng)品牌建設(shè)與網(wǎng)絡(luò)營銷,包括做網(wǎng)站、成都網(wǎng)站設(shè)計(jì)、SEO優(yōu)化、網(wǎng)絡(luò)推廣、整站優(yōu)化營銷策劃推廣、電子商務(wù)、移動(dòng)互聯(lián)網(wǎng)營銷等。創(chuàng)新互聯(lián)為不同類型的客戶提供良好的互聯(lián)網(wǎng)應(yīng)用定制及解決方案,創(chuàng)新互聯(lián)核心團(tuán)隊(duì)十載專注互聯(lián)網(wǎng)開發(fā),積累了豐富的網(wǎng)站經(jīng)驗(yàn),為廣大企業(yè)客戶提供一站式企業(yè)網(wǎng)站建設(shè)服務(wù),在網(wǎng)站建設(shè)行業(yè)內(nèi)樹立了良好口碑。

前言

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

Gradle是一個(gè)基于Apache Ant和Apache Maven概念的項(xiàng)目自動(dòng)化建構(gòu)工具。它使用一種基于Groovy的特定領(lǐng)域語言來聲明項(xiàng)目設(shè)置,而不是傳統(tǒng)的XML。當(dāng)前其支持的語言限于Java、Groovy和Scala,計(jì)劃未來將支持更多的語言。

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

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

我們?cè)陂_頭處提到“Gradle是一種構(gòu)建工具”。實(shí)際上,當(dāng)我們想要更靈活的構(gòu)建過程時(shí),Gradle就成為了一個(gè)編程框架——我們可以通過編程讓構(gòu)建過程按我們的意愿進(jìn)行。也就是說,當(dāng)我們把Gradle作為構(gòu)建工具使用時(shí),我們只需要掌握它的配置腳本的基本寫法就OK了;而當(dāng)我們需要對(duì)構(gòu)建流程進(jìn)行高度定制時(shí),就務(wù)必要掌握Groovy等相關(guān)知識(shí)了。

遭遇的問題

我們?cè)趯?shí)時(shí)多項(xiàng)目構(gòu)建的時(shí)候經(jīng)常遇到以下這些問題:

1、同時(shí)依賴了不同版本的某個(gè)庫,編譯時(shí)出現(xiàn)duplicate class錯(cuò)誤;

2、gradle 不同版本api報(bào)錯(cuò);

3、不會(huì)寫gradle配置,看不懂gradle語法,不知道從何學(xué)起;

4、對(duì)編譯過程中g(shù)radle的報(bào)錯(cuò)無從下手;

等等…

我們接下來將從實(shí)際項(xiàng)目出發(fā)一步一步來學(xué)習(xí)gradle的這些事,本文主旨在于學(xué)習(xí)gradle的思路,深度細(xì)節(jié)將會(huì)忽略;

揭開Gradle的面紗

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

以上這條命令可以分解為三個(gè)部分,gradle,clean, assembleDebug;實(shí)際上就和我們執(zhí)行腳本一樣,gradle是執(zhí)行器,而clean 和 assembleDebug是入?yún)ⅲ?在這里它們兩個(gè)代表不同的task,就類似gradle task1 task2 這樣。

二、什么是task?

在build.gradle寫上

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

這樣就定義了兩個(gè)task;當(dāng)我們執(zhí)行g(shù)radle task1 task2 -q的時(shí)候(-q是設(shè)置日志級(jí)別),理論上會(huì)看到日志輸出:

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

task的關(guān)系有dependsOn,mustRunAfter等等,由于項(xiàng)目中用的比較少這里先跳過這部分;

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

閉包在groovy中是一個(gè)處于代碼上下文中的開放的,匿名代碼塊。它可以訪問到其外部的變量或方法,
更詳細(xì)的請(qǐng)自行g(shù)oogle

然而,當(dāng)我們?cè)陧?xiàng)目里執(zhí)行g(shù)radle task1 task2 -q的時(shí)候,我們發(fā)現(xiàn)輸出是這樣的:

 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具有自己的生命周期:

初始化階段:負(fù)責(zé)判斷有多少個(gè)Projects參與構(gòu)建:
 先執(zhí)行settings.gradle
配置階段:負(fù)責(zé)對(duì)初始化階段創(chuàng)建的Projects完成配置:
 比如添加Task,修改Task的行為,閉包的內(nèi)容會(huì)被執(zhí)行,執(zhí)行build.gradle的內(nèi)容;
執(zhí)行階段:根據(jù)配置階段的配置執(zhí)行任務(wù):
 執(zhí)行task對(duì)應(yīng)的內(nèi)容,如doLast,doFirst之類的

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

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

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

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

 最終調(diào)用exec "$JAVACMD" "$@"來執(zhí)行;所以入口就是:org.gradle.launcher.GradleMain

 具體細(xì)節(jié)可以參照:https://blog.csdn.net/yanbober/article/details/60584621

2,最終會(huì)調(diào)用DefaultGradleLauncher里,我們可以很明確的看到它的生命周期:

這邊最需要注意的時(shí)候,當(dāng)我們只執(zhí)行g(shù)radle -q這樣的時(shí)候,實(shí)際上每一次都會(huì)執(zhí)行到TaskGraph的階段;也就是所有的tasks都已經(jīng)梳理完成;

public class DefaultGradleLauncher implements GradleLauncher {
 //(這里是4.5.1的版本,生命周期更細(xì)致化)
 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);
  }
 }
 
 //調(diào)用時(shí)機(jī)
  @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、我們經(jīng)常在app/build.gradle看到這樣的代碼:

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

這里我們介紹幾個(gè)概念:

project

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

android

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

gradle

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

那么接下來假設(shè)我們有這樣一個(gè)需求:找到一個(gè)叫cleanBuildCache的task,找到之后添加一個(gè)action,打印一行字; 要實(shí)現(xiàn)這個(gè)需求,首先我們?nèi)绾伪闅v這個(gè)app的所有task:

有很多種寫法:

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

執(zhí)行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)
   }
 }
}

執(zhí)行gradle cleanBuildCache感受一下,

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

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

2、現(xiàn)在回顧我們之前主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')
    }
   }
 }

實(shí)際上也可以寫成這樣(但是這樣因?yàn)樽兎N不確定,寫死成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、如何知道某個(gè)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、如何查找某個(gè)task的依賴呢,比如我想知道assmebleZroTestDebug執(zhí)行后最終執(zhí)行了哪些task;

1、編譯后打印;

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("任務(wù)列表:\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 分析整個(gè)app的aar依賴

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

請(qǐng)注意?。簩?duì)工程依賴無效;

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添加這個(gè)依賴
}

3、排除依賴和強(qiáng)制使用某個(gè)版本和強(qiáng)制排除某個(gè)庫:

configurations.all {
  resolutionStrategy {
//   force 'org.javassist:javassist:3.18.2-GA'
   // don't cache changing modules at all
   cacheChangingModulesFor 0, 'seconds'
//   //強(qiáng)制模塊使用指定版本號(hào)(防止其他模塊使用、跟主工程不匹配的版本:
   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'
  }
 }

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“gradle中需要知道的事情”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持創(chuàng)新互聯(lián),關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來學(xué)習(xí)!

本文題目:gradle中需要知道的事情
標(biāo)題來源:http://bm7419.com/article14/jdjoge.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供搜索引擎優(yōu)化、自適應(yīng)網(wǎng)站網(wǎng)站制作、網(wǎng)站維護(hù)、網(wǎng)站排名、外貿(mào)網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

微信小程序開發(fā)