經過上一節Android中的Gradle之配置及構建優化,咱們已經瞭解了Gradle的各個配置項的含義,知道了如何優化構建配置,但只會用別人提供好的,沒法按本身的意願實現功能。經過本章節,咱們將簡單介紹Groovy,瞭解Gradle中的Project與Task,引入gradle腳本,根據android plugin插件提供的功能自定義擴展,以及寫本身的Task及本身的gradle插件,相信看完以後能對gradle有進一步的瞭解。java
Apache Groovy is a powerful, optionally typed and dynamic language, with static-typing and static compilation capabilities, for the Java platform aimed at improving developer productivity thanks to a concise, familiar and easy to learn syntax. It integrates smoothly with any Java program, and immediately delivers to your application powerful features, including scripting capabilities, Domain-Specific Language authoring, runtime and compile-time meta-programming and functional programming.react
上面的意思大體以下:
Apache Groovy是一種功能強大,可選類型和動態語言,與靜態類型和靜態編譯功能,支持Java平臺,因爲其簡潔與易學性提升了開發人員的開發效率。它能夠與任何Java程序平滑集成,併爲您的應用程序提供強大的功能,包括腳本功能,域特定語言創做,運行時和編譯時元編程以及函數編程。
具體能夠專題進行學習Groovy語法。android
// 1 可選的類型定義 def version = 1 // 2 assert assert version == 2 // 3 括號是可選的 println version // 4 字符串 def s1 = 'Groovy' def s2 = "version is ${version}" def s3 = '''三個 分號 能夠 換行 ''' println s1 // Groovy println s2 // version is 1 println s3 // 三個 // 分號 // 能夠 // 換行 // 5 集合api // list def buildTools = ['ant','maven'] buildTools << 'gradle' println buildTools.getClass() // class java.util.ArrayList assert buildTools.size() == 3 // 沒有異常 // map def buildYears = ['ant':2000,'maven':2004] buildYears.gradle = 2009 println buildYears.ant // 2000 println buildYears['gradle'] // 2009 println buildYears.getClass() // class java.util.LinkedHashMap // 6 閉包 def c1 = { v -> println v } def method1(Closure closure){ closure('param') } method1(c1) // param method1{ c1 "hello" } 複製代碼
能夠說每個build.gradle都是一個Project對應項目中就是某一個module,直接在其中定義的屬性或者方法均可以使用project來調用。如def valueTest = 5
可使用valueTest
或者project.valueTest
來獲取valueTest的值。
根目錄的build.gradle也是一個Project,在其中定義ext{valueTest = 5}
,在module中的build.gradle中能夠直接使用rootProject.valueTest
或者rootProjext.ext.valueTest
來引用。這裏的ext是一個全局變量的意思。
編程
Project又是由一個或者多個Task組成,Task存在着依賴關係,依賴關係又保證了任務的執行順序。好比咱們熟知的Task有clean,jar,assemble等。
api
Gradle的聲明週期分爲三段:
bash
include ':app',':example'
引入gradle腳本,能夠實現對gradle代碼的複用,減小維護成本。能夠經過apply from: 'other.gradle'
來對other.gradle進行引入。平常開發中,咱們能夠經過以下幾點優化咱們的項目:
微信
在gradle中有不少配置項,如版本號,版本名稱,最小支持SDK版本,是否使用混淆,依賴的第三方庫等。這些配置頗有可能在項目中複用,如需修改,可能會致使遺漏,將這些配置提取出來,供各個build.gradle使用。具體以下:
一、在根目錄新建config.gradle
markdown
ext { android = [ compileSdkVersion: 27, minSdkVersion : 16, targetSdkVersion : 27, versionCode : 1, versionName : "1.0.0", multiDexEnabled : true ] version = [ kotlin_version : "1.2.50", support_version : "27.1.1", constraint_version : "1.1.3", junit_version : "4.12", runner_version : "1.0.2", espresso_core_version: "3.0.2" ] dependencies = [ "androidJUnitRunner": "android.support.test.runner.AndroidJUnitRunner", "kotlin" : "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${version["kotlin_version"]}", "appcompat_v7" : "com.android.support:appcompat-v7:${version["support_version"]}", "constraint_layout" : "com.android.support.constraint:constraint-layout:${version["constraint_version"]}", "junit" : "junit:junit:${version["junit_version"]}", "runner" : "com.android.support.test:runner:${version["runner_version"]}", "espresso_core" : "com.android.support.test.espresso:espresso-core:${version["espresso_core_version"]}" ] } 複製代碼
二、在根目錄build.gradle中增長apply from:"config.gradle"
三、修改module的gradle文件
閉包
android { compileSdkVersion rootProject.ext.android["compileSdkVersion"] defaultConfig { applicationId "net.loosash.learngradle" minSdkVersion rootProject.ext.android["minSdkVersion"] targetSdkVersion rootProject.ext.android["targetSdkVersion"] versionCode rootProject.ext.android["versionCode"] versionName rootProject.ext.android["versionName"] testInstrumentationRunner rootProject.ext.dependencies["androidJUnitRunner"] } ... } ... dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation rootProject.ext.dependencies["kotlin"] implementation rootProject.ext.dependencies["appcompat_v7"] implementation rootProject.ext.dependencies["constraint_layout"] testImplementation rootProject.ext.dependencies["junit"] androidTestImplementation rootProject.ext.dependencies["runner"] androidTestImplementation rootProject.ext.dependencies["espresso_core"] } 複製代碼
一、對於依賴工程或者組件化工程,建議將依賴module中的配置也提取出來。在根目錄新建default.gradle文件。
app
apply plugin: 'com.android.library' android { compileSdkVersion rootProject.ext.android["compileSdkVersion"] defaultConfig { minSdkVersion rootProject.ext.android["minSdkVersion"] targetSdkVersion rootProject.ext.android["targetSdkVersion"] versionCode rootProject.ext.android["versionCode"] versionName rootProject.ext.android["versionName"] testInstrumentationRunner rootProject.ext.dependencies["androidJUnitRunner"] } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) // support implementation rootProject.ext.dependencies["appcompat_v7"] // test testImplementation rootProject.ext.dependencies["junit"] androidTestImplementation rootProject.ext.dependencies["runner"] androidTestImplementation rootProject.ext.dependencies["espresso_core"] } 複製代碼
二、修改使用默認配置的module中的build.gradle文件
apply from:"../default.gradle" android{ // 寫入該模塊特定的配置 resourcePrefix "example_" //給 Module 內的資源名增長前綴, 避免資源名衝突 } dependencies { // 該模塊使用的依賴 } 複製代碼
修改後的好處在於,好比在工程中多處使用support包中的依賴,一次修改版本號便可對所有工程生效,下降了維護成本。
最經常使用的寫法,執行./gradlew hello
打印出hello world
:
task hello{ println 'hello world' } 複製代碼
在android studio建立項目後,會在根目錄的build.gradle中建立clean任務,就是刪除build文件夾下文件
task clean(type: Delete) { delete rootProject.buildDir } 複製代碼
說明:Task建立的時候能夠經過 type: SomeType 指定Type,Type其實就是告訴Gradle,這個新建的Task對象會從哪一個基類Task派生。好比,Gradle自己提供了一些通用的Task,最多見的有Copy 任務。Copy是Gradle中的一個類。當咱們:task myTask(type:Copy)的時候,建立的Task就是一個Copy Task。相似的,有以下寫法:
task copyDocs(type: Copy) { from 'src/main/doc' into 'build/target/doc' } 複製代碼
Gradle中Task存在着依賴關係,在執行過程當中會先執行所依賴的任務,再執行目標任務。
task hello { doLast { println 'Hello world!' } } Task intro(dependsOn: hello) { doLast { println "I'm Gradle" } } 複製代碼
執行結果
> Task :hello Hello world! > Task :intro I'm Gradle 複製代碼
task能夠增長分組和說明
task hello { group 'Custom Group 1' description 'This is the Hello Task' doLast { println 'Hello world!' } } task intro(dependsOn: hello) { group 'Custom Group 2' description 'This is the intro Task' doLast { println "I'm Gradle" } } 複製代碼
輸入./gradlew tasks
Custom Group 1 tasks
--------------------
hello - This is the Hello Task
Custom Group 2 tasks
--------------------
intro - This is the intro Task
複製代碼
在上面已有的Task hello中增長執行處理。
task hello { group 'Custom Group 1' description 'This is the Hello Task' doLast { println 'Hello world!' } } task intro(dependsOn: hello) { group 'Custom Group 2' description 'This is the intro Task' doLast { println "I'm Gradle" } } tasks.hello{ doFirst{ println "prepare to say" } } 複製代碼
執行./gradlew intro
獲得以下輸出
> Task :hello prepare to say Hello world! > Task :intro I'm Gradle 複製代碼
能夠利用這裏的屬性,針對debug版本和release版本對包名進行修改。具體使用以下:
1)對applicationId進行修改,使其能同時安裝debug版本和release版本
android { ... buildTypes { debug { // bebug版本包名爲xxx.xxx.xxx.debug applicationIdSuffix ".debug" } ... } } 複製代碼
2)對release版本進行簽名配置
signingConfigs { release { keyAlias 'xxxx' keyPassword 'xxxxxx' storeFile file('your-keystore-path') storePassword 'xxxxxx' } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' signingConfig signingConfigs.release } } 複製代碼
能夠根據該屬性進行生成不一樣的APK版本,版本能夠攜帶不一樣的特性,最經常使用的就是多渠道打包、根據不一樣環境生成不一樣APK。
productFlavors{ productA{ // 定義特定版本號 // applicationIdSuffix ".a" applicationId "xxx.xxx.xxx.a" // 定義特定版本名稱 versionName "version-a-1.0" // 定義特定的BuildConfig buildConfigField("String","CUSTUMER_CONFIG","xaxaxaxa") } productB{ applicationId "xxx.xxx.xxx.b" versionName "version-b-1.0" buildConfigField("String","CUSTUMER_CONFIG","xbxbxbxb") } } dependencies{ ... // 特定版本依賴 productACompile 'io.reactivex.rxjava2:rxjava:2.0.1' ... } 複製代碼
將生成的apk以自定義命名複製到自定義文件夾
applicationVariants.all { variant -> tasks.all { if ("assemble${variant.name.capitalize()}".equalsIgnoreCase(it.name)) { it.doLast { copy { rename { String fileName -> println "------------${fileName}--------------" fileName.replace(".apk", "-${defaultConfig.versionCode}.apk") } def destPath = file("/Users/solie_h/Desktop/abc/") from variant.outputs.first().outputFile into destPath } } } } } 複製代碼
到這裏,其實其實剛剛是使用Gradle的開始,咱們能夠操做Gradle完成一些本身的需求,但想對外提供Gradle插件還須要一些功夫,接下來還要繼續對android plugin源碼進行研讀,也找一些有Gradle插件的開源項目進行學習,如Replugin、Tinker,進一步提升本身對Gradle的認識。