Android.Gradle Build Process

http://www.itdecent.cn/p/7c288a17cda8

總的來說,Android的系統(tǒng)體系結(jié)構(gòu)分為四層,自頂向下分別是:

  • 應(yīng)用程序(Applications)
  • 應(yīng)用程序框架(Application Frameworks)
  • 系統(tǒng)運行庫與Android運行環(huán)境(Libraris & Android Runtime)
  • Linux內(nèi)核(Linux Kernel)

安卓系統(tǒng)結(jié)構(gòu)示意圖

image.png

下面對每層進(jìn)行詳細(xì)說明

1. 應(yīng)用程序(Applications)

Android會同一系列核心應(yīng)用程序包一起發(fā)布,該應(yīng)用程序包包括email客戶端,SMS短消息程序,日歷,地圖,瀏覽器,聯(lián)系人管理程序等。所有的應(yīng)用程序都是使用JAVA語言編寫的。通常開發(fā)人員就處在這一層。

2. 應(yīng)用程序框架(Application Frameworks)

提供應(yīng)用程序開發(fā)的各種API進(jìn)行快速開發(fā),也即隱藏在每個應(yīng)用后面的是一系列的服務(wù)和系統(tǒng),大部分使用Java編寫,所謂官方源碼很多也就是看這里,其中包括:

  • 豐富而又可擴(kuò)展的視圖(Views),可以用來構(gòu)建應(yīng)用程序, 它包括列表(lists),網(wǎng)格(grids),文本框(text boxes),按鈕(buttons), 甚至可嵌入的web瀏覽器。
  • 內(nèi)容提供器(Content Providers)使得應(yīng)用程序可以訪問另一個應(yīng)用程序的數(shù)據(jù)(如聯(lián)系人數(shù)據(jù)庫), 或者共享它們自己的數(shù)據(jù)
  • 資源管理器(Resource Manager)提供 非代碼資源的訪問,如本地字符串,圖形,和布局文件( layout files )。
  • 通知管理器 (Notification Manager) 使得應(yīng)用程序可以在狀態(tài)欄中顯示自定義的提示信息。
  • 活動管理器( Activity Manager) 用來管理應(yīng)用程序生命周期并提供常用的導(dǎo)航回退功能。

3. 系統(tǒng)運行庫與Android運行環(huán)境(Libraris & Android Runtime)

1) 系統(tǒng)運行庫

Android 包含一些C/C++庫,這些庫能被Android系統(tǒng)中不同的組件使用。它們通過 Android 應(yīng)用程序框架為開發(fā)者提供服務(wù)。以下是一些核心庫:

  • Bionic系統(tǒng) C 庫 - 一個從 BSD 繼承來的標(biāo)準(zhǔn) C 系統(tǒng)函數(shù)庫( libc ), 它是專門為基于 embedded linux 的設(shè)備定制的。
  • 媒體庫 - 基于 PacketVideo OpenCORE;該庫支持多種常用的音頻、視頻格式回放和錄制,同時支持靜態(tài)圖像文件。編碼格式包括MPEG4, H.264, MP3, AAC, AMR, JPG, PNG 。
  • Surface Manager - 對顯示子系統(tǒng)的管理,并且為多個應(yīng)用程序提 供了2D和3D圖層的無縫融合。這部分代碼
  • Webkit,LibWebCore - 一個最新的web瀏覽器引擎用,支持Android瀏覽器和一個可嵌入的web視圖。鼎鼎大名的 Apple Safari背后的引擎就是Webkit
  • SGL - 底層的2D圖形引擎
  • 3D libraries - 基于OpenGL ES 1.0 APIs實現(xiàn);該庫可以使用硬件 3D加速(如果可用)或者使用高度優(yōu)化的3D軟加速。
  • FreeType -位圖(bitmap)和矢量(vector)字體顯示。
  • SQLite - 一個對于所有應(yīng)用程序可用,功能強勁的輕型關(guān)系型數(shù)據(jù)庫引擎。
  • 還有部分上面沒有顯示出來的就是硬件抽象層。其實Android并非講所有的設(shè)備驅(qū)動都放在linux內(nèi)核里面,而是實現(xiàn)在userspace空間,這么做的主要原因是GPL協(xié)議,Linux是遵循該 協(xié)議來發(fā)布的,也就意味著對 linux內(nèi)核的任何修改,都必須發(fā)布其源代碼。而現(xiàn)在這么做就可以避開而無需發(fā)布其源代碼,畢竟它是用來賺錢的。 而 在linux內(nèi)核中為這些userspace驅(qū)動代碼開一個后門,就可以讓本來userspace驅(qū)動不可以直接控制的硬件可以被訪問。而只需要公布這個 后門代碼即可。一般情況下如果要將Android移植到其他硬件去運行,只需要實現(xiàn)這部分代碼即可。包括:顯示器驅(qū)動,聲音,相機(jī),GPS,GSM等等

