1. 程式人生 > >理解AndroidStudio中的build.gradle檔案

理解AndroidStudio中的build.gradle檔案

對於才從eclipse轉AndroidStudio的開發者來說,初次接觸到build.gradle檔案真是一頭霧水,不知道這是幾個意思,如下圖

上圖就是建立一個as工程生成的基本構建檔案了,其中這些語句是什麼意思,我們來慢慢看

Gradle 是基於Groovy語言來構建的,第一點就得了解什麼是Groovy語言,以及它的語法

Groovy是一種動態語言,是從Java語言進化而來,可以說是Java的加強,它能運行於JVM上,具體的一些介紹可以自行谷歌瞭解,這裡只是為了簡單的看懂Groovy和基本使用。

我們先在我們熟悉的eclipse上來寫一個Groovy專案

開發環境:

第一個Groovy程式:

這裡寫圖片描述

右鍵,新建一個Groovy工程,新增一個groovy資源包(只是為了區分),然後在src下先新建一個JavaTest.java類,勾選main方法,關在方法裡寫下一行程式碼列印一個字串 Hello groovy!

public class JavaTest {  
    public static void main(String[] args) {  
        System.out.println("Hello groovy!");  
    }  
}  

上面是一個很簡單的Java類,執行之後將會在控制檯輸出Hello groovy!

然後同理,我們在groovy資源包下新建一個GroovyTest.groovy 選擇新建-other-Grooovy Class

    class GroovyTest {  
        static main(args) {  
            println "Hello groovy!"  
        }  
    }  

右鍵使用Groovy執行後,也會在控制檯上打印出 Hello groovy!

相比於JavaTest.java我們發現,GroovyTest.groovy。似乎和JavaTest.java長得很像,只不過是將JavaTest.java去掉了一些內容,如class的修飾符public ,列印的System.out ,引數args的型別String[]。下面我們對GroovyTest.groovy做一些改動:

    class GroovyTest {  
        static main(args) {  
            System.out.println "Hello groovy!"  
        }  
    }  

執行之後發現一樣能輸入Hello groovy!,那我們再給args加上String[]的型別能

class GroovyTest {  
    static main(String[] args) {  
        System.out.println.println "Hello groovy!"  
    }  
}

發現一樣能執行,並且同樣輸入Hello groovy! ,有趣了,是不是說我把java裡面的程式碼都拷過來也能執行呢,這時變成這樣:

public class GroovyTest {  
    public static void main(String[] args) {  
        System.out.println("Hello groovy!");  
    }  
} 

和你想的結果一樣,他也準確無語的輸出了Hello groovy!,原來和我們看到的一些說明是正確的,groovy和java程式碼是一樣的,它只不過是對java程式碼進行簡化了。

不是說簡化嗎,假如我把GroovyTest類的程式碼簡化成這樣:

println "Hello groovy!"

執行後,發現居然可以輸出來Hello groovy!

下面是groovy的一些說明:

Groovy類和java類一樣,完全可以用標準java bean的語法定義一個Groovy類。但作為另一種語言,可以使用更Groovy的方式定義類,這樣的好處是,可以少寫一半以上的javabean程式碼。
(1)不需public修飾符
如前面所言,Groovy的預設訪問修飾符就是public,如果Groovy類成員需要public修飾,則根本不用寫它。
(2)不需要型別說明
同樣前面也說過,Groovy也不關心變數和方法引數的具體型別。
(3)不需要getter/setter方法
在很多ide(如eclipse)早就可以為程式設計師自動產生getter/setter方法了,在Groovy中,不需要getter/setter方法–所有類成員(如果是預設的public)根本不用通過getter/setter方法引用它們(當然,如果一定要通過getter/setter方法訪問成員屬性,Groovy也提供了它們)。
(4)不需要建構函式
不再需要程式設計師宣告任何建構函式,因為實際上只需要兩個建構函式(1個不帶引數的預設建構函式,1個只帶一個map引數的建構函式–由於是map型別,通過這個引數可以構造物件時任意初始化它的成員變數)。
(5)不需要return
Groovy中,方法不需要return來返回值。
(6)不需要()
Groovy中方法呼叫可以省略()(建構函式除外)。

我們可以這樣寫:

定義一個字串

def aa="Hello"  
println aa

定義一個整形

def num=10  
println num

定義一個整形陣列集合

def colum=[2,3,4,5]  
println colum[0]

