相信現(xiàn)在的安卓程序員對(duì)gradle腳本的配置應(yīng)該都或多或少有些了解,例如applicationId、version幔戏、混淆等的基本配置應(yīng)該都是比較熟悉的了,像比較高級(jí)的自定義buildTypes缩挑、productFlavors可能也多多少少了解一些.
但是對(duì)于groovy語言和如何自定義gradle插件,相信很多同學(xué)還是比較陌生的.
作為一個(gè)有理想的安卓程序員,我覺得這種高階的技能還是需要懂的.像一些熱更新弹渔、插件化等高級(jí)技能都會(huì)涉及到groovy代碼的編寫甚至自定義gradle插件.
project.apply方法
我們新建一個(gè)Android Studio項(xiàng)目,得到兩個(gè)build.gradle文件,一個(gè)是項(xiàng)目根目錄下的,一個(gè)是模塊目錄(如app目錄)下的.我們只看模塊目錄下的:
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
applicationId "me.linjw.demo"
minSdkVersion 24
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
這里的第一行代碼指定了com.android.application這個(gè)插件的使用,這個(gè)插件用來構(gòu)建apk項(xiàng)目.
apply plugin: 'com.android.application'
另外比較常見的插件有用于構(gòu)建aar包的com.android.library插件
apply plugin: 'com.android.library'
和用于構(gòu)建jar包的java-library插件
apply plugin: 'java-library'
我們都知道build.gradle使用的是groovy語法,那這個(gè)使用插件的代碼的語法含義又是怎樣的呢?讓我們一起來看看.
第一個(gè)知識(shí)點(diǎn)是我們?cè)趃radle文件里面默認(rèn)使用的都是project這個(gè)對(duì)象的方法或者屬性,也就是說我們的插件配置代碼等價(jià)于:
project.apply plugin: 'com.android.application'
groovy基本語法
接下來我們就要開始學(xué)一些groovy的基本語法了.
我們可以像一般的強(qiáng)類型語言一樣去定義方法,也可以選擇像一些動(dòng)態(tài)語言一樣忽略參數(shù)和返回值類型:
int foo(int x, int y) {
return x + y
}
def foo2(def x, def y) {
return x + y
}
在調(diào)用方法的時(shí)候?yàn)榱撕?jiǎn)潔,你可以選擇省略括號(hào),比如下面的兩行代碼是效果是一樣的,而且我們可以看到,在定義變量的時(shí)候也可以選擇忽略變量的類型:
def x = foo(1,2)
int y = foo 1,2
接下來看看groovy中l(wèi)ist和map的定義方式:
def list = [1, 2, 3, 4]
def map = ['key1': 'val1', key2: 'val2', 3: 'val3', 1.23: 312]
可以看到,map很靈活,key/value都可以是任意的類型,然后在key是字符串的時(shí)候甚至可以直接省略引號(hào).
甚至,在當(dāng)作方法參數(shù)的時(shí)候,我們連map的中括號(hào)也是可以省略的,讓我們來看看groovy代碼是怎樣一步步省略到極致的:
//下面的四行方法是完全等價(jià)的
//不做任何省略
func(['key1': 1, 'key2': 'val2'])
//省略key的雙引號(hào)
func([key1: 1, key2: 'val2'])
//省略map中括號(hào)
func(key1: 1, key2: 'val2')
//省略方法調(diào)用的小括號(hào)
func key1: 1, key2: 'val2'
現(xiàn)在讓我們回過頭來看這行代碼,是不是感覺突然好像有點(diǎn)理解了?
apply plugin: 'com.android.application'
首先它省略了調(diào)用apply的project對(duì)象,然后它省略了key的雙引號(hào),接著又省略了map里面的中括號(hào),最后還省略了方法調(diào)用的小括號(hào)
如果不做任何省略的話,它的完整形式應(yīng)該是:
project.apply(['plugin': 'com.android.application'])
其實(shí)我們也按住ctrl鍵然后用鼠標(biāo)點(diǎn)擊apply,查看方法的聲明:
public interface PluginAware {
...
void apply(Map<String, ?> options);
...
}
可以看到它跳轉(zhuǎn)到了一個(gè)java接口里面,這個(gè)apply其實(shí)是PluginAware這個(gè)接口中的一個(gè)方法,參數(shù)為Map類型.
groovy其實(shí)是一種基于jvm的腳本,它可以直接使用java的代碼.
所以我們可以選擇直接用java編寫插件,也可以選擇使用groovy語言編寫,不過最后groovy也是會(huì)被編譯器編譯成java字節(jié)碼的.
編寫自定義gradle代碼
在gradle中編寫代碼有三種方式
最簡(jiǎn)單的一種是直接在build.gradle文件里面添加我們的代碼
第二種是新建一個(gè)gradle文件,在里面編寫我們的代碼,然后用apply from在build.gradle里面導(dǎo)入我們的代碼
第三中就是編寫我們自己的插件了
第一種方法我們就不說了,直接講第二種.
apply from操作
首先我們需要?jiǎng)?chuàng)建一個(gè)gradle文件,然后在里面寫我們的方法.
例如我在項(xiàng)目根目錄下面新建了一個(gè)mycode.gradle文件,然后寫好代碼:
def add(def x, def y) {
return x + y
}
println('=================')
println(add(1, 2))
println('=================')
然后在app目錄下的build.gradle里面使用apply from操作導(dǎo)入這個(gè)文件:
apply plugin: 'com.android.application'
apply from: '../mycode.gradle'
然后點(diǎn)擊build,就可以看到輸出了:
Executing tasks: [build]
NDK is missing a "platforms" directory.
If you are using NDK, verify the ndk.dir is set to a valid NDK directory. It is currently set to /home/linjw/android/sdk/ndk-bundle.
If you are not using NDK, unset the NDK variable from ANDROID_NDK_HOME or local.properties to remove this warning.
=================
3
=================
NDK is missing a "platforms" directory.
If you are using NDK, verify the ndk.dir is set to a valid NDK directory. It is currently set to /home/linjw/android/sdk/ndk-bundle.
If you are not using NDK, unset the NDK variable from ANDROID_NDK_HOME or local.properties to remove this warning.
...
當(dāng)然我們知道apply是一個(gè)接收Map的方法,我們不用調(diào)用兩次apply方法,也可以直接這么寫,直接在一次調(diào)用中com.android.application插件和mycode.gradle的導(dǎo)入
apply plugin: 'com.android.application', from: '../mycode.gradle'
自定義gradle插件
最高級(jí)的方法就是直接編寫自定義插件了,編寫好的插件可以發(fā)布到j(luò)center或者maven上給人使用.
創(chuàng)建Gradle Module
首先我們需要?jiǎng)?chuàng)建一個(gè)Gradle Module用于編寫gradle插件的代碼.但是Android Studio是沒有辦法直接創(chuàng)建Gradle Module的.
所以我們新建個(gè)普通的apk項(xiàng)目,或者新建個(gè)Android Library module然后再更改下配置將它改成Gradle Module就好
我這里就直接用新建出來的apk項(xiàng)目了.
第一步是進(jìn)入app目錄,將里面的東西全部都刪掉.
1.編寫build.gradle
然后新建一個(gè)在app目錄下新建一個(gè)build.gradle文件,寫入代碼:
apply plugin: 'groovy'
dependencies {
compile gradleApi()
compile localGroovy()
}
2.編寫代碼
接著在app目錄下面新建src目錄,然后進(jìn)入src目錄新建main目錄,然后再進(jìn)入main繼續(xù)新建groovy目錄
最后在groovy目錄中根據(jù)包名新建目錄層級(jí),并且新建MyPlugin.groovy文件用于編寫我們的插件代碼.
我的包名是me.linjw.plugin,所以目錄結(jié)構(gòu)如下:
插件都需要實(shí)現(xiàn)Plugin<Project>接口,然后編寫自己代碼.代碼如下:
package me.linjw.plugin
import org.gradle.api.Plugin
import org.gradle.api.Project
public class MyPlugin implements Plugin<Project> {
def add(def x, def y) {
return x + y
}
void apply(Project project) {
println("=======MyPlugin========")
println(add(1, 2))
println("===============")
}
}
3.注冊(cè)插件
上面我們已經(jīng)編寫好了我們的插件了,接下來的事情就是告訴gradle哪個(gè)是我們的插件類.
main目錄下新建resources目錄,然后在resources目錄里面再新建META-INF目錄,再在META-INF里面新建gradle-plugins目錄.最后在gradle-plugins目錄里面新建properties文件.
這個(gè)properties文件的名字就是你插件的名字了,例如之前看到的com.android.application媒熊、com.android.library
我這邊的名字為me.islinlw.plugin.demo.properties
接著在properties文件里面配置我們的插件類:
implementation-class=me.linjw.plugin.MyPlugin
發(fā)布插件到本地maven
這個(gè)時(shí)候其實(shí)點(diǎn)擊build已經(jīng)可以在app/build/libs目錄下看到我們的插件被編譯成app.jar了
但是需要先發(fā)布出去別人才能使用,一般可以發(fā)布到公司內(nèi)部或者公網(wǎng)的倉(cāng)庫(kù)如jcenter等.我們這邊由于是demo,可以先選擇發(fā)布到電腦的本地倉(cāng)庫(kù).
我們修改下build.gradle:
apply plugin: 'groovy'
apply plugin: 'maven'
dependencies {
compile gradleApi()
compile localGroovy()
}
repositories {
mavenCentral()
}
group='me.islinjw.plugin'
version='1.0.0'
uploadArchives {
repositories {
mavenDeployer {
repository(url: uri('/home/linjw/workspace/LocalMaven'))
}
}
}
然后點(diǎn)擊uploadArchives,就可以將插件發(fā)布到/home/linjw/workspace/LocalMaven
使用插件
讓我們打開一個(gè)項(xiàng)目來驗(yàn)證下.
首先在項(xiàng)目根目錄的build.gradle的buildscript.repositories里面配置本地倉(cāng)庫(kù)的路徑,并且在buildscript.dependencies配置插件依賴:
最后在app目錄下的build.gradle里面使用我們的插件:
就可以點(diǎn)擊build看到輸出了
16:57:12: Executing task 'build'...
Executing tasks: [build]
NDK is missing a "platforms" directory.
If you are using NDK, verify the ndk.dir is set to a valid NDK directory. It is currently set to /home/linjw/android/sdk/ndk-bundle.
If you are not using NDK, unset the NDK variable from ANDROID_NDK_HOME or local.properties to remove this warning.
=======MyPlugin========
3
===============
NDK is missing a "platforms" directory.
If you are using NDK, verify the ndk.dir is set to a valid NDK directory. It is currently set to /home/linjw/android/sdk/ndk-bundle.
If you are not using NDK, unset the NDK variable from ANDROID_NDK_HOME or local.properties to remove this warning.
修改插件的ArtifactID
我們看到添加依賴的時(shí)候,插件的ArtifactID其實(shí)是app,這個(gè)又要怎么修改呢?
classpath 'me.islinjw.plugin:app:1.0.0'
回到我們的插件項(xiàng)目的根目錄,修改settings.gradle,將模塊名改成DemoPlugin:
//原來是include ':app'
include ':DemoPlugin'
然后將我們的app目錄改名成DemoPlugin
最后再發(fā)布一次,就修改完成了
于是依賴就變成了
classpath 'me.islinjw.plugin:DemoPlugin:1.0.0'