2) Android運行環(huán)境

該核心庫提供了JAVA編程語言核心庫的大多數(shù)功能。
每一個Android應(yīng)用程序都在它自己的進(jìn)程中運 行,都擁有一個獨立的Dalvik虛擬 機(jī)實例。Dalvik被設(shè)計成一個設(shè)備可以同時高效地運行多個虛擬系統(tǒng)。 Dalvik虛擬機(jī)執(zhí)行(.dex)的Dalvik可執(zhí)行文件,該格式文件針對小內(nèi)存使用做了 優(yōu)化。同時虛擬機(jī)是基于寄存器的,所有的類都經(jīng)由JAVA編譯器編譯,然后通過SDK中 的 "dx" 工具轉(zhuǎn)化成.dex格式由虛擬機(jī)執(zhí)行。

4. Linux內(nèi)核(Linux Kernel)

Android的核心系統(tǒng)服務(wù)依賴于Linux 2.6 內(nèi)核,如安全性,內(nèi)存管理,進(jìn)程管理, 網(wǎng)絡(luò)協(xié)議棧和驅(qū)動模型。 Linux 內(nèi)核也同時作為硬件和軟件棧之間的抽象層。其外還對其做了部分修改,主要涉及兩部分修改:

  1. Binder (IPC):提供有效的進(jìn)程間通信,雖然linux內(nèi)核本身已經(jīng)提供了這些功能,但Android系統(tǒng)很多服務(wù)都需要用到該功能,為了某種原因其實現(xiàn)了自己的一套。
  2. 電源管理:主要是為了省電,畢竟是手持設(shè)備嘛,低耗電才是我們的追求。

注:最后附上原博連接懶蟲一個V:android系統(tǒng)體系結(jié)構(gòu),關(guān)于谷歌Android源碼的目錄結(jié)構(gòu)并未一并貼出可在原博查閱

1.概況

  • Android APK是如何來的呢?
    懷著這個問題去查資料,發(fā)現(xiàn)了下邊這張圖。
android-build9a.png
  • 由android的項目經(jīng)過編譯和打包,形成了:

    1. .dex 文件
    2. resources.arsc
    3. uncompiled resources
    4. AndroidManifest.xml

    解壓了一個普通的apk文件,解壓出來的文件如下:

    [圖片上傳失敗...(image-3f4e80-1520594989213)]

    classes.dex 是.dex文件。
    resources.arsc是resources resources文件。
    AndroidManifest.xml是AndroidManifest.xml文件。
    res是uncompiled resources。
    META-INF是簽名文件夾。

  • META-INF其中有三個文件:

    [圖片上傳失敗...(image-471345-1520594989213)]

    MANIFEST.MF文件
    版本號以及每一個文件的哈希值(BASE64)。包括資源文件。這個是對每個文件的整體進(jìn)行SHA1(hash)。

Manifest-Version: 1.0
Built-By: Generated-by-ADT
Created-By: Android Gradle 2.2.0
Name: res/drawable-xhdpi-v4/abc_scrubber_control_to_pressed_mtrl_005.png
SHA1-Digest: I9s6aQ5VyOLrNo4odqSij549Oyo=
Name: res/drawable-mdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png
SHA1-Digest: D6dilO+UMcglambujyMOhNbLZuY=
……

CERT.SF
這個是對每個文件的頭3行進(jìn)行SHA1 hash。

Signature-Version: 1.0
X-Android-APK-Signed: 2
SHA1-Digest-Manifest: QxOfCCAuQtZnHh0YRNnoxmiHT80=
Created-By: 1.0 (Android)
Name: res/drawable-xhdpi-v4/abc_scrubber_control_to_pressed_mtrl_005.png
SHA1-Digest: I9s6aQ5VyOLrNo4odqSij549Oyo=
Name: res/drawable-mdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png
SHA1-Digest: D6dilO+UMcglambujyMOhNbLZuY=
……