因為groovy已經過載了<<所以我們可以用<<來向集合加入一個值

    def strArry=["jin","mu","shui"]  
    strArry.add("huo")  
    strArry << "tu"  
    println strArry  

輸出
[jin, mu, shui, huo, tu]

定義一個Map型別

 def map=[key:"name",value:"mjk"]  
    println map["key"]  
    println map["value"]  

輸出:
name
mjk

只要看到是 key:value都是map型別,因為key會自動轉換成string,所以你可以寫成def map=[“key”:”name”,”value”:”mjk”]也可以寫成def map=[key:”name”,value:”mjk”]

如果想知道當前這個變數是一個什麼型別可以用 .class來列印

    def str="I am string"  
    println str.class  

輸出
class java.lang.String

迴圈的使用:

    def arry=["a","b","c","d"]  
    for(a in arry){  
        println a  
    }  

定義一個方法,接收兩個引數,輸出它們的和

    def num=10  
    def method(a,b){  
        println a+b  
    }  
    method num,9  

當然使用method(num,9)是一樣的,只是去掉了括號,引數之間用逗號隔開

在定義一個map型別的時候,一定要加[],如果不加會報錯,而在方法呼叫時,可以不加[],如果方法呼叫的map引數要加[],則必須在外部加()

如:

 //列印 map值  
    def printMap(map){  
        println map["key"]  
    }  
    printMap key:"name"  

輸出:name

定義一個方法,列印接收到的值

    def printStr(str){  
        println str  
    }  
    printStr "Hello groovy!"  

定義閉包:

閉包是Groovy裡面一個很重要的特性,這裡要著重講,

先看一個groovy對集合本身的實現了的一個包含閉包的方法each

 def acoll = ["媽媽", "爸爸", "我"]     
    acoll.each{  
     println it  
    }  

輸出:

媽媽
爸爸

acoll是一個字串的集合,這個集合裡面有一個方法叫each,接收一個引數,這個引數是一個閉包,因為閉包也是一種型別,所以它也能像變數一樣當引數傳遞給方法使用。我們這裡簡單的理解,閉包是一個用花括號{}括起來的程式碼塊,這個程式碼塊在被觸發的時候呼叫。

上面each中的it是一個關鍵字,它儲存了返回到這個閉包中的一個值。下面我們來用自己的方法實現這個each

定義一個方法,這個方法接收一個集合引數跟一個閉包,當方法被呼叫時,遍歷集合裡面的值,並把值傳回給閉包

    def myEach(arry,block){  
        for(i in arry){  
            block(i)  
        }  
    }  
    def arr=["爸爸","媽媽","我"]//字串集合  
    //呼叫myEach方法  
    myEach(arr,{  
        println it  
    });  

輸出:

爸爸
媽媽

原來流程是這樣,當我們呼叫myEach方法時,要傳入一個集合arr,和閉包。當方法,myEach在遍歷集合的時候,每得到一個值 就會回撥一下這個閉包並把值傳給它,從這裡看是不是覺得,這個閉包既像一個變數引數,又像一個方法呢。

定義一個閉包並呼叫它:

因為閉包是一種型別,所以在定義的時候要用=來給它賦值,這是跟方法有本質的區別

    def block={  
        println "呼叫block"  
    }  
    block()  
    輸出:呼叫block  
    定義一個閉包並傳入一個引數  
    def block={  
        println "呼叫block"+it  
    }  
    block("--並傳入引數")  

輸出
呼叫block--並傳入引數

要是我們不想用it這個關鍵字,也是可以定義自己的關鍵字的,只是我們要使用->來告訴它,是用我們自己定義的變數來接收值,而不用it

    def block={param ->  
        println "呼叫block"+param  
    }  
    block("--並傳入引數")  
    //兩個引數  
    def block={param1,param2->  
        println "呼叫block"+param1+param2  
    }  
    block("--引數1","引數2")  

下面我們定義一個block,這個block是用來列印接收到的值,目的是去遍歷給定的集合

    def block={data->  
        println "value="+data  
    }  
    //定義一個方法,這個方法接收一個集合引數跟一個閉包,當方法被呼叫時,遍歷集合裡面的值,並把值傳回給閉包  
    def myEach(arry,block){  
        for(i in arry){  
            block(i)  
        }  
    }  
    def arr=["爸爸","媽媽","我"]//字串集合  
    //呼叫myEach方法  
    myEach(arr,block)  

