Gradle

Gradle

Gradle是什么

  1. gradle是一個工具 --> 會寫,會配置腳本
  2. gradle是一個編程框架 --> 更多內(nèi)容

Gradle的基本組件

gradle中,每一個待編譯的工程都是一個project,每一個project構(gòu)建時都包括一系列Task,比如一個android apk的編譯包括java源碼編譯Task,資源編譯Task,Jni編譯Task,Lint檢查Task,打包Apk的Task,簽名Task等,這些Task的定義和執(zhí)行是有插件決定的。Gradle是一個框架,負(fù)責(zé)定義流程和規(guī)則,具體的編譯工作是由Gradle插件完成的,比如編譯Java有Java插件,編譯Groovy有Groovy插件,編譯Android APP有Android APP插件,編譯Android Library有Android Library插件。

舉個栗子


這個gradle目錄中包含5個項目,其中3個Lib項目和2個Apk項目,其中每一個項目的根目錄下都有一個Build.gradle,build.gradle是該項目的編譯腳本。要同時編譯這些gradle的話,需要Multi-Projects Build。
這要需要以下步驟:

  1. 在root目錄下新建build.gradle。它用來配置其他子project,比如為子project添加一些屬性。這個build.gradle可以沒有。
  2. 在root目錄下新家settings.gradle。它來定義這個multiprojects包含哪些子project。這個settings.gradle是必須的,很重要!settings.gradle除了include外,還可以設(shè)置一些方法,用于這些函數(shù)會在gradle構(gòu)建整個工程時執(zhí)行。

gradle命令介紹

gradle的工作流程

  1. 初始化階段。 對于剛才的例子來說就是執(zhí)行settings.gradle
  2. 配置階段。 解析根目錄和每個項目的build.gradle。確定內(nèi)部的Task關(guān)系和流程。
  3. 執(zhí)行階段。

最后,關(guān)于gradle的工作流程,只需要記?。?/p>

  • Gradle有一個初始化流程,這個時候settings.gradle會執(zhí)行。
  • 在配置階段,每個Project都會被解析,其內(nèi)部的任務(wù)也會被添加到一個有向圖里,用于解決執(zhí)行過程中的依賴關(guān)系。
  • 然后才是執(zhí)行階段。你在gradle xxx中指定什么任務(wù),gradle就會將這個xxx任務(wù)鏈上的所有任務(wù)全部按依賴順序執(zhí)行一遍!

Gradle的編程模型和API實例

先看官方文檔

Gradle對象

Project對象

在project中,我們要:

  • 加載插件
  • 如果插件不同,要對插件進(jìn)行不同的配置
  • 設(shè)置屬性
1. 加載插件

apply plugin:xxx

2. 設(shè)置屬性
3. Task介紹
4. 實例
  • settings.gradle是必不可少的
  • 根目錄下的build.gradle。這個我們沒講過,因為有的根目錄本身不包含代碼,而是包含其他5個子project。
  • 每個project目錄下包含對于的build.gradle
  • 另外,我把常用的函數(shù)封裝到一個名為utils.gradle的腳本里了。
4.1 utils.gradle

utils.gradle是自定義的,主要是添加一些常用的函數(shù)

[utils.gradle]

import groovy.util.XmlSlurper  //解析XML時候要引入這個groovy的package  

def copyFile(String srcFile,dstFile){  
     ......//拷貝文件函數(shù),用于將最后的生成物拷貝到指定的目錄  
}  
def rmFile(String targetFile){  
    .....//刪除指定目錄中的文件  
}  
   
def cleanOutput(boolean bJar = true){  
    ....//clean的時候清理  
}  
   
def copyOutput(boolean bJar = true){  
    ....//copyOutput內(nèi)部會調(diào)用copyFile完成一次build的產(chǎn)出物拷貝  
}  
   
def getVersionNameAdvanced(){//老朋友  
   defxmlFile = project.file("AndroidManifest.xml")  
   defrootManifest = new XmlSlurper().parse(xmlFile)  
   returnrootManifest['@android:versionName']    
}  
   