CERT.RSA
這個文件保存了簽名和公鑰證書。

2. 具體打包過程

[圖片上傳失敗...(image-cbaba7-1520594989214)]

2.1 aapt階段

  • 使用aapt來打包res資源文件,生成R.java、resources.arsc和res文件(二進(jìn)制 & 非二進(jìn)制如res/raw和pic保持原樣)

  • res目錄有9種目錄
    --animator。這類資源以XML文件保存在res/animator目錄下,用來描述屬性動畫。
    --anim。這類資源以XML文件保存在res/anim目錄下,用來描述補間動畫。
    --color。這類資源以XML文件保存在res/color目錄下,用描述對象顏色狀態(tài)選擇子。
    --drawable。這類資源以XML或者Bitmap文件保存在res/drawable目錄下,用來描述可繪制對象。例如,我們可以在里面放置一些圖片(.png, .9.png, .jpg, .gif),來作為程序界面視圖的背景圖。注意,保存在這個目錄中的Bitmap文件在打包的過程中,可能會被優(yōu)化的。例如,一個不需要多于256色的真彩色PNG文件可能會被轉(zhuǎn)換成一個只有8位調(diào)色板的PNG面板,這樣就可以無損地壓縮圖片,以減少圖片所占用的內(nèi)存資源。
    --layout。這類資源以XML文件保存在res/layout目錄下,用來描述應(yīng)用程序界面布局。
    --menu。這類資源以XML文件保存在res/menu目錄下,用來描述應(yīng)用程序菜單。
    --raw。這類資源以任意格式的文件保存在res/raw目錄下,它們和assets類資源一樣,都是原裝不動地打包在apk文件中的,不過它們會被賦予資源ID,這樣我們就可以在程序中通過ID來訪問它們。例如,假設(shè)在res/raw目錄下有一個名稱為filename的文件,并且它在編譯的過程,被賦予的資源ID為R.raw.filename,那么就可以使用以下代碼來訪問它:

Resources res = getResources();  
InputStream is = res .openRawResource(R.raw.filename);  

--values。這類資源以XML文件保存在res/values目錄下,用來描述一些簡單值,例如,數(shù)組、顏色、尺寸、字符串和樣式值等,一般來說,這六種不同的值分別保存在名稱為arrays.xml、colors.xml、dimens.xml、strings.xml和styles.xml文件中。
--xml。這類資源以XML文件保存在res/xml目錄下,一般就是用來描述應(yīng)用程序的配置信息。

  • R.java文件

    [圖片上傳失敗...(image-3c05df-1520594989213)]

    這就是R.java的源代碼,里面擁有很多個靜態(tài)內(nèi)部類,比如layout,string等。
    每當(dāng)有這種資源添加時,就在R.java文件中添加一條靜態(tài)內(nèi)部類里的靜態(tài)常量類成員,且所有成員都是int類型。

    [圖片上傳失敗...(image-b7f577-1520594989213)]

    里面的資源可以有兩種方法引用:
    1.在java程序中引用資源按照java的語法來引用即:R.resource_type.resource_

    name注意:resource_name不需要文件的后綴名
    2.在XML文件中引用資源格式:@[package:]type/name

  • resources.arsc文件
    resources.arsc這個文件記錄了所有的應(yīng)用程序資源目錄的信息,包括每一個資源名稱、類型、值、ID以及所配置的維度信息。我們可以將這個resources.arsc文件想象成是一個資源索引表,這個資源索引表在給定資源ID和設(shè)備配置信息的情況下,能夠在應(yīng)用程序的資源目錄中快速地找到最匹配的資源。

2.2 aidl階段

  • AIDL (Android Interface Definition Language), Android接口定義語言,Android提供的IPC (Inter Process Communication,進(jìn)程間通信)的一種獨特實現(xiàn)。
    這個階段處理.aidl文件,生成對應(yīng)的Java接口文件。

2.3 Java Compiler階段

  • 通過Java Compiler編譯R.java、Java接口文件、Java源文件,生成.class文件。

2.4 dex階段

  • 通過dex命令,將.class文件和第三方庫中的.class文件處理生成classes.dex。