輸出:

value=爸爸
value=媽媽
value=我

我們知道方法的括號是可以去掉的,如果上面呼叫方法時我們去掉括號就變成了:

原來的是:myEach(arr,block)

myEach arr,block

而如果只是接收一個閉包的方法就可以寫成:

myEach block

如果block是匿名的,那就成為:

myEach {

}

所以我們可以知道,集合的each方法是怎麼實現遍歷的,當我們的這個集合已經知道了裡面的值,呼叫each時,只需要傳入一個閉包引數,就可以得到每一個值。

一般來說,如果有多個引數,而最後一個為閉包的時候,呼叫方法的時候習慣寫成:

myEach(param1,param2,param3){

}

例項:

定義一個方法,這個方法接收一個集合引數跟一個閉包,當方法被呼叫時,遍歷集合裡面的值,並把值傳回給閉包

 def myEach(arry,block){  
        for(i in arry){  
            block(i)  
        }  
    }  
    def arr=["爸爸","媽媽","我"]//字串集合  
    //呼叫myEach方法,並傳入一個集合跟一個匿名的閉包  
    myEach (arr){data->  
        println "value="+data  
    }  

到此差不多把groovy裡面的一些基本的語法說完了。下面講一下gradle

build.gradle檔案

關於Android Studio構建檔案 build.gradle 的相關配置,重點學習幾個方面的內容:

  1. applicationId 和 package 屬性值的關係
  2. 怎麼配置安全的自定義簽名
  3. 兩種構建型別的區別
  4. 為什麼要定製產品的偏好配置?
  5. 怎麼才能加快DEX檔案的生成速度
  6. 為什麼要將一個apk拆分成多個?
  7. 關於引入依賴包你不知道的祕密

通過以下學習,你會對 build.gradle 檔案有一個全新的認識
程式碼如下:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 24
    buildToulsVersion "25.0.2"
    /**
     * 一、預設產品偏好配置
     */
    defaultConfig {
        ...
    }
    /**
     * 二、自定義簽名配置
     */
    signingConfigs {
        config {
          ...
        }
    }
    /**
     * 三、構建型別,分為release和debug兩種
     */
    buildTypes {
        release {
          ...
        }
        debug {
          ...
        }
    }
    /**
     * 四、自定義產品偏好配置,可以定義多個偏好產品
     */
    productFlavors {
        demo {
            applicationId "cn.teahcourse.demo"
            versionName "1.0-demo"
            signingConfig signingConfigs.config
        }
        personal{
          ...
        }
        enterprise{
          ...
        }
    }
    /**
     *五、DEX檔案構建屬性配置(加快構建速度)
     */
    dexOptions {
        ...
    }
    /**
     * 六、將一個apk拆分成多個相關配置(拆分依據:螢幕密度、系統架構)
     */
    splits {
        density {
           ...
        }
        abi {
           ...
        }
    }
}
/**
 * 七、引入依賴包的祕密
 */
dependencies {
   ...
}

1. applicationId 和 package 屬性值的關係

Android Studio開發工具建立module的時候,預設在 build.gradle 檔案生成一個 applicationId ,對應的屬性值是填寫的package name,如下圖:

這時候的 applicationId 和 package 屬性值一樣,剛開始接觸Android Studio的時候,applicationId 表示真正的包名,而 package 不再被認為是包名,因為應用程式被打包成apk檔案的時候,原先在manifest宣告的 package 被 applicationId 代替,也就是說如果你的 build.gradle 檔案添加了 applicationId 屬性值,無論兩者是否一樣,打包的apk檔案的 package 屬性值等於 applicationId 。

如果不信,先來做個實驗,將 applicationId 改為 cn.teachcourse.demo ,將 package 改為 cn.teachcourse ,然後將module打包成apk檔案,使用反編譯工具apktoul.exe,如下圖

最後,開啟 AndroidManifest.xml 檔案,如下圖:

結果證明: cn.teachcourse 被 cn.teachcourse.demo 代替

正是因為打包的apk檔案的 package 的屬性值被 applicationId 代替,也剛好說明為什麼應用程式安裝到手機後,手機上顯示的是 applicationId ,而不是顯示 package ,同時如果想在應用程式中接入第三方的API,填寫的包名也必須是 applicationId ,常見的例子有:
1.接入微信的支付功能
2.接入微信的分享功能
3.整合百度地圖的定位功能