//對于android library編譯,我會disable所有的debug編譯任務(wù)  
def disableDebugBuild(){  
  //project.tasks包含了所有的tasks,下面的findAll是尋找那些名字中帶debug的Task。  
  //返回值保存到targetTasks容器中  
  def targetTasks = project.tasks.findAll{task ->  
     task.name.contains("Debug")  
  }  
  //對滿足條件的task,設(shè)置它為disable。如此這般,這個Task就不會被執(zhí)行  
 targetTasks.each{  
     println"disable debug task  :${it.name}"  
    it.setEnabled false  
  }  
}  
//將函數(shù)設(shè)置為extra屬性中去,這樣,加載utils.gradle的Project就能調(diào)用此文件中定義的函數(shù)了  
ext{  
    copyFile= this.&copyFile  
    rmFile =this.&rmFile  
   cleanOutput = this.&cleanOutput  
   copyOutput = this.&copyOutput  
   getVersionNameAdvanced = this.&getVersionNameAdvanced  
   disableDebugBuild = this.&disableDebugBuild  
}
4.2 settings.gradle

內(nèi)容為include的項目和一些初始化操作

[settings.gradle]

/*我們團(tuán)隊內(nèi)部建立的編譯環(huán)境初始化函數(shù) 
  這個函數(shù)的目的是 
  1  解析一個名為local.properties的文件,讀取AndroidSDK和NDK的路徑 
  2  獲取最終產(chǎn)出物目錄的路徑。這樣,編譯完的apk或者jar包將拷貝到這個最終產(chǎn)出物目錄中 
  3 獲取Android SDK指定編譯的版本 
*/  
def initMinshengGradleEnvironment(){  
    println"initialize Minsheng Gradle Environment ....."  
   Properties properties = new Properties()  
   //local.properites也放在posdevice目錄下  
    FilepropertyFile = new File(rootDir.getAbsolutePath()+ "/local.properties")  
   properties.load(propertyFile.newDataInputStream())  
    /* 
      根據(jù)Project、Gradle生命周期的介紹,settings對象的創(chuàng)建位于具體Project創(chuàng)建之前 
      而Gradle底對象已經(jīng)創(chuàng)建好了。所以,我們把local.properties的信息讀出來后,通過 
     extra屬性的方式設(shè)置到gradle對象中 
      而具體Project在執(zhí)行的時候,就可以直接從gradle對象中得到這些屬性了! 
    */  
    gradle.ext.api =properties.getProperty('sdk.api')  
    gradle.ext.sdkDir =properties.getProperty('sdk.dir')  
     gradle.ext.ndkDir =properties.getProperty('ndk.dir')  
     gradle.ext.localDir =properties.getProperty('local.dir')  
    //指定debugkeystore文件的位置,debug版apk簽名的時候會用到  
    gradle.ext.debugKeystore= properties.getProperty('debug.keystore')  
     ......  
    println"initialize Minsheng Gradle Environment completes..."  
}  
//初始化  
initMinshengGradleEnvironment()  
//添加子Project信息  
include 'CPosSystemSdk' , 'CPosDeviceSdk' ,'CPosSdkDemo','CPosDeviceServerApk', 'CPosSystemSdkWizarPosImpl'  
4.3 build.gradle

全局配置

[build.gradle]

//下面這個subprojects{}就是一個Script Block  
subprojects {  
  println"Configure for $project.name" //遍歷子Project,project變量對應(yīng)每個子Project  
  buildscript {  //這也是一個SB  
    repositories {//repositories是一個SB  
       ///jcenter是一個函數(shù),表示編譯過程中依賴的庫,所需的插件可以在jcenter倉庫中  
       //下載。  
       jcenter()  
    }  
    dependencies { //SB  
        //dependencies表示我們編譯的時候,依賴android開發(fā)的gradle插件。插件對應(yīng)的  
       //class path是com.android.tools.build。版本是1.2.3  
        classpath'com.android.tools.build:gradle:1.2.3'  
    }  
   //為每個子Project加載utils.gradle 。當(dāng)然,這句話可以放到buildscript花括號之后  
   applyfrom: rootProject.getRootDir().getAbsolutePath() + "/utils.gradle"  
 }//buildscript結(jié)束  
}  
4.4 重要的Script Block