2.5 apkbuilder階段

  • 將classes.dex、resources.arsc、res文件夾(res/raw資源被原裝不動地打包進(jìn)APK之外,其它的資源都會被編譯或者處理)、Other Resources(assets文件夾)、AndroidManifest.xml打包成apk文件。
    注意
    res/raw和assets的相同點:
    1.兩者目錄下的文件在打包后會原封不動的保存在apk包中,不會被編譯成二進(jìn)制。
    res/raw和assets的不同點:
    1.res/raw中的文件會被映射到R.java文件中,訪問的時候直接使用資源ID即R.id.filename;assets文件夾下的文件不會被映射到R.java中,訪問的時候需要AssetManager類。
    2.res/raw不可以有目錄結(jié)構(gòu),而assets則可以有目錄結(jié)構(gòu),也就是assets目錄下可以再建立文件夾

2.6 Jarsigner階段

  • 對apk進(jìn)行簽名,可以進(jìn)行Debug和Release 簽名。

2.7 zipalign階段

  • release mode 下使用 aipalign進(jìn)行align,即對簽名后的apk進(jìn)行對齊處理。
    Zipalign是一個android平臺上整理APK文件的工具,它對apk中未壓縮的數(shù)據(jù)進(jìn)行4字節(jié)對齊,對齊后就可以使用mmap函數(shù)讀取文件,可以像讀取內(nèi)存一樣對普通文件進(jìn)行操作。如果沒有4字節(jié)對齊,就必須顯式的讀取,這樣比較緩慢并且會耗費額外的內(nèi)存。
    在 Android SDK 中包含一個名為 “zipalign” 的工具,它能夠?qū)Υ虬蟮?app 進(jìn)行優(yōu)化。 其位于 SDK 的 build-tools 目錄下, 例如: D:\Develop\Android\sdk\build-tools\23.0.2\zipalign.exe

是什么?

在語法上是基于Groovy語言的(Groovy 是一種基于JVM的敏捷開發(fā)語言,可以簡單的理解為強類型語言java的弱類型版本),在項目管理上是基于Ant和Maven概念的項目自動化建構(gòu)工具。

基礎(chǔ)知識準(zhǔn)備

Java基礎(chǔ),命令行使用基礎(chǔ)
官方文檔https://docs.gradle.org/current/dsl/
** Gradle使用指南:** https://gradle.org/docs/current/userguide/userguide
Android插件文檔https://github.com/google/android-gradle...
AndroidGradle使用文檔http://tools.android.com/tech-docs/new-build-system/user-guide
Groovy基礎(chǔ): http://attis-wong-163-com.iteye.com/blog/1239819
Groovy閉包的Delegate機(jī)制http://www.cnblogs.com/davenkin/p/gradle-learning-3.html

搭建Gradle運行環(huán)境

  1. Gradle 運行依賴JVM,也就是java運行的環(huán)境。所以要安裝jdk和jre,好像目前的Gradle的運行環(huán)境要求jdk的版本在1.6以上,應(yīng)該的,現(xiàn)在jdk都到1.8了。
  2. 然后到Gradle官網(wǎng)現(xiàn)在Gradle的壓縮包。地址,這個頁面里面又兩種方式,一種手動安裝,一種通過腳本安裝。我一般喜歡自己動手,這樣將來清理起來比較方便。
  3. 下載壓縮包后,解壓,然后配置環(huán)境變量,手動安裝過jdk的人應(yīng)該都配置環(huán)境變量很熟了吧。每個平臺下配置環(huán)境變量的方式不一樣

MacOS 下配置。在~/.bash_profile中添加如下代碼

#gradle  注意gradle-2.14.1是自己解壓的路徑
export GRADLE_HOME=${HOME}/gradle-2.14.1
PATH=${PATH}:${GRADLE_HOME}/bin
export PATH

保存后在終端輸入source ~/.bash_profile回車執(zhí)行讓剛剛的配置生效。然后命令行輸入gradle -v查看是否安裝成功。

$ gradle -v

------------------------------------------------------------
Gradle 2.14.1
------------------------------------------------------------

Build time:   2016-07-18 06:38:37 UTC
Revision:     d9e2113d9fb05a5caabba61798bdb8dfdca83719

Groovy:       2.4.4
Ant:          Apache Ant(TM) version 1.9.6 compiled on June 29 2015
JVM:          1.8.0_111 (Oracle Corporation 25.111-b14)
OS:           Mac OS X 10.12.2 x86_64

弄一個HelloWorld看看

創(chuàng)建一個test_gralde文件夾。然后在文件夾里面創(chuàng)建一個build.gradle文件。注意文件名不要亂起。在build.gradle中添加如下代碼:

task helloworld{
    doLast{
        println'Hello World!'
    }
}
#后者等同于下面的代碼,
task helloworld2 <<{
    println "Hello World!"
}

然后來運行一下:

liuqiangs-MacBook-Pro:test_gralde liuqiang$ gradle helloworld
:helloworld
Hello World!

BUILD SUCCESSFUL

Total time: 1.52 secs

This build could be faster, please consider using the Gradle Daemon: https://docs.gradle.org/2.14.1/userguide/gradle_daemon.html

我們分析一下執(zhí)行步驟。build.gradle是Gradle默認(rèn)的構(gòu)建腳本文件,執(zhí)行Gradle命令的時候,會默認(rèn)加載當(dāng)前目錄下的build.gradle腳本文件,當(dāng)然你也可以通過 -b 參數(shù)指定想要加載執(zhí)行的文件。這只是個最簡單的task例子,后面詳細(xì)介紹task的常見定義。

這個構(gòu)建腳本定義一個任務(wù)(Task),任務(wù)名字叫helloworld,并且給任務(wù)helloworld添加了一個動作,官方名字是Action,閱讀Gradle源代碼你會到處見到它,其實他就是一段Groovy語言實現(xiàn)的閉包,doLast就意味著在Task執(zhí)行完畢之后要回調(diào)doLast的這部分閉包的代碼實現(xiàn)。第二個方法中的“<<”表示向helloworld中加入執(zhí)行代碼。至于語法部分,基本是Groovy語法(包括一些語法糖,也就是寫簡寫方式,如果寫個JavaScript或者Python會好理解一些,但是還是建議去讀一下groovy的基本語法),加上一些DSL(domain specific language)的約定。

執(zhí)行流程和基本術(shù)語

和Maven一樣,Gradle只是提供了構(gòu)建項目的一個框架,真正起作用的是Plugin。Gradle在默認(rèn)情況下為我們提供了許多常用的Plugin,其中包括有構(gòu)建Java項目的Plugin,還有Android等。與Maven不同的是,Gradle不提供內(nèi)建的項目生命周期管理,只是java Plugin向Project中添加了許多Task,這些Task依次執(zhí)行,為我們營造了一種如同Maven般項目構(gòu)建周期。

Gradle是一種聲明式的構(gòu)建工具。在執(zhí)行時,Gradle并不會一開始便順序執(zhí)行build.gradle文件中的內(nèi)容,而是分為兩個階段,第一個階段是配置階段,然后才是實際的執(zhí)行階段。
配置階段,Gradle將讀取所有build.gradle文件的所有內(nèi)容來配置Project和Task等,比如設(shè)置Project和Task的Property,處理Task之間的依賴關(guān)系等。

看一個基本結(jié)構(gòu)的Android多Moudule(也就是gradle中的多Project Multi-Projects Build)的基本項目結(jié)構(gòu)。

├── app #Android App目錄
│   ├── app.iml
│   ├── build #構(gòu)建輸出目錄
│   ├── build.gradle #構(gòu)建腳本
│   ├── libs #so相關(guān)庫
│   ├── proguard-rules.pro #proguard混淆配置
│   └── src #源代碼,資源等
├── module #Android 另外一個module目錄
│   ├── module.iml
│   ├── build #構(gòu)建輸出目錄
│   ├── build.gradle #構(gòu)建腳本
│   ├── libs #so相關(guān)庫
│   ├── proguard-rules.pro #proguard混淆配置
│   └── src #源代碼,資源等
├── build
│   └── intermediates
├── build.gradle #工程構(gòu)建文件
├── gradle
│   └── wrapper
├── gradle.properties #gradle的配置
├── gradlew #gradle wrapper linux shell腳本
├── gradlew.bat
├── LibSqlite.iml
├── local.properties #配置Androod SDK位置文件
└── settings.gradle #工程配置

上面的是完整的AndroidStudio中的項目結(jié)構(gòu),我們抽象成Gradle多個Project的樣子