那麼,AndroidManifest.xml的 package 到底有什麼用呢?儘管, package 在打包成apk的時候被 applicationId 代替,但是在構建的時候 package 有兩方面的作用:

第一個作用:在 package 指定的目錄下,生成對應的 R.java 檔案,上面的例子,構建的時候,生成 R 檔案的目錄,如下圖:

app\build\generated\source\r\demo\debug\cn\teachcourse\R.java

第二個作用:在 package 指定的目錄下,引用對應的 activity 、 server 元件,如下圖:

<!-- 定義Activity -->
<activity android:name=".MainActivity"/>

<!-- 新增service元件 -->
<service android:name=".service.music.MusicService" />

在上面反編譯的AndroidManifest.xml檔案中,檢視對應的元件目錄,如下圖:

也就是說,manifest指定的元件不管使用相對路徑還是絕對路徑,打包成apk檔案後,都變成絕對路徑,結構是:package.元件

需要特別注意的問題有:

第一個問題:程式碼中使用 getPackageName() 或 getPackageManager() 對應的方法,返回的是 applicationId 屬性值

第二問題:使用 WebView 基本存放於 res/raw 內的檔案時,如果 applicationId 不等於 package ,提示 ClassNotFoundException 異常(可能是官方的bug),測試後找到兩個解決的辦法

嘗試將 res/raw/note.html 檔案移動到 assets 資料夾下,更換資原始檔載入路徑

mWebView.loadUrl("file:///android_asset/note.html");

保持 applicationId 屬性值和 package 屬性值一致

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="cn.teahcourse.demo">
     ...
    </manifest>

2. 配置安全的自定義簽名

自定義簽名指的是使用開發者匯出的金鑰庫對apk檔案進行簽名,關於怎麼生成自己的金鑰庫,不懂的同學,可以後面看一下這篇文章《 Android Studio執行時自帶簽名配置過程詳解 》,文章介紹了怎麼配置Android Studio的執行時簽名,這樣做的目的:在接入一些需要自定義簽名的API時,方便直接除錯。

這裡,介紹的是安全的自定義簽名,即怎麼才讓別人看不到我們在 build.gradle 寫入的密碼(包括別名密碼、金鑰庫密碼),關於簽名檔案的重要性,在這裡就不說了。

2.1 配置安全的自定義簽名(1),步驟:

  • 在專案的根目錄下建立一個名稱為 keystore.properties 的檔案。此檔案應當包含您的簽署資訊,如下所示:
storePassword=myStorePassword
keyPassword=mykeyPassword
keyAlias=myKeyAlias
storeFile=myStoreFileLocation

這裡需要注意:keystore.properties中 storeFile 簽名檔案是相對module目錄的路徑,即將金鑰庫檔案儲存在module根目錄下

  • 在模組的 build.gradle 檔案中,於 android {} 塊的前面新增用於載入 keystore.properties 檔案的程式碼。
