1. 程式人生 > >從程式設計的角度理解gradle指令碼﹘﹘Android Studio指令碼構建和程式設計[魅族Degao]

從程式設計的角度理解gradle指令碼﹘﹘Android Studio指令碼構建和程式設計[魅族Degao]

本篇文章由嵌入式企鵝圈原創團隊、魅族資深工程師degao撰寫!

隨著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腳本里全是各種函式呼叫!!

由於我們也用到了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 呼叫後,生成了一個名為:CommonProject物件,project(':Common')取出這個物件,設定ProjectprojectDir屬性。

那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'

3. android相關部分

上面就是我們在android{}裡面能寫的所有block的資訊,然後在每個block裡面能寫什麼繼續點選進去察看有哪些屬性和方法就可以了,

比如signingConfigs:

signingConfigs{

   release {

        storeFile file('../sign/release.jks')

        storePassword "[email protected]!d"

keyAlias "small"

keyPassword"[email protected]!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<T> List<T>each(List<T> self, Closure closure) 

上面這個函式表示針對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構建和程式設計之旅吧~~

更多的嵌入式linux和android、物聯網、汽車自動駕駛等領域原創技術分享請關注微信公眾號:


相關推薦

程式設計角度理解gradle指令碼﹘﹘Android Studio指令碼構建程式設計[Degao]

本篇文章由嵌入式企鵝圈原創團隊、魅族資深工程師degao撰寫!隨著Android 開發環境從Eclipse轉向Android Studio,我們每個人都開始或多或少要接觸gradle指令碼,大多數人將g

理解與配置Android studio中的gradle

使用gradle構建android應用時,你總是需要這樣一個檔案:build.gradle。你應該已經看過這個檔案了,如果沒有看過的話,你現在就可以看一下,它沒有多少內容。它的簡潔性得益於它提供了很多對設定和屬性的預設值。gradle是基於groovy語言的,不過如果只是用

一分鐘學會 ConstraintLayout 之屬性角度理解布局

uic mem app tor normal add 部分 pla ray ConstraintLayout 在 Android 開發中,我們通常是手寫布局,很少會用拖動來寫布局,雖然 ConstraintLayout 在 I/O 上以拖動來展現了各種功能

npm 角度理解 mvn 的 pom.xml

npm ram tro 方法 根據 頁面 e-book maven class 從npm 角度理解 mvn 的 pom.xml pom -- project object model. 用於描述項目的配置: 基礎說明 依賴 如何構建運行 類似 node.js 的 pac

cpu角度理解PCIe續集

概述 上篇文章剩下兩個問題,上電掃描PCIe樹和儲存地址到PCIe地址的對映,本篇文章將對這兩個問題做出解答。本文可能會針對某一款晶片做出詳細流程解答,讀者可以只關注整個流程,具體對映機制和暫存器參考晶片datasheet。上篇文章已經瞭解到如何訪問配置空間,前256Bytes可以

騰訊TEG視覺演算法團隊負責人肖萬鵬:看圖說話—演算法角度理解影象內容

10月28日FMI 2018人工智慧與大資料高峰論壇深圳場圓滿落幕,騰訊TEG視覺演算法團隊負責人肖萬鵬以從演算法角度理解影象內容為主題進行了精彩的分享。   騰訊TEG 視覺演算法團隊負責人肖萬鵬   以下是肖萬鵬演講內容,飛馬網根據現場速記進行了不改變原

原始碼角度理解Java設計模式--責任鏈模式

本文內容思維導圖如下:                                        

原始碼角度理解Java設計模式——裝飾者模式

一、飾器者模式介紹 裝飾者模式定義:在不改變原有物件的基礎上附加功能,相比生成子類更靈活。 適用場景:動態的給一個物件新增或者撤銷功能。 優點:可以不改變原有物件的情況下動態擴充套件功能,可以使擴充套件的多個功能按想要的順序執行,以實現不同效果。 缺點:更多的類,使程式複雜 型別:結構型。 類圖

原始碼角度理解Java設計模式——門面模式

一、門面模式介紹 門面模式定義:也叫外觀模式,定義了一個訪問子系統的介面,除了這個介面以外,不允許其他訪問子系統的行為發生。 適用場景:子系統很複雜時,增加一個介面供外部訪問。 優點:簡化層級間的呼叫,減少依賴,防止風險。 缺點:如果設計不當,增加新的子系統可能需要修改門面類的原始碼,違背了開閉原則

android studio配置sdkgradle中的一些問題

因為最近在家裡也要搞一下android專案,家裡筆記本上一直裝的Eclipse,心血來潮給裝上了as。建立專案,配置sdk,gradle環境,又是一個蛋疼的過程。幾經周折終於build gradle finished之後,為了讓以後不在蛋疼,在此記錄一下問題以及解決辦法。

DFA角度理解KMP演算法

KMP 演算法 KMP(Knuth-Morris-Pratt)演算法在字串查詢中是很高效的一種演算法,假設文字字串長度為n,模式字串長度為m,則時間複雜度為O(m+n),最壞情況下能提供線性時間執行時間保證。 《演算法導論》和其他地方在講解KMP演算法的時候

Gradle問題: Android Studio 如何生成jar,生成jar出錯如何處理

在升級Android Studio到3.2 Canary 11之後, 專案中無法正常打jar包,報錯如下 Execution failed for task ':migutvbox:lint'. > Could not resolve all files

JVM角度理解執行緒

http://blog.csdn.net/iter_zc/article/details/41843595 程式設計技術交流請加QQ群:點選連結加入群【Just Do IT】:https://jq.qq.com/?_wv=1027&k=478lBF3

怎麼匯入github上下載下來的android studio專案

現在從github上面現在的專案大部分都是Android Studio工程,那麼問題來了,從github上面down一個工程下來,怎麼匯入android studio呢? 對剛從eclipse轉Android Studio的童鞋來說,

Android Studio怎麼構建配置檔案(config.gradle)

1、新建config.gradle 在android studio的根專案中新建config.gradle檔案(和settings.gradble同目錄) 開始寫想要的資訊 ext { a

cpu角度理解PCIe

概述 為什麼需要寫這篇文章,當我閱讀《深入淺出SSD》這篇書籍中PCIe章節時發現,本書籍的側重點是放在PCIe控制器和PCIe協議上,從CPU角度理解PCIe知識偏少,本文對下面幾個知識點做出一些補充。 CPU訪問外設暫存器與記憶體編址方式; CPU如何訪問PCIe配置

設計角度理解VR對設計領域的一些影響

最近我們常聽到的就是VR這個名詞,也就是虛擬現實技術。VR是近幾年才走進人們生活的,是利用電腦模擬產生一個三維空間的虛擬世界,提供視覺、聽覺、觸覺等感官的模擬,可以及時、沒有限制地觀察三度空間內的事物,構建一個虛擬世界,以前我們在科幻電影中看到的情景現在可以真實地發生在我們身邊。網上對VR技術的說辭太多了,隨

關於Android Studio中安裝gradle的一些坑

本人從開始用Android Studio到現在已經快一年了吧,在我剛開始用的時候Android Studio還是1.2的版本。當時安裝會因為國內牆的緣故,導致下載SDK步驟卡死無法安裝。 最近的版本似乎都沒出現這個問題,但是還是要吐槽一下最近的2.2.3的更新。這個版本安

Socket程式設計java[簡單易懂],附帶Android Studio 專案聊天例項

伺服器端 package socket_util; import java.io.BufferedReader; import java.io.IOException; import java.

利用Gradle釋出Android Studio專案到JCenter

Android Studio預設的遠端倉庫是JCenter,在bintray網站。本文將介紹如何將studio專案釋出到Jcenter。 獲取使用者名稱和APIkey nobady即為使用者名稱 通過圖中的1、2、3、4步驟可以獲取apikey