自動編譯組件
目前Android開發的主流開發工具是 Eclipse 和 IDEA
目前主流的自動化打包工具時 ant,maven,gradle。
maven工具中有自己的依賴倉庫維護,很多開源支持包在上面都有維護(國內的除外)
gradle是近年來發展起來自動化構建應用,解決 ant 構建上的繁瑣代碼,並且也支持讀取maven的配置形式,依賴maven的支持包結構
好了,平時你們使用 eclipse 發布的時候,不要說你沒用過自動構建,eclipse 的 android項目是用ant的構建方式。如果你要加渠道發布,你就需要自己寫 ant 的腳本。
maven自動構建,目前大多數用在 javaweb 項目,安卓項目用的不多。
eclipse 的構建應用大多使用 ant,maven,gradle也有相關支持。Android的項目默認使用 ant 進行構建
在 IDEA 中,可以使用 ant 方式構建 android應用,也能使用 gradle 方式構建,目前主流支持都是 gradle 方式。
IDEA 默認使用 gradle 工具做 android 的構建程序,你試試在新建應用的時候選擇 Application Module 而不是 Gradle:Android Module 看看,你會發現,工程項目和 eclipse 沒什么區別。而且包含 ant 的腳本文件。(如下圖)
Gradle Module原理
IDEA 支持 ant,maven,gradle 工具來構建引用。目前 Android 應用在 as下編碼基本使用 gradle 進行構建,本章將基於這個思路講解那些內容屬於 IDEA,那些內容屬於 gradle 。
首先,我們建立一個 Gradle:Android Module 來看看 Gradle 項目結構。
得到的目錄結構如下:
其中根目錄是一個project,下面的app目錄是其中一個module。
目錄文件 | 作用 |
---|---|
.gradle | gradle項目產生文件(自動編譯工具產生的文件) |
.idea | IDEA項目文件(開發工具產生的文件) |
app | 其中一個module,復用父項目的設置,可與父項目擁有相同的配置文件 |
build | 自動構建時生成文件的地方 |
gradle | 自動完成gradle環境支持文件夾 |
.gitignore | git源碼管理文件 |
build.gradle | gradle 項目自動編譯的配置文件 |
gradle.properties | gradle 運行環境配置文件 |
gradlew | 自動完成 gradle 環境的linux mac 腳本,配合gradle 文件夾使用 |
gradlew.bat | 自動完成 gradle 環境的windows 腳本,配合gradle 文件夾使用 |
local.properties | Android SDK NDK 環境路徑配置 |
*.iml | IDEA 項目文件 |
setting.gradle | gradle 項目的子項目包含文件 |
- .gradle .idea 是在分別在 gradle ,IDEA 運行時候會生成的文件,一般這樣的文件也不會納入源代碼管理之中。
- app文件夾,是其中一個module,里面的文件內容與父類差不多,若沒有定義,則在項目中使用父類的設置(意思就是,里面也能包含build.gradle、gradle.properties、setting.gradle 等相關gradle文件,怎么理解?其實每一層都是一個module,整個項目是一個大的 module 而已)
- gradle 文件夾,用於保存gradle 下載路徑的配置文件位置,用於沒有gradle環境的環境初始化使用
- build.gradle 項目的編譯環境配置,比如制定項目依賴的lib包。
- gradle.properties 配置gradle運行環境的文件,比如配置gradle運行模式,運行時jvm虛擬機的大小
- gradlew && gradlew.bat 代替gradle 命令實現自動完成gradle環境搭建。配合gradle文件夾的內容,會降到IDEA如何搭配gradlew使用。
- local.properties 配置android NDK,SDK的地方,恩,非android項目可能沒有這個文件,這個路徑根據不同想電腦不同,一般也不會納入源代碼管理之中,一般可以寫一個local.properties.simple 文件,告知需要修改該文件名並寫上本地SDK NDK 路徑。simple文件納入源碼管理之中。
- setting.gradle 子項目包含文件,聲明當前目錄下含有什么module,當然你的app底下加上這樣的文件,也能繼續在app底下加module。和我第點說的,整個project就是一個大的module,每個module下面還能包含相應的module。如果你理解這個了,其實app目錄單獨作為一個項目管理也是可以的,,把相應的配置文件配上而已,相當於主目錄應用 android 的gradle plugin (下一點會說到這個)
-
gitignore 該文件是源碼管理的配置文件,不在該文講解。
既然gradle 是多 module形式,那么我們來看看 setting.gradle 配置的內容
從上面目錄的配置文件內容來看,整個project也算是一個module,如果改module底下還有module,就可以通過setting.gradle配置進來,使得該module底下的gradle,從app module底下可以看出,module最少要含有 build.gradle文件,這個module的項目編譯文件,該module依賴什么插件對該目錄進行編譯都在此配置,比如android與android-library,其他內容可繼承父類的
Gradle 與 IDEA 的關聯性
上面介紹了gradle項目的目錄結構,以及module的模式,接下來,了解一下IDEA如何與gradle項目關聯上來的。
idea的項目,在建立或者導入的時候,就已經確定他是基於什么自動構建工具的項目。新建的時候,使用 gradle androiw module 說明他是 gradle 自動構建的項目,那么導入的時候也是一樣。我們看看下面的項目導入圖,在選擇項目地址之后,我們看到一下的內容。
如果你的項目里面包含了gradle的相關文件,就可以選擇 import project form external model 導入IDEA了,如果項目只有源碼,沒有包含自動構建的相關信息,你只能選擇 Create project form existing sources 了,讓他生成自動構建工具需要的文件。
你可能會想,想gradle里面的依賴包,我不知道有哪些,怎么辦。好吧,你私下里就罵罵作者吧,上傳了源碼不上傳自動構建相關的文件,導致項目依賴不完整,還得找資料自己完善,所以這里也希望廣大開源代碼愛好者,在分享你的作品的時候,希望能夠分享完整的項目信息,別只有源碼,讓人跑不起來項目(牢騷說多了)。
那么下一步是什么呢:
恩,是不是似曾相識?你沒看錯,在新建項目的時候也有這個選擇,這個是選擇自動編譯的工具的方式。
如果你到官方下載 gradle 綠色包,解壓到某個目錄,你可以使用 gradle distribution,並設定 Gradle home 目錄,這樣 IDEA 構建編譯項目的時候,就使用你設定的gradle版本進行。
說到這里,我覺得該說說 use default gradle wrapper 記得新建項目的時候也是使用 use default gradle warpper,這時候,上面目錄說到的三個文件。
- gradle/
- gradlew
- gradlew.bat
如果使用這種方法,IDEA會調用項目根目錄 gradlew 或者 gradlew.bat (根據linux,windows,osx自動選型)代替原生的 gradle 方法做自動構建。
這兩個文件做了什么事情呢:
- 解析 gradle/wrapper/gradle-wrapper.properties 文件,獲取項目需要的 gradle 版本下載地址
- 判斷本地用戶目錄下的 ./gradle 目錄下是否存在該版本,不存在該版本,走第3點,存在走第4點
- 下載 gradle-wrapper.properties 指定版本,並解壓到用戶目錄的下 ./gradle 文件下
- 利用 ./gradle 目錄下對應的版本的 gradle 進行相應自動編譯操作
看了上面的原理,應該明白了,如果你自己下載 gradle 讓idea 導入項目的時候使用。那么其他人就不知道你使用什么版本的gradle版本進行自動編譯,如果使用 項目目錄自帶的 gradlew 的話,gradlew 就會自動完善 gradle 的安裝,若需要更新 gradle 的版本,只需要修改 gradle/wrapper/gradle-wrapper.properties 文件內的下載鏈接即可。而且gradlew的版本和 android 版本是需要適配的,在自己電腦維護需求不同版本的 gradlew 也是一個麻煩的事情。
而且這樣的好處也有一個,當你在新電腦上下載你的源碼進行編譯時,你完全可以不依賴IDE開發工具,直接在項目目錄下使用 ./gradlew build 即可對源碼進行編譯,因為它會自動下載 gradle 進行調用,可以使得新電腦較快完成項目開發環境適配(對網絡依賴較強,希望帶上vpn,這就是為什么你們在新建項目時需要去下載gradle,還比較慢的原因,我們一起來 f-u-c-k-g-f-w)
當然,導入項目需要能夠選上 use default gradle wrapper 的前提是存在上面一個gradle文件夾與gradlew、gradew.bat
既然可以選擇使用 gradlew 來管理 gradle 或者手動指定 gradle 工具,那么已經存在的項目如何更改?
這個問題,我曾在 idea 13 版本上有找到,但是在 idea 14 上面沒找到相應的變更設置。誰要是找到了,記得留言告知我一聲。
在導入項目或者新建項目的時候,idea 會根據 build.gradle 文件更新 *.iml 項目文件,有時候,你會發現,新增一個jar包,但是無法讀取jar包內容,因為對於 gradle 來說,jar包屬於項目外依賴,包括maven拓展包,都是屬於項目外依賴,需要修改build.gradle 文件,當你加入新的 jar包,或者添加了 maven支持包,在idea上面都會提示需要進行同步
恩,上圖你看到的,是我模擬添加一個jar包之后,隨便加了一個空格,文件上提示需要進行 gradle 項目與 idea項目文件同步,點擊 sync now 之后,idea 會根據 gradle 文件重新更新 .idea 目錄以及 *.iml 文件,讓idea 可以識別引入的 資源。
這也就是為什么有人說加入一個jar包確沒有自動提示,而重新打開idea之后就能夠提示使用jar內的方法了,因為重新打開idea,開發工具會重新同步 build.gradle 的內容
Gradle Android Plugin Version與 Gradle Version對應
gradle只是一個自動化編譯工具,它需要以來插件來識別這是什么項目,用什么方式去編譯的。我們來看看 build.gradle 與 app/build.gradle 的設置看看。
不知道你們會不會奇怪,在app里面的 build.gradle 中,缺少了 buildscript 與 allproject 的設置,恩,沒錯,我刪掉了,因為這個設置是可以繼承父項目的 build.gradle
其中父類的整個項目需要依賴插件 com.android.tools.build:gradle:0.14.2 最后是版本號。(目前AS的版本號已經是1.0.0了)
這個插件的版本號與gradle調用編譯時是有依賴關系的,插件的版本越高,需要更多gradle的新特性,新的gradle特新就需要新版本的gradle才能支持。
這時候,回到上一節的內容,如果要使用android項目自動編譯的新特性,如果選擇不同的gradle指向方式,那么你就要做不同的處理
- 下載不同版本的gradle對不同的項目的不同版本做插件與gradle的對應維護,如果其他人使用你的項目,你還要告知他使用什么版本以上的gradle才能使用這個插件。
- 把對應關系一次弄好之后,更新gradle/wrapper/gradle-wrapper.properties下載地址,利用gradlew自動使用相應版本的gradle,這樣gradlew版本的需求就跟着源碼管理一直保留到其他人的電腦商。
具體的插件依賴,可點擊這里
目錄格式
先來看看eclipse的完整目錄與IDEA的完整目錄結構(當然,IDEA看的目錄時基於module的目錄設置的,而不是根據總項目的目錄設置的)
eclipse目錄
IDEA(AS)目錄
按照Android開發的目錄,區分為以下的目錄格式:
目錄類型 | Eclipse | IDEA(Android Studio) |
---|---|---|
代碼根目錄 | / | src/main |
adil文件 | /src | [代碼根目錄]/aidl |
java文件 | /src | [代碼根目錄]/java |
assets文件 | /assets | [代碼根目錄]/assets |
jni文件 | /jni | [代碼根目錄]/jni |
jnilibs文件 | /libs | [代碼根目錄]/jniLibs |
res文件 | /res | [代碼根目錄]/res |
AndroidMainfest.xml | AndroidMainfest.xml | [代碼根目錄]/AndroidMainfest.xml |
上面所展示的內容就是目前Android用到的所有資源文件類型。為什么要把項目根目錄列出來?這個到后面渠道包的時候需要用到。可是也可能也會問到,為什么項目根目錄會有兩級。src 第一級我的理解是項目源碼相關都在這里,第二級,認為是主項目源碼在 main 目錄,根據系統完善性,應該針對主項目添加測試項目的源碼,所以新的代碼里面在 main 目錄同級的地方會有 tests 目錄,用於測試項目的目錄源碼維護。如果有渠道,還可以為渠道包新建項目目錄去坐項目自定義。
1
2
3
4
|
1. 其中adil是跨進程通信使用的
2. jni文件夾是存放dnk編譯的c或者cpp文件
3. jnilibs文件,就是平時jni接入使用的 *.so庫。需要里面是需要包含平台文件夾的。入下圖所示
|
當我們要從eclipse里面轉移到as的時候,是可以通過gradle來從新定義以上路徑的。在module/build.gradle文件里面有這么一段設置默認設置,如果按照缺省,可以不寫。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
sourceSets {
main.setRoot('src/main')
main {
manifest.srcFile '[mainRoot]/AndroidManifest.xml'
java.srcDirs = ['[mainRoot]/java']
resources.srcDirs = ['[mainRoot]/java']
aidl.srcDirs = ['[mainRoot]/aidl']
res.srcDirs = ['[mainRoot]/res']
assets.srcDirs = ['[mainRoot]/assets']
jni.srcDirs=['[mainRoot]/jni']
jniLibs.srcDirs = ['[mainRoot]/jniLibs']
}
|
所示需要適應eclipse的目錄格式,可以寫成:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
sourceSets {
//main.setRoot('src/main') 因為下面的路徑全部被定義了,所以這個方法已經不起作用了。
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
jni.srcDirs=['jni']
jniLibs.srcDirs = ['libs']
}
|
你一定奇怪,為什么libs 的jar包沒有目錄呢?沒錯,還記得我上面寫過的,對於 gradle android 項目來說,jar包和library支持,屬於android項目的外部支持,通通由 Gradle 配置文件去管理。上圖的最后一段說明這一切。
Jar支持、Library支持、倉庫插件
說到 library ,不得不說說項目類型。
在項目根目錄,我們引入了 android 插件:com.android.tools.build:gradle:0.14.2
我們具體看上圖 build.gradle 的第一行代碼:
1
2
|
apply plugin: 'com.android.library'
|
這是說明這個 module 項目說明 android-library 項目,我看過其他的項目,gradle module 是可以依賴多個 plugin 讓這個項目成為多種類型的項目。
如果是一個普通android項目,會是這樣的代碼:
1
2
|
apply plugin: 'com.android.applition'
|
那么 android 項目 gradle 中依賴 jar libs 又三種方法:
1
2
3
4
5
6
7
8
9
10
11
|
#1 依賴項目相對路徑的jar包,當然,你可以換成全路徑
compile files('libs/something_local.jar')
#或者依賴libs目錄下的所有jar包
compile fileTree(dir: 'libs', include: ['*.jar'])
#2 依賴maven倉庫中的支持包(目前很多好的都在maven進行管理,比如 v4,v7支持包)
compile 'com.android.support:appcompat-v7:20.0.0'
#3 依賴其他library module
compile project(':jiechic-library')
|
這么一看,我這個 jiechic-library 其實就是一個module,與app同級,使用的是 apply plugin: ‘com.android.library’ 形式的一個安卓 library 。
有時候,你可能新加入一個
很多開源倉庫不懂怎么加?maven插件添加不在這篇文章的討論范圍了。你在搜搜可否?
編譯過程及渠道模式
ant 的的編譯目錄基於當前工作目錄進行,如果你需要自定義渠道,你需要編寫 ant 腳本代碼,去替換當前目錄的文件,而且當前目錄的文件你還需要進行保存一次。若出錯了,你還得在本地目錄進行恢復。
gradle 的編譯方式,是根據基礎項目內容,以及渠道信息 ,將相關代碼文件拷貝合並到 build 目錄下,然后在build 目錄下進行編譯。在此設計小文件多文件的平凡拷貝更新,則正常編譯速度讓很多人覺得,IDEA 開發比 Eclipse 的卡,其實這也是他實現的方式造成。
但是 gradle 項目卻提供了很多eclipse 不方便提供的功能。比如渠道模式
比如渠道模式:
在build.gradle 中有這樣的設定 productFlavors 這樣的設定,當改設定存在,則 main 主代碼渠道不在進行打包,全部依賴渠道進行編譯安裝調試
看到我的 productFlavors 中定義了兩個版本,一個是線上測試餐飲版,一個是線上測試大眾版。你看了,可能舉得這么定義沒什么用,你看到大括號里面了么?? applicationID,恩,沒錯,這就是可以重新定義他的包名,在這括號里面能夠重新定義defaultConfig里面的所有配置。每個渠道的版本號都能單獨維護。不需要你寫ant腳本去替換。而且每個渠道也是同時可以編譯 debug 和 release 版本
再來看下圖:
記得在目錄詳解的目錄上,設定的是 main 渠道的目錄,恩,沒錯,現在我設定的是渠道的目錄。這個怎么說?還記得我設定主目錄的設定代碼么?
1
2
3
4
5
6
7
8
9
10
11
12
13
|
sourceSets {
main.setRoot('src/main')
main {
manifest.srcFile '[mainRoot]/AndroidManifest.xml'
java.srcDirs = ['[mainRoot]/java']
resources.srcDirs = ['[mainRoot]/java']
aidl.srcDirs = ['[mainRoot]/aidl']
res.srcDirs = ['[mainRoot]/res']
assets.srcDirs = ['[mainRoot]/assets']
jni.srcDirs=['[mainRoot]/jni']
jniLibs.srcDirs = ['[mainRoot]/jniLibs']
}
|
沒錯,你看到的,所有android相關的文件,都在某個root下面。
那么,我們渠道上面設置的的路徑也是基於如此路徑進行文件分類的。
恩,沒錯,設定了渠道的內容,編譯渠道的時候,不同的渠道就會依據主項目,然后替換自己的內容。 比如上圖的String,你需要替換的只要寫入你需要替換的String name,這么value的值就會在這個渠道就會替換過來。包括drawable等二進制文件,他會進行替換操作,比如說,你給應用寶打包的應用啟動歡迎頁是應用寶的,給其他應用商城的是其他圖片,就可完全放置在渠道文件夾內。
如何檢查是否合並?檢查 build 文件內的合並內容,具體的不細說,你應該能找到,比如某個渠道替換的文件,找到那個渠道相應的文件查看即可。如果時Value的值,那就查看value文件內對應的name是否已經變更成功。
說到這個,不得不提,你有時候雙擊報錯信息,打開了某個文件,或者某個圖片(.9圖較多),想對其進行修改,你發現它是只讀狀態,不可修改,恩,沒錯,這個編譯文件經過 gradle 拷貝到build 相應渠道目錄下才開始進行編譯,在那個目錄下的文件都是只讀狀態,並非你的源代碼文件。你可以根據提示信息找到你自己的源文件進行修改。
你可能會問,這么多渠道包,那我要調試,究竟默認使用哪個渠道呢?看下圖:
恩,你沒看錯,在項目下面,有個選擇編譯版本的地方。。這樣,你想調試那個渠道的版本都可以輕松進行了。
提速編譯
gradle 在編譯時候有些東西跟java虛擬機相關,記得一開頭的項目文件 gradle.properties 么?這個是設定 gradle 在運行時的編譯環境。
在渠道編譯的時候,默認情況下,一個渠道會啟用一個java 虛擬機進行編譯,在java 虛擬機啟動,關閉的過程是非常耗時的,gradle 提供了守護進程的模式進行編譯,從頭到尾就使用一個java 虛擬機(jvm)
至於什么是守護進程模式,資料比較多,我就不解析了,可看這里這里
同時,根據你自己的電腦內存,你還可以定義虛擬機的內存數大小,這個其實會關乎你電腦卡不卡的問題,電腦內存太小,編譯虛擬機內存太多,也許你切換個環境都會覺得舉步維艱吧。
很多教程,都寫編譯打包版本,使用命令
1
2
|
./gradlew build
|
可是,都不會告訴你,更好的提高效率。官方提供release 版本與debug版本的區別編譯
一般我們要編譯所有渠道的 release版本,會使用如下命令:
1
2
3
4
|
./gradlew assembleRelease
#or
./gradlew aR
|
你肯定會問,如果我基於一個穩定版本,新增一個渠道,我只想打包一個渠道,怎么辦?之行下面的命令看看
1
2
|
./gradlew task
|
這會列表所有 gradle task。task的概念我就不解釋了,很多gradle教程已經說了。
我只要打包 onlinetestcatering 渠道,只要之行這樣的命令就可以了。
1
2
|
./gradlew task assembleOnlinetestcateringRelease
|