...
def keystorePropertiesFile = rootProject.file("keystore.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
android {
 ...
}

注:您可以選擇將 keystore.properties 檔案儲存在其他位置(例如,儲存在模組資料夾中而不是專案的根資料夾中,或者如果您使用連續整合工具,也可以儲存在構建伺服器上)。在這種情況下,您應當修改上面的程式碼,以便使用實際 keystore.properties 檔案的位置正確初始化 keystorePropertiesFile。

  • 可以使用語法 keystoreProperties[‘屬性名稱’] 引用儲存在 keystoreProperties 中的屬性。修改模組 build.gradle 檔案的 signingConfigs 塊,以便使用此語法引用儲存在 keystoreProperties 中的簽署資訊。
android {
 signingConfigs {
     config {
         keyAlias keystoreProperties['keyAlias']
         keyPassword keystoreProperties['keyPassword']
         storeFile file(keystoreProperties['storeFile'])
         storePassword keystoreProperties['storePassword']
     }
 }
 ...
}
  • 最後,我們就可以按照《Android Studio執行時自帶簽名配置過程詳解》介紹的方式,將 signingConfigs 塊作用於release版本或debug版本

2.2 配置安全的自定義簽名(2),步驟:

第二種安全的自定義簽名的方式是:
將別名、別名密碼、金鑰密碼以鍵值對的形式儲存到當前電腦的環境變數中,然後通過變數名讀取變數值

android {
 signingConfigs {
     config {
         keyAlias System.getenv("KEYALIAS")
         keyPassword System.getenv("KEYPWD")
         storeFile file('release.jks')
         storePassword System.getenv("KSTOREPWD")
     }
 }
 ...
}

KEYALIAS指的是環境變數的變數名, System.getenv(“KEYALIAS”) 的讀取變數名對應的變數值,如下圖:

KEYPWD,按照上圖的方式新增,如下圖:
這裡寫圖片描述

KSTOREPWD以同樣的方式,如下圖:
這裡寫圖片描述

需要特別注意的是:第二種自定義簽名的方式,需要先檢查Android Studio是否已配置了 gradle 工具的環境變數,開啟Android Studio的terminal視窗,輸入: gradle build ,如下圖

這裡寫圖片描述

如果,你的terminal視窗提示gradle不是內部命令,操作上述步驟之前,你得新增 gradle 工具的環境變數,Android Studio的 gradle 工具預設存放路徑:

C:\Program Files\Android\Android Studio\gradle\gradle-3.2

配置 gradle 的環境變數,如下圖:

這裡寫圖片描述
這裡寫圖片描述

三. 兩種構建型別的區別

每一個APP至少包含 debug 和 release 兩種構建型別, debug 定義APP的除錯版本, debug 模式的幾個特點:

  • 支援斷點除錯和log資訊列印, debuggable 屬性值為 true
  • 使用系統預設的金鑰庫簽署apk檔案
  • 沒有對apk檔案進行程式碼和資原始檔的優化(包括檔案壓縮、冗餘檔案刪除)
  • 沒有對程式碼進行混淆

release 定義APP的釋出版本,建立專案module中的 build.gradle 檔案,程式碼如下:

buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}

minifyEnable 定義是否壓縮程式碼, false 表示不壓縮; proguardFiles 定義混淆程式碼的預設混淆規則, proguard-android.txt 表示系統自帶的混淆規則, proguard-rules.pro 位於當前module根目錄下,用於定義開發者自己的混淆規則。

release 模式需要注意的幾個特點:

  • 不支援斷點除錯, debuggable 預設為false
  • 沒有壓縮類檔案程式碼, minifyEnabled ,預設為false
  • 沒有壓縮資原始檔, shrinkResources ,預設為false
  • 沒有指定自定義簽名檔案,預設使用系統的金鑰庫簽署apk

開發者在釋出應用程式時,需要對 release 模式下的屬性配置進行修改,優化apk檔案,刪除無用的程式碼和資原始檔,混淆類檔案和資源名稱,自定義簽名金鑰庫,程式碼如下:

release {
    shrinkResources true
    minifyEnabled true
    useProguard true
    proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    signingConfig signingConfigs.config
}

總結: debug 和 release 模式,最大的區別預設屬性配置不一樣,兩種模式支援的屬性配置還包括,如下圖:

debug
這裡寫圖片描述

release
這裡寫圖片描述

記不住程式碼的同學,可以選中Build Types定義的模式,在可選項中改變對應屬性配置,Android Studio執行時簽名的實質將 debug 模式下的 Signing Config 設定為自定義金鑰庫檔案,但是TeachCourse隨著不斷深入學習後發現,其實 debug 模式下配置 Signing Config 是 多此一舉 ,而只要在 release 模式下配置 Signing Config 就夠了,Android Studio的可以方便為我們生成兩種模式下對應的apk檔案,在Android Studio的左下角Build Variant中切換,如下圖:

這裡寫圖片描述

下面介紹了 產品偏好配置 後,回頭再看看它們兩者之間的關係。

四. 為什麼要定製產品的偏好配置?

什麼是產品的偏好配置呢?比如說,TeachCourse想要開發一個應用程式,包含個人版本 personal 和企業版本 enterprise ,這兩個版本之間在功能上有所區別,企業版自然比個人版功能要多一些,很明顯就是要就一個Android專案打包成兩個產品釋出,它們之間的要求如下所示:

personal:版本號為1,最低SDK版本定義為11,最高SDK定義為24,版本名稱字尾定義為-personal,applicationId字尾定義為-per,簽名檔案為自定義金鑰庫,程式碼如下:

personal {
    versionCode 1
    minSdkVersion 11
    targetSdkVersion 24
    versionNameSuffix '-personal'
    applicationIdSuffix '-per'
    signingConfig signingConfigs.config
}