├── app 
│   ├── build.gradle #構(gòu)建腳本
├── module 
│   ├── build.gradle #構(gòu)建腳本
├── build.gradle #工程構(gòu)建文件
├── gradle
│   └── wrapper    #先不去管它
├── gradle.properties #gradle的配置
├── gradlew #gradle wrapper linux shell腳本
├── gradlew.bat
└── settings.gradle #工程配置

  • Gradle為每個build.gradle都會創(chuàng)建一個相應(yīng)的Project領(lǐng)域?qū)ο?,在編寫Gradle腳本時,我們實際上是在操作諸如Project這樣的Gradle領(lǐng)域?qū)ο蟆T诙郟roject的項目中,我們會操作多個Project領(lǐng)域?qū)ο?。Gradle提供了強大的多Project構(gòu)建支持。
  • 要創(chuàng)建多Project的Gradle項目,我們首先需要在根(Root)Project中加入名為settings.gradle的配置文件,該文件應(yīng)該包含各個子Project的名稱。Gradle中的Project可以簡單的映射為AndroidStudio中的Module。
  • 在最外層的build.gradle。一般干得活是:配置其他子Project的。比如為子Project添加一些屬性。
  • 在項目根目錄下有個一個名為settings.gradle。這個文件很重要,名字必須是settings.gradle。它里邊用來告訴Gradle,這個multiprojects包含多少個子Project(可以理解為AndroidStudio中Module)。

讀懂Gradle配置語法

Gradle向我們提供了一整套DSL,所以在很多時候我們寫的代碼似乎已經(jīng)脫離了groovy,但是在底層依然是執(zhí)行的groovy所以很多語法還是Groovy的語法規(guī)則。
看一個AndroidStudio中app下的build.gradle的配置

apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.0"
    defaultConfig {
        applicationId "me.febsky.demo"
        minSdkVersion 15
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:25.1.0'
}

分析第一行apply plugin: 'com.android.application'

這句其實是Groovy語法糖,像Ruby和Js都有這種語法糖,apply實際上是個方法,補上括號后的腳本:apply (plugin: 'com.android.application'),看起來還是有點別扭是不?還有個語法糖,如果方法參數(shù)是個map類型,那么方括號可以省略,進(jìn)一步還原apply([ plugin: 'com.android.application']),不理解的可以去看下Groovy的map的寫法,和js一樣。所以這行的意思是:apply其實是個方法,接收一個Map類型的參數(shù)。

總結(jié)兩點:1. 方法調(diào)用,圓括號可以省略 2. 如果方法參數(shù)是個Map,方括號可以省略。

Groovy語言的閉包語法

看上面的dependencies 這其實是個方法調(diào)用。調(diào)用了Project的dependencies方法。只不過參數(shù)是個閉包,閉包的用法在文章開始給出了鏈接。我們對其進(jìn)行還原一下:

#方法調(diào)用省略了()我們加上
dependencies ({
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:25.1.0'
})

提示一點:如果閉包是方法的最后一個參數(shù),那么閉包可以放在圓括號外面

#所以代碼還能寫成這樣
dependencies (){
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:25.1.0'
}

Getter和Setter

Groovy語言中的兩個概念,一個是Groovy中的Bean概念,一個是Groovy閉包的Delegate機(jī)制。
Java程序員對JavaBeans和Getter/Setter方法肯定不陌生,被設(shè)計用來獲取/設(shè)置類的屬性。但在Groovy中就不用那些沒用的方法了。即Groovy動態(tài)的為每一個字段都會自動生成getter和setter,并且我們可以通過像訪問字段本身一樣調(diào)用getter和setter。比如Gradle的Project對象有個version屬性(Property)下面這兩行代碼執(zhí)行結(jié)果是一樣的:

println project.version // Groovy  
println(project.getVersion()) // Java  

Project,Task ,Action

Gradle的Project之間的依賴關(guān)系是基于Task的,而不是整個Project的。

Project:是Gradle最重要的一個領(lǐng)域?qū)ο?,我們寫?strong>build.gradle腳本的全部作用,其實就是配置一個Project實例。在build.gradle腳本里,我們可以隱式的操縱Project實例,比如,apply插件、聲明依賴、定義Task等,如上面build.gradle所示。apply、dependencies、task等實際上是Project的方法,參數(shù)是一個代碼塊。如果需要,也可以顯示的操縱Project實例,比如:project.ext.myProp = 'myValue'

Task:被組織成了一個有向無環(huán)圖(DAG)。Gradle中的Task要么是由不同的Plugin引入的,要么是我們自己在build.gradle文件中直接創(chuàng)建的。Gradle保證Task按照依賴順序執(zhí)行,并且每個Task最多只被執(zhí)行一次。