某些Script Block的解釋

  • subprojects:它會遍歷posdevice中的每個子Project。在它的Closure中,默認(rèn)參數(shù)是子Project對應(yīng)的Project對象。由于其他SB都在subprojects花括號中,所以相當(dāng)于對每個Project都配置了一些信息。
  • buildscript:它的closure是在一個類型為ScriptHandler的對象上執(zhí)行的。主意用來所依賴的classpath等信息。通過查看ScriptHandler API可知,在buildscript SB中,你可以調(diào)用ScriptHandler提供的repositories(Closure )、dependencies(Closure)函數(shù)。這也是為什么repositories和dependencies兩個SB為什么要放在buildscript的花括號中的原因。明白了?這就是所謂的行話,得知道規(guī)矩。不知道規(guī)矩你就亂了。記不住規(guī)矩,又不知道查SDK,那么就徹底抓瞎,只能到網(wǎng)上到處找答案了!
  • 關(guān)于repositories和dependencies,大家直接看API吧。后面碰到了具體代碼我們再來介紹
4.5 依賴項目的build.gradle

如果該項目是一個android library。android studio默認(rèn)的build方式編譯得到的是一個.aar文件,如果需求是生成.jar格式的文件,就要按下邊的配置來。

[build.gradle]

//Library工程必須加載此插件。注意,加載了Android插件就不要加載Java插件了。因為Android  
//插件本身就是拓展了Java插件  
apply plugin: 'com.android.library'   
//android的編譯,增加了一種新類型的ScriptBlock-->android  
android {  
       //你看,我在local.properties中設(shè)置的API版本號,就可以一次設(shè)置,多個Project使用了  
      //借助我特意設(shè)計的gradle.ext.api屬性  
       compileSdkVersion =gradle.api  //這兩個紅色的參數(shù)必須設(shè)置  
       buildToolsVersion  = "22.0.1"  
       sourceSets{ //配置源碼路徑。這個sourceSets是Java插件引入的  
       main{ //main:Android也用了  
           manifest.srcFile 'AndroidManifest.xml' //這是一個函數(shù),設(shè)置manifest.srcFile  
           aidl.srcDirs=['src'] //設(shè)置aidl文件的目錄  
           java.srcDirs=['src'] //設(shè)置java文件的目錄  
        }  
     }  
   dependencies {  //配置依賴關(guān)系  
      //compile表示編譯和運行時候需要的jar包,fileTree是一個函數(shù),  
     //dir:'libs',表示搜索目錄的名稱是libs。include:['*.jar'],表示搜索目錄下滿足*.jar名字的jar  
     //包都作為依賴jar文件  
       compile fileTree(dir: 'libs', include: ['*.jar'])  
   }  
}  //android SB配置完了  
//clean是一個Task的名字,這個Task好像是Java插件(這里是Android插件)引入的。  
//dependsOn是一個函數(shù),下面這句話的意思是 clean任務(wù)依賴cposCleanTask任務(wù)。所以  
//當(dāng)你gradle clean以執(zhí)行clean Task的時候,cposCleanTask也會執(zhí)行  
clean.dependsOn 'cposCleanTask'  
//創(chuàng)建一個Task,  
task cposCleanTask() <<{  
    cleanOutput(true)  //cleanOutput是utils.gradle中通過extra屬性設(shè)置的Closure  
}  
//前面說了,我要把jar包拷貝到指定的目錄。對于Android編譯,我一般指定gradle assemble  
//它默認(rèn)編譯debug和release兩種輸出。所以,下面這個段代碼表示:  
//tasks代表一個Projects中的所有Task,是一個容器。getByName表示找到指定名稱的任務(wù)。  
//我這里要找的assemble任務(wù),然后我通過doLast添加了一個Action。這個Action就是copy  
//產(chǎn)出物到我設(shè)置的目標(biāo)目錄中去  
tasks.getByName("assemble"){  
   it.doLast{  
       println "$project.name: After assemble, jar libs are copied tolocal repository"  
        copyOutput(true)  
     }  
}  
/* 
  因為我的項目只提供最終的release編譯出來的Jar包給其他人,所以不需要編譯debug版的東西 
  當(dāng)Project創(chuàng)建完所有任務(wù)的有向圖后,我通過afterEvaluate函數(shù)設(shè)置一個回調(diào)Closure。在這個回調(diào) 
  Closure里,我disable了所有Debug的Task 
*/  
project.afterEvaluate{  
    disableDebugBuild()  
}  

android定義的script

其中buildToolsVersion和compileSdkVersion是必須配置的

4.6 APK項目的build.gradle

一個apk的build,包括ndk的編譯,項目簽名,混淆,配置依賴等。

[build.gradle]

