Gradle系列相關文章:
1、Gradle理論與實踐一:Gradle入門
2、Gradle理論與實踐二:Groovy介紹
3、Gradle理論與實踐三:Gradle構建腳本基礎
4、Gradle理論與實踐四:自定義Gradle插件
5、Gradle配置subprojects和allprojects的區別:subprojects和allprojects的區別
Gradle插件
Gradle可以認爲是一個框架,負責定義流程和規則。而具體的編譯工作則是通過插件的方式來完成的。比如編譯 Java 有 Java 插件,編譯 Groovy 有 Groovy 插件,編譯 Android APP 有 Android APP 插件,編譯 Android Library 有 Android Library 插件。在Gradle中一般有兩種類型的插件,腳本插件和二進制插件。使用插件方式可以使得同一邏輯在項目中複用,也可以針對不同項目做個性化配置,只要插件代碼支持即可。
一、Java Gradle插件
Java插件引入方式:
- apply plugin: 'java'
Java插件約定src/main/java
爲我們項目源代碼存放位置;src/main/resources
爲資源存放位置;src/test/java
爲我們單元測試用例存放目錄;src/test/resources
存放我們單元測試中資源存放位置。
java插件引入了一個概念叫做SourceSets,通過修改SourceSets中的屬性,可以指定哪些源文件(或文件夾下的源文件)要被編譯,哪些源文件要被排除。Gradle就是通過它實現Java項目的佈局定義。
默認配置:
android {
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
jniLibs.srcDirs = ['libs']
}
}
如果想修改源代碼的目錄以及多個resource的目錄,可以通過下面來設置:
sourceSets {
main {
java.srcDirs = ['other/java']
res.srcDirs =
[
'src/main/res/',
'src/main/res/extra'
]
}
}
更多設置請移步官網:
https://developer.android.com/studio/build/build-variants
二、Android Gradle插件
Android其實就是Gradle的一個第三方插件,Android Gradle和Android Studio完美無縫搭配的新一代構建系統。
- APP插件id :com.android.application
- Library插件id:com.android.library
- Test插件id:com.android.test
2.1、應用Android Gradle插件
上面說了Android Gradle是Gradle的一個三方插件,託管在jcenter上,如果要使用,必須知道他們的插件id,另外還要配置他們依賴的classpath,在根目錄的build.gradle中配置如下:
buildscript {
repositories {
//代碼倉庫
jcenter()
}
dependencies {
//Android Gradle插件版本
classpath 'com.android.tools.build:gradle:2.3.3'
}
}
配置代碼倉庫爲jcenter,當編譯項目時,Gradle會去jcenter倉庫中尋找Android Gradle對應版本的依賴,以上配置好後,就可以使用Android Gradle插件了,在我們app目錄的build.gradle下:
apply plugin: 'com.android.application
android {
compileSdkVersion 26
buildToolsVersion "26.0.3"
defaultConfig {
applicationId "org.ninetripods.qrcode"
minSdkVersion 14
targetSdkVersion 26
versionCode 1
versionName "1.0"
}
signingConfigs {
release {
keyAlias 'xxx'
keyPassword 'xxx'
storeFile file('xxx.jks')
storePassword 'xxx'
}
}
buildTypes {
release {
minifyEnabled false
signingConfig signingConfigs.release
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
android{}是Android Gradle插件提供的一個擴展類型,可以讓我們自定義Android Gradle工程。
compileSdkVersion: compileSdkVersion告訴Gradle用哪個Android SDK版本編譯應用
1、應用想兼容新版本、使用了新版本API,此時就必須使用新版本及以上版本編譯,否則就會編譯報錯;
2、如果使用了新版本的Support Library,此時也必須使用新版本及以上版本編譯buildToolsVersion:構建該Android工程所用構建工具(aapt, dx, renderscript compiler, etc...)的版本,一般google在發佈新的SDK時,會同時發佈對應的buildToolsVersion,更詳細的見:https://stackoverflow.com/questions/24521017/android-gradle-buildtoolsversion-vs-compilesdkversion
PS:Android Studio 3.0以上時,buildToolsVersion不是必須要寫的了。defaultConfig: 默認配置,它是一個ProductFlavor。ProductFlavor允許我們在打多渠道包時根據不同的情況生成不同的APK包。即如果不在ProductFlavor中單獨配置的話,那麼會使用defaultConfig的默認配置。
minSdkVersion: 最低支持的Android系統API Level
targetSdkVersion: App基於哪個Android版本開發的
versionCode: App應用內部版本號,一般用來控制App升級
versionName: App應用的版本名稱,即我們發佈的App版本,一般用戶可以看到。
minSdkVersion、targetSdkVersion、compileSdkVersion三者的關係:
minSdkVersion <= targetSdkVersion <= compileSdkVersion。
理想狀態是:
minSdkVersion(lowest possible) <= targetSdkVersion == compileSdkVersion(latest SDK)。
三、自定義Gradle插件
編寫自定義Gradle插件源代碼的有下面三個地方:
3.1、Build script
可以在構建腳本中直接編寫自定義插件的源代碼。這樣做的好處是插件可以自動編譯幷包含在構建腳本的classpath中,不需要再去聲明。然而,這個自定義插件在構建腳本之外是不可見的,因此這種方式實現的插件在構建腳本之外是不能複用。
舉個例子,在根目錄下的build.gradle中寫入:
//build.gradle
class GreetingPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
//新建task hello
project.task('hello') {
doLast {
println 'Hello from the GreetingPlugin'
}
}
}
}
//引入插件
apply plugin: GreetingPlugin
執行結果
./gradlew hello
Hello from the GreetingPlugin
3.2、buildSrc project
在項目的根目錄下新建一個buildSrc/src/main/groovy的目錄,將自定義插件的源代碼放入此目錄中,Gradle將負責編譯和測試插件,並使其在構建腳本的classpath中可見。這個插件對於項目中所有的build script都是可見的,但是在構建之外是不可見的,此構建方式不能再其他項目中複用。
舉例:在rootProjectDir/buildSrc/src/main/groovy目錄下新建了一個插件類,如下:
GreetingExtensionPlugin.groovy中代碼如下:
//GreetingExtensionPlugin.groovy
package com
import org.gradle.api.Plugin
import org.gradle.api.Project
class GreetingExtensionPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
// Add the 'greeting' extension object
def extension = project.extensions.create('greeting', GreetingExtension)
// Add a task that uses configuration from the extension object
project.tasks.create('buildSrc') {
doLast {
println "${extension.message} from ${extension.greeter}"
println project.greeting
}
}
}
}
class GreetingExtension {
String message
String greeter
}
定義好的插件就可以在項目中所有的build.gradle中使用了:
//build.gradle
apply plugin: GreetingExtensionPlugin
greeting {
message = 'hello'
greeter = 'GreetingExtensionPlugin'
}
執行結果:
./gradlew buildSrc
hello from GreetingExtensionPlugin
com.GreetingExtension_Decorated@42870556
- 擴展屬性:自定義插件代碼中有一句
def extension = project.extensions.create('greeting', GreetingExtension)
,可以用來擴展屬性,可以理解爲往GreetingExtension中新加了一個屬性。創建完成後,可以通過project.greeting來獲取擴展屬性的實例。使用這種方式來給插件傳遞參數。
3.3、Standalone project
上面兩種自定義插件都只能在自己的項目中使用,如果想在其他項目中也能複用,可以創建一個單獨的項目並把這個項目發佈成一個JAR,這樣多個項目中就可以引入並共享這個JAR。通常這個JAR包含一些插件,或者將幾個相關的task捆綁到一個庫中,或者是插件和task的組合, Standalone project創建步驟:
- 在Android Studio的rootProject目錄下新建一個Module,類型隨便選一個就行(如 Android Module),後面會有大的改動。(也可以選擇IDEA來開發,IDEA中可以直接創建groovy組件)
- 清空Module目錄下build.gradle中的所有內容,刪除其他所有文件
- 在Module中創建
src/main/groovy
的目錄,然後再創建包名文件夾。在main目錄下再新建resources/META-INF/gradle-plugins
目錄,在這個目錄下編寫一個和插件id名字相同的.properties
文件,這樣Gradle就可以找到插件實現了。
舉個例子,下面的代碼實現了在build目錄中新建個文本文件,並寫入文本的功能。
1、在src/main/groovy
下新建/com/fastgo/plugin
目錄並創建一個名爲CustomPlugin.groovy
的文件:
package com.fastgo.plugin
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.Plugin
import org.gradle.api.tasks.TaskAction
class CustomPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
project.tasks.create('writeToFile', CustomPluginTask) {
destination = { project.greetingFile }
doLast {
println project.file(destination).text
}
}
}
}
class CustomPluginTask extends DefaultTask {
def destination
File getDestination() {
//創建路徑爲destination的file
project.file(destination)
}
@TaskAction
def greet() {
def file = getDestination()
file.parentFile.mkdirs()
//向文件中寫入文本
file.write('hello world')
}
}
上面的代碼中在插件的apply(Project project)
中創建了名爲writeToFile
的Task,並依賴於CustomPluginTask
。CustomPluginTask
中定義了一個destination
路徑,並通過project.file(destination)
創建創建一個路徑爲destination
的文件,並往文件中寫入文本。
注意:別忘了引入 package com.fastgo.plugin
,否則最後生成後會提示找不到插件。
2、創建resources/META-INF/gradle-plugins/com.fastgo.plugin.properties
文件,並在文件裏寫入:
implementation-class=com.fastgo.plugin.CustomPlugin
3、在build.gradle
中寫入:
plugins {
id 'groovy'
id 'maven-publish'
id 'maven'
}
dependencies {
implementation gradleApi()
implementation localGroovy()
}
repositories {
mavenCentral()
}
group = 'com.fastgo'
version = '1.0-test'
publishing {
repositories {
maven {
url = uri("$rootDir/repo")
}
}
publications {
maven(MavenPublication) {
from components.java
}
}
}
本例中是通過url = uri("$rootDir/repo")
將代碼打包到maven的本地倉庫中,如果想上傳到遠端,換成遠端鏈接即可。通過設置GroupId、ArtifactId、Version來保證插件的唯一性。
- GroupId:
group = 'com.fastgo'
- ArtifactId:
plugin
- Version:
version = '1.0-test'
最終的文件如下:
插件編寫完成後,在Android Studio的右上角打開Gradle,執行:plugin
分組中的publish
命令,執行完成後,會在項目根目錄下生成repo倉庫:
4、在項目根目錄的
build.gradle
中引用插件:
buildscript {
repositories {
maven {
url = uri("$rootDir/repo")
}
------其他-------
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
classpath 'com.fastgo:plugin:1.0-test'
}
}
//通過插件id找到插件
apply plugin: 'com.fastgo.plugin'
ext.greetingFile="$buildDir/hello.txt"
5、驗證效果
mqdeMacBook-Pro:AndroidStudy mq$ ./gradlew -q writeToFile
hello world
執行writeToFile的task輸出了我們寫入的文本,再看看build目錄下是否有我們想要的文件:
可以看到在build
目錄下生成了hello.txt
文件並往裏面寫入了設置的文本,說明我們編寫的插件被成功引入了。
四、源碼地址
上述例子源碼已上傳至:https://github.com/crazyqiang/AndroidStudy
五、引用
【1】https://docs.gradle.org/current/userguide/custom_plugins.html
【2】https://docs.gradle.org/4.1/userguide/publishing_maven.html
【3】https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html