Gradle在默認(rèn)情況下為我們提供了幾個常用的Task,比如查看Project的Properties、顯示當(dāng)前Project中定義的所有Task等。可以通過一下命令行查看Project中所有的Task:$ gradle tasks (具體log不再貼出來)。可以看到,Gradle默認(rèn)為我們提供了dependencies、projects和properties等Task。dependencies用于顯示Project的依賴信息,projects用于顯示所有Project,包括根Project和子Project,而properties則用于顯示一個Project所包含的所有Property。

**Tips: **查看Project中所有的Task:$ gradle tasks
查看Project中所有的properties:$ gradle properties

在上面的build.gradle中加入如下代碼:

task myTask {  
    doFirst {  
        println 'hello'  
    }  
    doLast {  
        println 'world'  
    }  
}  

這段代碼的含義:給Project添加一個名為“myTask”的任務(wù)
用一個閉包來配置這個任務(wù),Task提供了doFirst和doLast方法來給自己添加Action。

其實build.gradle腳本的真正作用,就是配置一個Project實例。在執(zhí)行build腳本之前,Gradle會為我們準(zhǔn)備好一個Project實例,執(zhí)行完腳本之后,Gradle會按照DAG依次執(zhí)行任務(wù)。

自定義Task的寫法

看下面代碼文件路徑~/Test/build.gradle

#1
task helloWorld << {
    println "Hello World"
}
#2 Test文件夾下建一個src目錄,建一個dst目錄,src目錄下建立一個文件,命名為test.txt
task copyFile(type: Copy){
    from "src"
    into "dst"
}

第一個這里的helloWorld是一個DefaultTask類型的對象,這也是定義一個Task時的默認(rèn)類型,當(dāng)然我們也可以顯式地聲明Task的類型,甚至可以自定義一個Task類型。
第二個代碼中(type:Copy)就是“顯式地聲明Task的類型”,執(zhí)行gradle copyFile test.txt也跑到dst中去了。

如果task聲明在根Project的build.gradle中的allprojects()方法中,那么這個Task會應(yīng)用于所有的Project。

task的依賴關(guān)系

Gradle不提供內(nèi)建的項目生命周期管理,只是java Plugin向Project中添加了許多Task,這些Task依次執(zhí)行,為我們營造了一種如同Maven般項目構(gòu)建周期。那么這些task是如何依次執(zhí)行的這就用到聲明的依賴關(guān)系taskA.dependsOn taskB看下面代碼:

task taskA << {
   println 'this is taskA from project 1'
}

task taskB << {
   println 'this is taskB from project 1'
}

taskA.dependsOn taskB

然后我們在命令行運行:
$ gradle taskA
運行結(jié)果會先執(zhí)行taskB的打印,然后執(zhí)行taskA的打印

如果是Muliti-Project的模式,依賴關(guān)系要帶著所屬的Project,如taskA.dependsOn ':other-project:taskC' 其中taskC位于和taskA不同的Project中,相對于AndroidStudio來說,就是位于不同的Module下的build.gradle中,而other-project為Module名字。

Task 的type可以自定義(沒有深入研究)

自定義Plugin的寫法

沒有深入研究,給出一個網(wǎng)上的例子:

apply plugin: DateAndTimePlugin

dateAndTime {
    timeFormat = 'HH:mm:ss.SSS'
    dateFormat = 'MM/dd/yyyy'
}

class DateAndTimePlugin implements Plugin<Project> {
    //該接口定義了一個apply()方法,在該方法中,我們可以操作Project,
    //比如向其中加入Task,定義額外的Property等。
    void apply(Project project) {
        project.extensions.create("dateAndTime", DateAndTimePluginExtension)

        project.task('showTime') << {
            println "Current time is " + new Date().format(project.dateAndTime.timeFormat)
        }

        project.tasks.create('showDate') << {
            println "Current date is " + new Date().format(project.dateAndTime.dateFormat)
        }
    }
}
//每個Gradle的Project都維護(hù)了一個ExtenionContainer,
//我們可以通過project.extentions進(jìn)行訪問
//比如讀取額外的Property和定義額外的Property等。
//向Project中定義了一個名為dateAndTime的extension
//并向其中加入了2個Property,分別為timeFormat和dateFormat
class DateAndTimePluginExtension {
    String timeFormat = "MM/dd/yyyyHH:mm:ss.SSS"
    String dateFormat = "yyyy-MM-dd"
}