apply plugin: 'com.android.application'  //APK編譯必須加載這個插件  
android {  
      compileSdkVersion gradle.api  
      buildToolsVersion "22.0.1"  
      sourceSets{  //差不多的設(shè)置  
       main{  
           manifest.srcFile 'AndroidManifest.xml'  
          //通過設(shè)置jni目錄為空,我們可不使用apk插件的jni編譯功能。為什么?因為據(jù)說  
         //APK插件的jni功能好像不是很好使....暈菜  
          jni.srcDirs = []   
           jniLibs.srcDir 'libs'  
            aidl.srcDirs=['src']  
           java.srcDirs=['src']  
           res.srcDirs=['res']  
        }  
    }//main結(jié)束  
   signingConfigs { //設(shè)置簽名信息配置  
       debug {  //如果我們在local.properties設(shè)置使用特殊的keystore,則使用它  
           //下面這些設(shè)置,無非是函數(shù)調(diào)用....請務(wù)必閱讀API文檔  
           if(project.gradle.debugKeystore != null){  
              storeFile file("file://${project.gradle.debugKeystore}")  
              storePassword "android"  
              keyAlias "androiddebugkey"  
              keyPassword "android"  
           }  
        }  
   }//signingConfigs結(jié)束  
     buildTypes {  
       debug {  
           signingConfig signingConfigs.debug  
           jniDebuggable false  
        }  
    }//buildTypes結(jié)束  
   dependencies {  
        //compile:project函數(shù)可指定依賴multi-project中的某個子project  
       compile project(':CPosDeviceSdk')  
       compile fileTree(dir: 'libs', include: ['*.jar'])  
   } //dependices結(jié)束  
  repositories{  
   flatDir {//flatDir:告訴gradle,編譯中依賴的jar包存儲在dirs指定的目錄  
           name "minsheng-gradle-local-repository"  
            dirsgradle.LOCAL_JAR_OUT //LOCAL_JAR_OUT是我存放編譯出來的jar包的位置  
   }  
  }//repositories結(jié)束  
}//android結(jié)束  
/* 
   創(chuàng)建一個Task,類型是Exec,這表明它會執(zhí)行一個命令。我這里讓他執(zhí)行ndk的 
   ndk-build命令,用于編譯ndk。關(guān)于Exec類型的Task,請自行腦補Gradle的API 
*/  
//注意此處創(chuàng)建task的方法,是直接{}喔,那么它后面的tasks.withType(JavaCompile)  
//設(shè)置的依賴關(guān)系,還有意義嗎?Think!如果你能想明白,gradle掌握也就差不多了  
task buildNative(type: Exec, description: 'CompileJNI source via NDK') {  
       if(project.gradle.ndkDir == null) //看看有沒有指定ndk.dir路徑  
          println "CANNOT Build NDK"  
       else{  
            commandLine "/${project.gradle.ndkDir}/ndk-build",  
               '-C', file('jni').absolutePath,  
               '-j', Runtime.runtime.availableProcessors(),  
               'all', 'NDK_DEBUG=0'  
        }  
  }  
 tasks.withType(JavaCompile) {  
       compileTask -> compileTask.dependsOn buildNative  
  }  
  ......    
 //對于APK,除了拷貝APK文件到指定目錄外,我還特意為它們加上了自動版本命名的功能  
 tasks.getByName("assemble"){  
       it.doLast{  
       println "$project.name: After assemble, jar libs are copied tolocal repository"  
       project.ext.versionName = android.defaultConfig.versionName  
       println "\t versionName = $versionName"  
       copyOutput(false)  
     }  
}  

參考

http://blog.csdn.net/innost/article/details/48228651

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,725評論 25 709
  • 參考資料:http://gold.xitu.io/post/580c85768ac247005b5472f9htt...
    zhaoyubetter閱讀 11,233評論 0 6
  • 1. 概述 Android項目的構(gòu)建過程是由Gradle插件完成的,Gradle 插件是在Gradle框架的基礎(chǔ)上...
    小蕓論閱讀 8,378評論 1 42
  • 前言 從2013年Google推出Android Studio(后面以AS簡稱)開始,到現(xiàn)在已經(jīng)歷經(jīng)3年,版本也發(fā)...
    dfqin閱讀 1,962評論 1 3
  • 你常說你從來沒有和任何人有過那么好的開始。我們都因此引以為傲。 我已經(jīng)忘了你第一次用那些字眼罵我是什么時候了。我只...
    燕思閱讀 332評論 0 2

友情鏈接更多精彩內(nèi)容