Gradle編譯打包Android apk詳細(xì)介紹
Gradle編譯打包Android apk詳細(xì)介紹
理解Gradle構(gòu)建過程,解讀Android Gradle插件的配置
閱讀本文一定是要使用過Gradle生成apk,文中不會(huì)講如何安裝運(yùn)行Gradle,如有需要可先看文末的參考文章。
APK包是一個(gè)ZIP壓縮包,從Java源代碼、資源文件到生成這個(gè)APK,經(jīng)過了編譯打包一系列特定的過程,SDK文檔(/docs/tools/building/index.html)中找到。而這一系列特定的過程,重復(fù)繁瑣,構(gòu)建工具(build tool)就是來流程化這些過程,解放你的雙手。Ant作為apk早期的構(gòu)建工具,構(gòu)建過程顯得很直觀,像配置;Gradle可以方便地配置,但更像腳本,可以編程。
理解Gradle構(gòu)建
1.簡(jiǎn)單理解構(gòu)建工具
從一個(gè)程序員的角度,你該如何編寫代碼來自動(dòng)化你的apk生成過程呢?首先得知道你需要的SDK、NDK在什么位置,Android工程有幾個(gè)庫工程,它們的Java源代碼、資源文件分別有哪些?命令行的輸入?yún)?shù)肯定無法滿足需求,那自然而然想到配置文件。因此你的自動(dòng)化工具就是解析這些配置文件,按照生成apk文件要求執(zhí)行的程序。Gradle就是這樣的工具程序,配置文件就是你常見的settings.gradle,build.gradle,不過他還提供了更多的功能,如依賴管理,流程控制,還有插件機(jī)制來定制你的生成過程。
Gradle的編程語言是Groovy,其需要的配置文件支持Groovy。Groovy語言像Java一樣是基于JVM的,而且能夠很好的支持Java,因此可以用Java代碼編寫擴(kuò)展插件,像普通編程一樣來寫配置文件,而不用像Ant一樣用xml來編寫配置邏輯。
2.Groovy
Groovy的語法,把自己的代碼縮略的看上去像腳本,本人也只是看了一點(diǎn)點(diǎn)文檔,列出我們常用的介紹一下:
首先Groovy是面向?qū)ο蟮膭?dòng)態(tài)語言
(1)語句的末尾可省略分號(hào)
(2)變量定義可以用def,也可以直接使用
(3)函數(shù)定義可以用def,也可以不用(有返回值聲明也可)
函數(shù)可省略參數(shù)類型
函數(shù)調(diào)用可省略括號(hào)
來看個(gè)例子:
println 'Hello'
int power(int n) { 2**n }
println "2^6==${power(6)}"
第一行輸出字符串Hello,第二行定義一個(gè)函數(shù),第三行輸出函數(shù)調(diào)用值,雙引號(hào)中間的${}可被解析成表達(dá)式運(yùn)行。
(4)List和Map類型
List實(shí)現(xiàn)就是Java的java.util.ArrayList,變量由[]包圍,用逗號(hào)分隔,比如
def heterogeneous = [1, "a", true] //其元素可以是任何對(duì)象
Map的實(shí)現(xiàn)是java.util.LinkedHashMap,也是由[]包圍,用逗號(hào)分隔,其中的鍵值對(duì)是key:value形式,如:
def colors = [red: '#FF0000', green: '#00FF00', blue: '#0000FF'] assert colors['red'] == '#FF0000' //取值可以[key]或者.key的形式 assert colors.green == '#00FF00'
(5)閉包(Closures)
閉包我的理解類似C里的函數(shù)指針,或者說函數(shù)對(duì)象,可以像函數(shù)一樣調(diào)用的對(duì)象
{ [closureParameters -> ] statements }
例子
def testClosure = {int arg1, String arg2 ->//def可省略,參數(shù)可省略,默認(rèn)有it,當(dāng)只有->表示沒有參數(shù)
println "arg2:${arg2}" //執(zhí)行的代碼,返回值是最后一句,也可以用return
}
調(diào)用:
testClosure(1,'2')
testClosure.call(1,'2')
3.Gradle
接著說自動(dòng)化工具,編程語言有了,那實(shí)現(xiàn)一系列特定過程生成apk,就看如何實(shí)現(xiàn)了。Gradle里有Project,表示一個(gè)待編譯打包處理的工程,可以生成apk,可以生成Jar,Project中可以包含多個(gè)Project;每個(gè)Project由很多的Task構(gòu)成,可以理解為不同的過程;每個(gè)Task里又是有不同的Action,和一系列要執(zhí)行的操作(或者說你寫的要執(zhí)行的語句)。Project中的Task的執(zhí)行順序,則是由其dependsOn來控制的。
Gradle執(zhí)行時(shí)是以task為單位執(zhí)行,在命令行中以gradle task來執(zhí)行,這一過程的生命周期分為三個(gè)階段(官方使用手冊(cè)中也有詳細(xì)介紹The Build Lifecycle):
(1)初始化階段
判斷包含哪些工程,創(chuàng)建對(duì)應(yīng)的Project實(shí)例,可以看到最新執(zhí)行的是在settings.gradle中的語句
(2)配置階段
創(chuàng)建不同的Task(Task可以動(dòng)態(tài)生成),并根據(jù)Task之間的dependsOn,確定Task的執(zhí)行順序,或者禁用某些Task。此階段完成后,Task之間的依賴關(guān)系也就確定下來。
(3)執(zhí)行階段
在配置完成后,按照依賴關(guān)系,按順序執(zhí)行。
需要注意的是通常在Task中的語句,都是在配置階段執(zhí)行的,而doFirst,doLast這類Action是在執(zhí)行階段中的。因此會(huì)出現(xiàn)你的依賴關(guān)系中沒有的Task中語句也被執(zhí)行了的問題
如下例子,打印出的task內(nèi)容是不一樣的
task printTasksName {
tasks.all {//all是一個(gè)方法,參數(shù)可以是閉包,函數(shù)的元括號(hào)可以省略
println "show tasks in Configuration:${it.name}" //it是閉包的默認(rèn)參數(shù)
}
doFirst {
tasks.all {
println "show tasks in Execution:${it.name}"
}
}
}
4.Gradle插件和Androd插件
在提供了基本的流程控制之后,接下來是具體的要做什么,構(gòu)建什么。Gradle提供了針對(duì)語言的插件如java,groovy等負(fù)責(zé)編譯,集成插件如application,war等生成java可執(zhí)行程序,web程序的WAR文件。Android根據(jù)APK生成的過程,編寫了自己的插件,其中也使用了java插件。
(1)自定義插件的插件名稱在resources/META-INF/gradle-plugins
在resources/META-INF/gradle-plugins目錄下有后綴為properties文件,該文件的命名就是你在build.gradle中使用插件的名字,里面聲明了該插件的實(shí)現(xiàn)類。在Android插件的源碼中可以看到android.properties和com.android.application.properties中兩個(gè)插件名稱,因此在build.gradle中,應(yīng)用工程使用Android插件需要apply plugin: 'android'(已是deprecated)或者apply plugin: 'com.android.application'
(2)Android插件中application的實(shí)現(xiàn)類是AppPlugin,繼承自com.android.build.gradle.BasePlugin ,調(diào)用apply方法,相應(yīng)的configureProject(),解析local.properties,獲得sdk位置,創(chuàng)建AndroidBuilder,應(yīng)用JavaBasePlugin,而后createExtension() 關(guān)聯(lián)BuildType,ProductFlavor,SigningConfig,最后createTasks(),完成各個(gè)Task的創(chuàng)建
5.DSL(Domain Specific Language)
DSL我翻譯成領(lǐng)域?qū)S谜Z言,就是在這里預(yù)先規(guī)定好的規(guī)則,或者說是行話。在Gradle的DSL中一般常見的類型,一種是類型(Type)有Project、Task等,給他們定義了不同的操作和用法,一種是語句塊(build script block或configuration block)如build.gradle中常見的buildscript { },allprojects { }。Android中也定義了非常多語句塊,如buildTypes { },sourceSets { }。
Android Gradle插件配置
有了上述概念,再看android應(yīng)用中的build.gradle,其實(shí)文中也只能講一些,但是更多的可以自己查看Android插件的DSL
apply plugin: 'com.android.application' // 使用Android插件,非庫工程,生成的是apk
dependencies {
compile 'com.android.support:multidex:1.0.1'
compile fileTree(dir: 'libs', include: '*.jar')
compile project(':庫工程1') // 代碼、資源包含在主程序apk中的
provided project(':庫工程1') // 只參與編譯,不輸出到目標(biāo)apk中
}
// Android插件DSL中的AppExtension類型
android {
compileSdkVersion rootProject.ext.compileSdkVersion // 多工程時(shí)配置統(tǒng)一的屬性
buildToolsVersion rootProject.ext.buildToolsVersion
// lint檢查,避免lint檢測(cè)到不符合條件退出編譯
lintOptions {
abortOnError false
}
// gradle編譯會(huì)默認(rèn)合并庫工程的manifest到主工程,如果主程序和庫工程的包名不一致會(huì)有問題
enforceUniquePackageName = false
// 所有的 product flavors繼承
defaultConfig {
applicationId "cn.arainfo"
minSdkVersion 14
targetSdkVersion 10
// multiDexEnabled true
dexOptions {
javaMaxHeapSize "2g"
jumboMode true
}
}
// 此處由于工程是從Eclipse導(dǎo)入,所有路徑都進(jìn)行了聲明
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
}
debug.setRoot('build-types/debug')
release.setRoot('build-types/release')
}
// 編譯類型,指定release的proguard配置文件
buildTypes {
release {
minifyEnabled true
proguardFile 'proguard.flags'
}
}
}
afterEvaluate {
println "afterEvaluate set project dependsOn..."
project(':主程序工程').tasks.getByName("assembleDebug").dependsOn ":子程序工程:assembleDebug"
if (project.hasProperty('TestRelease')) {
project(':主程序工程').tasks.getByName("assembleRelease").dependsOn ":子程序工程:assembleRelease"
}
}
說明幾處
(1)如在根目錄的build.gradle中聲明公用屬性
ext {
compileSdkVersion = 21
buildToolsVersion = '25.0.0'
isOnWindows = Os.isFamily(Os.FAMILY_WINDOWS)
}
(2)buildTypes { },productFlavors{ },signingConfigs { }
上述三個(gè)語句塊,類型是NamedDomainObjectContainer<T>,其中T是BuildType ProductFlavor SigningConfig,而buildTypes { },productFlavors{ }中增加新的類型,會(huì)對(duì)應(yīng)有新的Task生成,規(guī)則為assemble[flavor][buildType],因此當(dāng)compileSdkVersion較低,又用了MultiDex時(shí),還想用InstantRun,就可以創(chuàng)建一個(gè)新的buildType,只在測(cè)試時(shí)使用
(3)afterEvaluate語句塊
Android的Gradle插件版本在2.2.0時(shí),Task的創(chuàng)建已經(jīng)在afterEvaluate,因此,如果想繼續(xù)使用tasks.getByName("assembleDebug"),必須要將自己的語句寫到afterEvaluate { }語句塊中
總結(jié):
越寫越心虛,零零碎碎的內(nèi)容非常多,按照自己的理解貫穿下來,涵蓋了部分內(nèi)容,基本可以理解build.gradle。但是語句塊如buildTypes { }和BuildType如何關(guān)聯(lián)起來(或者說如何解析出來并創(chuàng)建對(duì)象)的,并沒有很好的理解。
如果有時(shí)間可以好好看下《深入理解Android(一):Gradle詳解》,一般的問題,比如多渠道打包productFlavors怎么配置啊,自己通過查Android的DSL就能解決。
參考文檔:
1.Groovy官方文檔
2.Gradle用戶手冊(cè)
3.Gradle的DSL
4.Android插件的DSL
5.深入理解Android(一):Gradle詳解
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
相關(guān)文章
Android 嵌套Fragment的使用實(shí)例代碼
本文主要介紹Android Fragment,在這里提供了實(shí)例代碼跟效果圖,希望能幫助有需要的小伙伴2016-07-07
超簡(jiǎn)單Android集成華為HMS Scankit 掃碼SDK實(shí)現(xiàn)掃一掃二維碼
這篇文章主要介紹了超簡(jiǎn)單Android集成華為HMS Scankit 掃碼SDK實(shí)現(xiàn)掃一掃二維碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03
Android Listview notifyDataSetChanged() 不起作用的
這篇文章主要介紹了Android Listview notifyDataSetChanged()不起作用的解決方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2024-08-08
Android編程實(shí)現(xiàn)PendingIntent控制多個(gè)鬧鐘的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)PendingIntent控制多個(gè)鬧鐘的方法,涉及PendingIntent屬性設(shè)置與使用的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-12-12
android通過Location API顯示地址信息的實(shí)現(xiàn)方法
這篇文章主要介紹了android通過Location API顯示地址信息的方法,涉及Android操作Geocoder類獲取地址信息的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07
Android基于騰訊云實(shí)時(shí)音視頻仿微信視頻通話最小化懸浮
這篇文章主要為大家詳細(xì)介紹了Android基于騰訊云實(shí)時(shí)音視頻實(shí)現(xiàn)類似微信視頻通話最小化懸浮,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11
android使用Jsoup 抓取頁面的數(shù)據(jù)
本篇文章主要介紹了android使用Jsoup 抓取頁面的數(shù)據(jù),jsoup 是一款Java的HTML解析器,有需要的朋友可以了解一下。2016-11-11
Android實(shí)現(xiàn)自定義滑動(dòng)刻度尺方法示例
這篇文章主要給大家介紹了關(guān)于Android實(shí)現(xiàn)自定義滑動(dòng)刻度尺的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)各位Android開發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
詳解Flutter?響應(yīng)式狀態(tài)管理框架GetX
這篇文章主要為大家介紹了Flutter?響應(yīng)式狀態(tài)管理框架GetX詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
Android亮度調(diào)節(jié)的幾種實(shí)現(xiàn)方法
本篇文章詳細(xì)介紹了Android亮度調(diào)節(jié)的幾種實(shí)現(xiàn)方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2016-11-11