enterprise:版本號為1000,最低SDK版本定義為11,最高SDK定義為24,版本名稱字尾定義為-profession,applicationId字尾定義為-pro,簽名檔案為自定義金鑰庫程式碼如下:

enterprise {
    versionCode 1000
    minSdkVersion 11
    targetSdkVersion 24
    versionNameSuffix '-profession'
    applicationIdSuffix 'full'
    signingConfig signingConfigs.config
}

同時,定義第三個產品偏好配置為demo,用於上傳GitHub,提供下載,程式碼如下:

demo {
    applicationId "cn.teahcourse.demo"
    versionName "1.0-demo"
    signingConfig signingConfigs.config
}

一個Android專案,配置三個偏好的產品,即使修改了專案程式碼,也可以快速編譯並打包三個apk檔案,在Android Studio的左下角Build Variant中切換,如下圖:

這裡寫圖片描述

看上面的圖片,你是不是發現了什麼,突然間,三個偏好配置的產品,出現了 6 個變體,一個產品包含 debug 和 release 兩個版本,構建型別和偏好產品之間的關係是:一個偏好產品,肯定包含一個debug版本和一個release版本,可以生成變體的總數為flavors2*,選中需要除錯的版本或選中需要釋出的版本,Android Studio自動重新構建Android專案,就可以針對指定的產品進行除錯或打包,非常的方便吧!偏好產品相關配置,如下圖:

這裡寫圖片描述

defaultConfig 也屬於其中一種偏好產品,在我們沒有定義自己的偏好產品時,我們構建和編譯的就是預設的 defaultConfig 這個產品,也就只包含 debug 和 release 兩個變體。

五. 怎麼才能加快DEX檔案的生成速度?

你有沒有遇到Android Studio在每次構建的時候,都感覺花好長時間,TeachCourse就不止一次和同事抱怨說,Android Studio的編譯速度還不如Eclipse快,蝸牛的速度真受不了呀?那該怎麼辦呢?

Android Studio提供 dexOption 區塊以便於我們配置 DEX 構建屬性,加快DEX檔案的生成速度,程式碼如下:

dexOptions {
    preDexLibraries true
    maxProcessCount 8
    javaMaxHeapSize "2048m"
  }
  • preDexLibraries 宣告是否預先編譯依賴庫,從而加快構建速度,實質是通過延時清除已生成的依賴庫的構建檔案,從而提高構建速度,根據使用情況合理配置。
  • maxProcessCount 設定程序執行過程中可以使用的最大執行緒數。預設值為4。
  • javaMaxHeapSize 設定DEX編譯器的最大堆大小,堆或者棧都是用於存放暫時不用的垃圾,當記憶體不足時,垃圾回收機制會清除過時的快取,堆大小決定垃圾清除的頻率,影響著構建的速度

六. 為什麼要將一個apk拆分成多個?

根據以往的經驗,一個apk檔案可以支援不同螢幕密度和不同ABIs的手機裝置,是因為我們進行了螢幕適配,做法:將市場主流的螢幕密度和ABIs整合到一個apk,造成的影響,如果你的應用程式本身就比較大,集成了不同螢幕密度和支援不同ABIs的程式碼,打包出來的apk檔案變得更大,考慮到流量成本和使用者體驗,減少apk檔案的大小其中一種方式將一個apk檔案拆分成多個。

Gradle能夠單獨指定只包含一種螢幕密度或一種ABI程式碼和資原始檔的apk,在 build.gradle 檔案中使用到 splits 區塊, splits 區塊內同時提供了按螢幕密度拆分的 density 區塊和按abi拆分的 abi 區塊,在一個 build.gradle 檔案中可以同時指定兩者或兩者中的其中一者,下面分別介紹:

6.1 按螢幕密度拆分

android {
  ...
  splits {

    density {
      enable true
      exclude "xxxhdpi"
      reset() 
      include "ldpi", "xxhdpi"
      compatibleScreens 'small', 'normal', 'large', 'xlarge'
    }
  }
}

上面是一個按螢幕密度拆分的一個例子,各個標籤的含義是:

  • enable ,是否基於定義的螢幕密度拆分成多個apk檔案,預設為false
  • exclude ,指定忽略拆分的螢幕密度列表,想要拆分成更多型別的apk檔案,該關鍵字包含的螢幕密度列表應就可能少
  • reset() ,重置預設拆分的螢幕密度依據,然後使用 include 標籤定義拆分的螢幕密度依據
  • include ,結合 reset 一起使用,定義拆分的螢幕密度依據
  • compatibleScreens ,指定相容的螢幕尺寸列表,區別於螢幕密度,該標籤將會在清單檔案 manifest 中通過 注入到每一個apk檔案中,即apk檔案只能安裝到 指定尺寸的手機上

