編輯:關於Android編程
隨著Android 開發環境從Eclipse轉向Android Studio,我們每個人都開始或多或少要接觸gradle腳本,大多數人將gradle看做構建工具,出現問題不知如何著手分析,只能寄希望百度能找到解決方案,其實大可不必。
如果我們把gradle看做編程框架,並理清gradle腳本與gradle對象的關系,通過查閱文檔,不但能清晰理解gradle腳本,而且出現問題再也不用百度,通過查閱文檔就能輕松解決問題。
本文就通過一個最普通的gradle工程,告訴大家如何通過查閱api文檔來看懂Android裡面的gradle 腳本。
一、gradle介紹
gradle基於的語言是groovy,而groovy語言是基於java語言的一個擴展,它完全兼容java語言的類庫,所以在gralde腳本中你完全可以使用你熟悉的java語言來編程。在本文最後我們會給出一個直接在gradle腳本中使用java編程的例子。
由於本文僅僅是讓大家能看懂腳本,而不是自己去編寫,所以groovy和gradle的細節不是本文的重點,有需要的同學自行百度。這裡要提的一點是groovy語言的函數調用是雖然和java類似,但是它是可以省略括號的,特別是當它的最後一個參數是閉包的時候。
println(“aaa” )//這個是可以的,
println“aaa” //這個也沒有問題。
是不是發現了一個秘密:我們在gradle腳本裡全是各種函數調用!!
Gradle api文檔路徑:https://docs.gradle.org/current/dsl/
由於我們也用到了Android 插件,所以我們也需要插件的文檔,這個文檔比較特殊,它是一個git工程,在線浏覽老是顯示html的源碼而非頁面,所以我建議大家將它clone回本地,然後用浏覽器打開,工程地址:https://github.com/google/android-gradle-dsl.git
二、gradle腳本和gradle 類對應關系
稍稍浏覽下gradle的文檔,我們會發現工程裡面的gradle腳本,其實和gradle的對象是對應的:
1.Gradle:全局對象,一次gradle構建對應一個Gradle對象;
2.Project:每個工程都是一個Project對象,它對應一個build.gradle文件;
3.Settings:對應一個Setting.gradle文件;
4.Task:代表要執行的工作,一個Project會有一個或多個Task用於完成構建的工作,Project會通過合理設置Task之間的依賴來組織構建流程,完成最終的構建任務。
三、腳本執行流程與對象生成
1.每次調用gradle 執行時,會先生成一個Gradle對象,裡面保存一些全局的信息;
2.然後會解析setting.gradle,生成Settings對象,一般在setting.gradle中主要是調用include方法,導入工程下的各個子模塊;
3.接下來會執行導入的各個子工程的build.gradle,生成並配置Project對象;
一般在build.gradle中,會調用Project的apply方法,引入插件,在插件中完成定義各種屬性以及創建所需的Task。
四、實例解析
我們以一個Android studio 默認app樣例工程結構為例,工程包含的gradle腳本目錄結構:
1. setting.gradle
setting.gradle只有一行語句:
include ':app'
這裡的include其實是Setting的一個函數,‘:app' 是函數調用的參數。
那我們在setting.gradle裡面還能寫什麼呢?因為setting.gradle對應的是gradle中的Settings對象,那查下Settings的文檔(https://docs.gradle.org/current/dsl/org.gradle.api.initialization.Settings.html),看下它都有哪些方法,哪些屬性,就知道在setting.gradle能寫什麼了:
比如:
include ':Common'
project(':Common').projectDir = new File(settingsDir,'../../SDK/Common/')//include 調用後,生成了一個名為:Common的Project對象,project(':Common')取出這個對象,設置Project的 projectDir屬性。
那projectDir哪裡來的?請看Project類的文檔。
2. build.gradle
接下來我們來看build.gradle,build.gradle對應一個Project對象,而Project本身是一個Build script:
而BuildScript可以包含哪些東西呢?
是不是看到了很多老朋友?buildscript , dependencies 。。
看了這裡我們應該就能明白在build.gradle裡面可以寫哪些東西了。
這裡要說明的一個重要的點是buildscript,任何一個build.gradle執行的時候,會優先處理buildscript,它是來給腳本建立運行環境的,什麼是運行環境?
一般而言就是下載所需要的插件,舉個例子,android工程都需要android插件,它們都是通過下面的方式引入的:
buildscript {
repositories {//告訴如果本地沒有緩存,去哪個遠程倉庫下載插件
mavenCentral()
}
dependencies {
//這就是我們在applyplugin:com.android.application時的插件jar
classpath 'com.android.tools.build:gradle:1.3.0'
// NOTE: Do not place your application dependencies here; theybelong
//in the individual module build.gradle files
}
}
repositories {//告訴如果本地沒有緩存,去哪個遠程倉庫下載編譯時依賴的庫
mavenCentral()
}
上面的repositories, dependencies都是函數調用哦~~
看到上面的兩個一模一樣的repositories 了嗎?他們的作用是不一樣的,在buildscript裡面的那個是插件初始化環境用的,用於設定插件的下載倉庫,而外面的這個是設定工程依賴的一些遠程library的下載倉庫的。
在build.gradle中引入插件,是我們常見的動作:
apply plugin: 'com.android.application'
查閱Project的文檔(https://docs.gradle.org/current/dsl/org.gradle.api.Project.html).apply是Project的一個方法,
3. android相關部分
查閱android插件的文檔:(file:///home/test/android-gradle-dsl/1.5/index.html)
上面就是我們在android{}裡面能寫的所有block的信息,然後在每個block裡面能寫什麼繼續點擊進去察看有哪些屬性和方法就可以了,
比如signingConfigs:
signingConfigs{
release {
storeFile file('../sign/release.jks')
storePassword "5mall@ndro!d"
keyAlias "small"
keyPassword"5mall@ndro!d"
}
}
本文至此可以告一段落,相信大家結合文檔應該能弄明白gradle腳本裡面的每個代碼塊的意義了。
五、Android工程間的父子關系
需要補充的是:上面例子工程的結構中,工程之間是存在父子關系的,比如RxJava-Android-Samples/build.gradle對應的Project是RxJava-Android-Samples/app/build.gradle對應的Project的父親,而在父Project中做的操作是會被子Project繼承的,比如如果在 父Project引入過了android 插件,則在子Project中可直接引用,不需要重寫一個buildscript塊。
1.RxJava-Android-Samples/build.gradle
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.0'
// NOTE: Do not place your application dependencies here; theybelong
//in the individual module build.gradle files
}
}
allprojects {
repositories {
mavenCentral()
}
}
2.RxJava-Android-Samples/app/build.gradle
//直接引用android插件,沒有buildscript准備插件;
apply plugin:'com.android.application'
dependencies {
compile 'com.android.support:support-v13:23.0.1'
compile 'io.reactivex:rxandroid:1.0.1'
// BecauseRxAndroid releases are few and far between, it is recommended you also
// explicitly depend on RxJava'slatest version for bug fixes and new features.
compile 'io.reactivex:rxjava:1.0.14'
compile 'io.reactivex:rxjava-math:1.0.0'
compile 'com.jakewharton.rxbinding:rxbinding:0.2.0'
compile 'com.jakewharton:butterknife:5.1.1'
compile 'com.jakewharton.timber:timber:2.4.2'
compile 'com.squareup.retrofit:retrofit:1.6.1'
compile 'com.squareup.okhttp:okhttp:2.0.0'
compile 'com.squareup.okhttp:okhttp-urlconnection:2.0.0'
compile 'com.alibaba:fastjson:1.2.4'
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3'
}
android {
compileSdkVersion 23
buildToolsVersion '23.0.1'
defaultConfig {
applicationId "com.morihacky.android.rxjava"
minSdkVersion14
targetSdkVersion22
versionCode1
versionName"1.0"
}
buildTypes {
release {
minifyEnabled true
proguardFilesgetDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'
}
}
}
六、在gradle中使用java語言編程的例子
直接上代碼,想學習的就勞心理解啦,編輯器對代碼不太友好啊。
1. build.gradle
buildscript {
repositories{
maven {
url 'http://artifactory.rnd.meizu.com/artifactory/all'
}
}
dependencies {
classpath 'org.aspectj:aspectjtools:1.8.6'
}
}
importorg.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
importorg.aspectj.tools.ajc.Main
dependencies{
compile'org.aspectj:aspectjrt:1.8.6'
}
android.applicationVariants.all{ variant ->
JavaCompile javaCompile = variant.javaCompile
//println javaCompile.properties
javaCompile.doLast {
String[] args = [
"-showWeaveInfo",
"-1.5",
"-inpath", javaCompile.destinationDir.toString(),
"-aspectpath",javaCompile.classpath.asPath,
"-d", javaCompile.destinationDir.toString(),
"-classpath",javaCompile.classpath.asPath,
"-bootclasspath",android.bootClasspath.join(File.pathSeparator)
]
MessageHandler handler = newMessageHandler(true);
new Main().run(args, handler)
def log = project.logger
for (IMessage message : handler.getMessages(null,true)) {
switch (message.getKind()) {
case IMessage.ABORT:
case IMessage.ERROR:
case IMessage.FAIL:
log.error message.message,message.thrown
break;
case IMessage.WARNING:
case IMessage.INFO:
log.info message.message,message.thrown
break;
case IMessage.DEBUG:
log.debug message.message,message.thrown
break;
}
}
println "aspect args : " +args.toString()
}
}
2. groovy
下面這段代碼展示了groovy中變量定義,函數定義,List,Map,Range,閉包。
1)變量定義
defvariable1 = 1 //可以不使用分號結尾
2)函數定義;
StringtestFunction(arg1,arg2){//無需指定參數類型
...
}
3)List變量由[]定義,比如
defaList = [5,'string',true] //List由[]定義,其元素可以是任何對象
4)List存取
aList[100]= 100 //設置第101個元素的值為10
assertaList[100] == 100
5)Map變量由[:]定義,比如
defaMap = ['key1':'value1','key2':true]
Map由[:]定義,注意其中的冒號。冒號左邊是key,右邊是Value。key必須是字符串,value可以是任何對象。另外,key可以用''或""包起來,也可以不用引號包起來。比如:
defaNewMap = [key1:"value",key2:true]//其中的key1和key2默認被處理成字符串"key1"和"key2"
不過Key要是不使用引號包起來的話,也會帶來一定混淆,比如
defkey1="wowo"
defaConfusedMap=[key1:"who am i?"]
//aConfuseMap中的key1到底是"key1"還是變量key1的值“wowo”?顯然,答案是字符串"key1"。如果要是"wowo"的話,則aConfusedMap的定義必須設置成:
defaConfusedMap=[(key1):"who am i?"]
6)Range類,Range類型的變量 由begin值+兩個點+end值表示
def aRange =1..5
//如果不想包含最後一個元素,則
defaRangeWithoutEnd = 1..<5
//包含1,2,3,4這4個元素
7)閉包
defxxx = {paramters -> code} //或者
defxxx = {無參數,純code}
比如:
defgreeting = { "Hello, $it!" }
assertgreeting('Patrick') == 'Hello, Patrick!'
當函數的最後一個參數是閉包的話,可以省略圓括號;
public static
上面這個函數表示針對List的每一個元素都會調用closure做一些處理。這裡的closure,就有點回調函數的感覺。但是,在使用這個each函數的時候,我們傳遞一個怎樣的Closure進去呢?比如:
def iamList =[1,2,3,4,5] //定義一個List
iamList.each{//調用它的each,這段代碼的格式看不懂了吧?each是個函數,圓括號去哪了?
println it
}
上面代碼有以下知識點:
each函數調用的圓括號不見了!原來,Groovy中,當函數的最後一個參數是閉包的話,可以省略圓括號。比如
deftestClosure(int a1,String b1, Closure closure){
//dosomething
closure() //調用閉包
}
那麼調用的時候,就可以免括號!
testClosure4, "test", {
println"i am in closure"
}
開始你的Gradle構建和編程之旅吧~~
有時候因公司需求,要求合並兩個APP 使用裡面的功能。 平台:Studio 小白鼠:二維碼掃描 和自己項目 具體步驟: /** * 1.將解壓後的android
最近在公司,項目不是很忙了,偶爾看見一個兄台在CSDN求助,幫忙要一個自定義的漸變色進度條,我當時看了一下進度條,感覺挺漂亮的,就嘗試的去自定義view實現了一個,廢話不
關於android:layout_weight屬性的詳細解析效果一圖1 上面的效果圖中三個文本框的寬度比為 1:2:3圖2代碼如下所示:<code class=&q
概述類android.graphics.PorterDuffXfermode繼承自android.graphics.Xfermode。在用Android中的Canvas進