每一個自定義的Plugin都需要實現(xiàn)Plugin接口,除了給Project編寫Plugin之外,我們還可以為其他Gradle類編寫Plugin。該接口定義了一個apply()方法,在該方法中,我們可以操作Project,比如向其中加入Task,定義額外的Property等。

原文地址

Gradle Wrapper

Wrapper,顧名思義,其實就是對Gradle的一層包裝,便于在團(tuán)隊開發(fā)過程中統(tǒng)一Gradle構(gòu)建的版本,然后提交到git上,然后別人可以下載下來,這樣大家都可以使用統(tǒng)一的Gradle版本進(jìn)行構(gòu)建,避免因為Gradle版本不統(tǒng)一帶來的不必要的問題。(所以要明白這個東西可以沒有,有了只是為了統(tǒng)一管理,更加方便)

生成wrapper

gradle 內(nèi)置了生成wrapper的task,我們可以命令行下執(zhí)行:
$ gradle wrapper

生成后的目錄結(jié)構(gòu)如下(用過AndroidStudio的很熟悉了):

├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
└── gradlew.bat

  • gradlew和gradlew.bat分別是Linux和Window下的可執(zhí)行腳本,他們的用法和gradle原生命令是一樣的,gradle怎么用,他們也就可以怎么用。在MacOS下運行$ ./gradlew myTask
  • gradle-wrapper.jar是具體業(yè)務(wù)邏輯實現(xiàn)的jar包,gradlew最終還是使用java執(zhí)行的這個jar包來執(zhí)行相關(guān)gradle操作。
  • gradle-wrapper.properties是配置文件,用于配置使用哪個版本的gradle等

詳細(xì)的看下gradle-wrapper.properties內(nèi)容

#Sat Jan 21 14:02:40 CST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-bin.zip

從上面內(nèi)容和文件的名稱都可以看出,這就是個java的配置文件,上面看到的是自動生成的,我們也可以手動修改。然后看下各個字段的含義:

  • distributionBase 下載的gradle壓縮包解壓后存儲的主目錄
  • distributionPath 相對于distributionBase的解壓后的gradle壓縮包的路徑
  • zipStoreBase 同distributionBase,只不過是存放zip壓縮包的
  • zipStorePath 同distributionPath,只不過是存放zip壓縮包的
  • distributionUrl gradle發(fā)行版壓縮包的下載地址,也就是你現(xiàn)在這個項目將要依賴的gradle的版本。

生成wrapper可以指定參數(shù)

  • 生成wrapper可以通過指定參數(shù)的方式來指定gradle-wrapper.properties內(nèi)容。
  • 使用方法如gradle wrapper –gradle-version 2.14這樣,這樣就意味著我們配置wrapper使用2.14版本的gradle,它會影響gradle-wrapper.properties中的distributionUrl的值,該值的規(guī)則是http://services.gradle.org/distributions/gradle-${gradleVersion}-bin.zip
  • 如果我們在調(diào)用gradle wrapper的時候不添加任何參數(shù)呢,那么就會使用你當(dāng)前Gradle的版本作為生成的wrapper的gradle version。例如你當(dāng)前安裝的gradle是2.10版本的,那么生成的wrapper也是2.10版本的。注:當(dāng)前版本指的是環(huán)境變量中配置的那個版本。

【參考文章】
http://www.infoq.com/cn/articles/android-in-depth-gradle/
http://blog.csdn.net/innost/article/details/48228651

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,063評論 25 709
  • 說明 本文主要介紹和Gradle關(guān)系密切、相對不容易理解的配置,偏重概念介紹。部分內(nèi)容是Android特有的(例如...
    搬磚的小明閱讀 16,157評論 1 62
  • 前言 為什么需要學(xué)Gradle? Gradle 是 Android 現(xiàn)在主流的編譯工具,雖然在Gradle 出現(xiàn)之...
    真笨笨魚閱讀 1,593評論 0 0
  • 你說你洗了一桶衣服,我開玩笑的說你洗的時候怎么沒和我說下我也有一桶,你開始發(fā)脾氣說為什么你做什么事要想著我?憑什么...
    謝笑閱讀 340評論 0 1
  • 當(dāng)用戶與程序的界面進(jìn)行交互時,或者通過代碼控制一些東西時,UIKit中會發(fā)生一系列復(fù)雜的事件來處理這張交互。在這一...
    解放者莫雷爾閱讀 250評論 0 0

友情鏈接更多精彩內(nèi)容