按照上面在 build.gradle 配置完成後,點選 Build APK 後,將在apk資料夾內生成多個apk檔案,如下圖:

這裡寫圖片描述

為了驗證是否在清單檔案中注入指定螢幕尺寸,反編譯其中一個apk檔案,如下圖:

這裡寫圖片描述

6.2 按abi拆分

android {
  ...
  splits {

    abi {

      enable true
      reset()
      include "x86", "armeabi-v7a", "mips"
      universalApk false
    }
  }
}

上面是一個按abi拆分的一個例子,除了 universal 標籤不一樣外,其他標籤是一樣的,使用方法一樣, include 標籤定義拆分的abi依據,關於abi介紹,參考下面連線:

同樣,點選 Build APK 後,將在apk資料夾內生成多個apk檔案,如下圖:

這裡寫圖片描述

仔細觀察生成的apk檔案,會發現下面兩個規律:

  • 第一個規律:apk總數=abi數量+density數量xabi數量
  • 第二個規律:apk filename=modulename-screendensityABI-buildvariant.apk

七. 關於引入依賴包你不知道的祕密

dependencies 區塊引入的jar包的名稱長,基本無法記住,每一節又表示什麼含義?Android Studio引入依賴項有幾種方式?讓我先看下面的這個例子:

dependencies {
    compile project(":mylibrary")
    compile files('libs/zxing.jar')
    compile fileTree(include: ['*.jar'], dir: 'libs')
    compile 'com.android.support:appcompat-v7:25.1.0'
    compile group: 'com.android.support', name: 'appcompat-v7', version: '25.1.0'
}

可以看到Android Studio引入依賴項的方式分為上述四種,按順序依次稱為:
1、 模組依賴項
2、 本地二進位制依賴項
3、 本地二進位制依賴項
4、 遠端二進位制依賴項
5、 遠端二進位制依賴項

  • compile project(‘:mylibrary’) 行聲明瞭一個名為 mylibrary 的本地 Android 庫模組作為依賴項,並要求構建系統在構建應用時編譯幷包含該本地模組。
  • compile files(‘libs/zxing.jar’) 和 compile fileTree(dir: ‘libs’, include: [‘*.jar’]) 都稱為本地依賴項,告訴構建系統在編譯類路徑和最終的應用軟體包中包含 app/libs/ 目錄內的指定或全部 JAR 檔案。如果您有模組需要本地二進位制依賴項,請將這些依賴項的 JAR 檔案複製到專案內部的 /libs 中。
  • compile ‘com.android.support:appcompat-v7:25.1.0’ 和 compile group: ‘com.android.support’, name: ‘appcompat-v7’, version: ‘25.1.0’ 都稱為遠端二進位制依賴項,通過指定其 JCenter 座標,針對 Android 支援庫的 25.1.0 版本聲明瞭一個依賴項。預設情況下,Android Studio 會將專案配置為使用頂級構建檔案中的 JCenter 儲存區。當您將專案與構建配置檔案同步時,Gradle 會自動從 JCenter 中抽取依賴項。或者,您也可以通過使用 SDK 管理器下載和安裝特定的依賴項。

第五種可以清楚看出每一節表示的含義,在Android Studio引入遠端二進位制依賴項,通常的做法是在Library Dependency視窗中搜索,搜尋到最新版本的依賴項,如下圖:

這裡寫圖片描述

似乎無法搜尋到低版本的依賴項,如果想要引入低版本的,那該怎麼辦呢?如果先前不瞭解遠端二進位制依賴項的含義,可能想不到修改 version 的辦法,現在就變得很簡單了。

總結:

本篇文章在閱讀Android Studio使用者指南多篇相關文件後完成的,想要更詳細深入學習 gradle 指令的同學,可以繼續研讀Gradle官網文件,部分內容在TeachCourse開發的專案沒有對應的需求,暫時也沒有用到,是否使用更多應該根據專案實際情況而定,但可以作為使用者開發的例子,先分享和收藏,以備不時之需。

以上文章參考資料: