使用 Gradle 配置 Android 工程

0. 前言

自從將 Android 開發工具從 eclipse 遷移到 AndroidStudio 中以後,Android 工程構建愈來愈方便。見過不少項目中各式各樣的 Gradle 配置語法,懵懵懂懂。遂花費近兩週時間將官方文檔通讀一遍並整理出經常使用 Gradle 配置 Demo,以此博客做爲說明。java

1. 準備工做

一個最新版本的 AndroidStudio 毋庸置疑。下載地址:請戳。此處使用 3.5 版本。android

Gradle 版本 5.4.1 ,無須單獨下載,在工程中配置好便可。git

2. 基本概念

2.1. Module

軟件工程中,一般採用模塊化開發的策略,將一個項目劃分紅若干部分,多人協做開發中,每人完成一部分,最終完成整個項目。所以 AndroidStudio 也採用模塊化管理的方式編譯 Android 工程 。各個模塊之間的依賴關係相似於樹形結構,主 Module 做爲根節點,其他多個庫 Module 都直接或簡介的被主 Module 引用。github

2.2. Project

基於前面的描述,Project 即 AndroidStudio 打開的一個目錄,其中包含 N+1 個 Module ,經過 Gradle 構建多個 Module 並最終合併成一個 apk 文件。web

其實,在一個 Project 目錄中,能夠有多個主 Module ,每一個主 Module 能夠依賴相同的庫 Module 並負責生成一個 apk 文件。一般不建議這麼使用,由於這樣會破壞 Module 之間的樹形依賴關係,不利於後期維護。推薦將複用頻率較高的庫 Module 封裝成 aar 文件,經過直接依賴或遠程依賴的方式用於不一樣的主 Module 之中。api

2.3. buildType & productFlavor

經過直譯的方式 ,能夠理解成 構建類型產品特性 。構建類型中,默認包含 debug 和 release 兩種類型。一些基本的差別如 :debug 包容許調試,release 包不容許調試。產品特性能夠根據實際須要自行定義,如:收費和免費版本,漢語、英語及日語。不一樣的特性之間經過維度的概念區分。數組

最終如上圖所示,生成 2x3x2 共 12 種不一樣的 apk 文件。緩存

3. 工程結構

精簡後的 AndroidStudio 工程結構以下:bash

├── app
│   ├── build.gradle
│   ├── proguard-project.txt
│   └── src
│       └── main
│           ├── AndroidManifest.xml
│           ├── java
│           │   └── com
│           │       └── flueky
│           │           └── demo
│           │               └── MainActivity.java
│           └── res
│               ├── layout
│               │   └── activity_main.xml
│               └── values
│                   ├── strings.xml
│                   └── styles.xml
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
複製代碼
  1. 文件夾 app 做爲一個 Module ,必須有 build.gradle 文件,src 文件夾包含所有源文件:Java 文件、資源文件、清單文件等。
  2. 文件 build.gradle 不一樣於 app/build.gradle , 它用於整個工程的配置,而 app/build.gradle 只做用於 app Module 的配置。
  3. gradle 包含使用的 gradle 插件版本 。若未指定 ,AndroidStudio 會自動生成最新的 gradle 插件目錄。
  4. gradlew、gradlew.bat 分別是 Mac/Linux 和 Windows 的腳本文件。關聯於 gradle 插件版本,不可單獨刪除。
  5. settings.gradle 包含整個工程的全部 Module 的聲明。

settings.gradle 示例:app

// 主Module
include ':app'
// 庫Module
include ':library'

// 添加外部庫
include 'other-lib'
project(':other-lib').projectDir = new File(rootDir, "../other-sample/library")
複製代碼

4. 配置 Project

4.1. 構建腳本

buildscript {
    repositories {
        // 倉庫地址
        google()
        jcenter()
        mavenCentral()
    }
// 工程的依賴配置
    dependencies {
        // 必須包含的 gradle 版本的構建工具。
        classpath 'com.android.tools.build:gradle:3.4.1'
    }
}
複製代碼

4.2. 全工程配置

// 所有工程配置,做用於每一個 Module
allprojects {
    repositories {
        // 倉庫地址
        google()
        jcenter()
        mavenCentral()

        // 依賴倉庫,添加用於查找依賴項的目錄,能夠多個。
        flatDir {
            // jar、aar 存放的目錄
            dirs 'libs'
        }
    }
}
複製代碼

4.3. 擴展變量

// 擴展變量,用於統一管理每一個 Module 的有關配置
ext {
    // 應用 id
    applicationId = "com.flueky.demo"
    // 統一控制各個 Module 使用的 SDK 版本
    compileSdkVersion = 29
    buildToolsVersion = "29.0.0"
    minSdkVersion = 19
    targetSdkVersion = 29

    // 定義 Module 的版本號
    app = [
            versionCode: 1,
            versionName: '1.0.0'
    ]
}
複製代碼

4.4. 加載配置文件

// 加載其餘的 gradle 配置文件,可選
apply from: "config.gradle"
複製代碼

5. 配置 Module

5.1. 加載插件

// 聲明 Module 是主 Module,生成 apk 文件
apply plugin: 'com.android.application'
// 聲明 Module 是庫 Module,生成 aar 文件
apply plugin: 'com.android.library'
// 聲明 Module 是 Java 庫,生成 jar 文件
apply plugin: 'java-library'
複製代碼

還有不少 Plugin 能夠用,如: java web maven-publish 等。

另外一種寫法,一個 Module 中使用多個插件,如:

plugins {
    id 'java'
    id 'war'
    id 'maven-publish'
}
複製代碼

5.2. 使用SDK

android {
    // 定義編譯的 sdk 版本,只有使用最新版本的sdk,才能在代碼中只用最新的 api 方法
    compileSdkVersion rootProject.ext.compileSdkVersion
    // 定義構建工具的版本
    buildToolsVersion rootProject.ext.buildToolsVersion
}
複製代碼

rootProject.ext 是在 Project 的 build.gradle 文件中聲明。

5.3. 默認配置

android{
    defaultConfig {
        // 應用 id
        applicationId rootProject.ext.applicationId
        // 最小 sdk 版本,低於此 Android 版本的手機不能安裝
        minSdkVersion rootProject.ext.minSdkVersion
        // 目標 sdk 版本,低於此 Android 版本的手機完美兼容,高於此 Android 版本的手機,部分特性不能使用
        // 升級 target 須要針對高版本作兼容。
        targetSdkVersion rootProject.ext.targetSdkVersion
        // 應用版本號,覆蓋安裝時,升級版本依據
        versionCode rootProject.ext.app.versionCode
        // 版本名稱,
        versionName rootProject.ext.app.versionName
        // 指定須要編譯 abi 版本的 so
        ndk{
            abiFilters 'armeabi-v7a'
        }
        // 設置編譯的資源
        resConfigs "zh-rCN"
        // 多 dex 支持 ,minSdkVersion >= 20 使用 
        multiDexEnabled true
    }
}
複製代碼

5.4. 簽名配置

簽名文件,至少配置一個。如未使用,debug 包,會使用 user home/.android/debug.keystore 的簽名文件。 release 包默認不簽名。

// 能夠將簽名文件信息配置在 keystore.properties 文件中
def ksPropFile = rootProject.file("keystore.properties")
def ksProp = new Properties()
// 加載簽名配置文件
ksProp.load(new FileInputStream(ksPropFile))

android{
    signingConfigs {
        // 生產簽名,讀取配置文件
        release {
            keyAlias ksProp['keyAlias']
            keyPassword ksProp['keyPassword']
            storeFile file(ksProp['storeFile'])
            storePassword ksProp['storePassword']
        }
        // 測試簽名,靜態配置
        debug {
            keyAlias 'flueky'
            keyPassword 'android'
            storeFile file('../demo.keystore')
            storePassword 'android'
        }
    }
}
複製代碼

使用配置的好處是,將生產簽名文件信息保存在配置文件中,不添加到版本控制工具中,能夠有效防範簽名文件信息泄露,被他人使用。
針對簽名文件的校驗,會有專門的防篡改技術,防止應用被反編譯 apk 後二次打包。如簽名文件泄露,會構成很大威脅。

5.5. 構建類型

android{
    buildTypes {
        // 測試版本
        debug {
            // 容許 Java 代碼調試(默認容許)
            debuggable true
            // 容許 JNI 代碼調試(默認容許)
            jniDebuggable true
            // 簽名 信息
            signingConfig signingConfigs.debug
            // 版本名稱後綴
            versionNameSuffix = '-beta'
            // 應用 Id 後綴
            applicationIdSuffix '.debug'
        }
        // 發行版本
        release {
            // 禁止 Java 代碼調試(默認禁止)
            debuggable false
            // 禁止 JNI 代碼調試 (默認禁止)
            jniDebuggable false
            signingConfig signingConfigs.release
            // build/intermediates/proguard-files 目錄下存在三個混淆配置文件
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
            // 容許代碼壓縮、混淆、優化,默認狀況下會使用 R8 壓縮
            minifyEnabled true
            // 容許資源壓縮,包括清理無用資源。在生成 apk 時,如需保留被刪除的圖片使用,keep.xml 聲明 。
            shrinkResources true
            // 配置須要放在主 dex 中的類
// multiDexKeepFile file('multidex-config.txt')
            // 或者使用下面的配置,語法同 proguard file
// multiDexKeepProguard file('multidex-config.pro')
        }
        // 內部使用版本
        inner {
            // 使用 debug 的配置
            initWith debug
            applicationIdSuffix '.inner'
        }
    }
}
複製代碼

AndroidStudio 默認支持 debug 和 release 兩種類型。並有一些默認的配置。經常使用配置見上。

5.6. 產品特性

android{
    // 定義兩種緯度
    flavorDimensions 'stage', 'api'

    productFlavors {
        // 開發階段
        dev {
            dimension 'stage'
        }
        // 生產階段
        pro {
            dimension 'stage'
        }

        minApi21 {
            dimension 'api'
            minSdkVersion 21
        }
        minApi23 {
            dimension 'api'
            minSdkVersion 23
        }
        minApi26 {
            dimension 'api'
            minSdkVersion 26
        }
    }
}
複製代碼

產品特性和構建類型,不只僅是在構建的時候修改些配置,還能夠在 src 目錄下,定義同名的文件夾,存放 java 、res 、assets 和 AndroidManifest.xml 等。用於實現不一樣的業務邏輯,資源圖片等。

5.7. 過濾變體

產品特性和構建類型能夠經過組合的方式生成多個 apk 文件。但在實際中可能不須要部分組合方式。忽略後,可加速編譯過程。

如,內部使用不須要生產階段的 apk 文件,配置以下:

android{
    // 設置不須要生成 apk 的類型和特性
    variantFilter { variant ->
        // 將多個 flavor 組合轉成字符串數組
        def names = variant.flavors*.name
        // 獲取到 buildType 名稱
        def type = variant.buildType.name
        // 忽略部分不須要生成的 apk
        if (names.contains('pro') && type.equals("inner")) {
            setIgnore(true)
        }
    }
}
複製代碼

5.8. 細分APK

android{
    splits {
// 注意和 nkd.abiFilters 的衝突
        abi {
            enable true
            // 包含全部版本so 的apk 文件。此配置僅用於 abi 。
            // density 中默認會生成包含全部資源的 apk
            universalApk true
            // 去除 x86 和 x86_64
            exclude 'x86', 'x86_64'
        }
// 注意和 resConfigs 的衝突
        density {
            enable true
            reset()
            include "xhdpi"
// compatibleScreens 'small', 'normal', 'large', 'xlarge'
        }
    }
}
複製代碼

universalApk 只用在 abi 的配置中。true 表示按照默認的方式將所有 so 打包 。density 中,此值必須爲 true。 使用 abi 和 density 要注意同 ndk.abiFilters 和 resConfigs 使用的衝突。

5.9. 添加依賴

依賴第三方庫,在新版本 Gradle 中,經常使用的有 implementationapi

它們區別是:

  1. A 中 implementation B , B 中 implementation C 。 B 可使用 C 的類和方法,A 可使用 B 的類和方法。
  2. A 中 implementation B , B 中 api C 。 B 可使用 C 的類和方法,A 可使用 B 的類和方法。A 還可使用 C 的類和方法。

這樣的好處是,若是隻修改了 Module C , 狀況 1 只會從新編譯 B 和 C ,狀況 2 會從新編譯 A B C 。 所以在實際應用中,避免大量使用 api 的方式。

下面列舉了對 jar 文件、 aar 文件和 maven 庫文件的依賴方式。

dependencies {
    // 依賴同級的 libs 目錄,只包含jar
    implementation fileTree(include: '*.jar', dir: 'libs')
    // 依賴同級的 libs 目錄,包含 jar 和 aar,還能夠選擇不包含指定文件
    implementation fileTree(dir: 'libs', include: ['*.aar', '*.jar'], exclude: [])
    // debug 類型的依賴
    debugImplementation fileTree(include: '*.jar', dir: 'src/debug/libs')
    // release 類型的依賴
    releaseImplementation fileTree(include: '*.jar', dir: 'src/release/libs')
    // 同理 devImplementation minApi21Implementation innerImplementation
    // 若是 須要組合使用,如 devMinApi21DebugImplementation,見 configurations

    // 依賴本地 module
    implementation project(':library')
    implementation project(':other-lib')
    // 等同於帶 path 參數
// implementation project(path: ':library')
    // 依賴遠程庫,不建議在版本號中使用 + 通配符,括號能夠省略
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation('com.android.support:support-v4:28.0.0') {
        // 一個遠程庫可能包含多個第三方庫,能夠排除指定庫
        exclude group: 'com.android.support', module: 'collections'
    }
    // 等同於複雜形勢
// implementation group: 'com.android.support', name: 'support-v4', version: '28.0.0'
    implementation 'com.google.dagger:dagger:2.24'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.24'
    // 5.0 以前多 dex 支持,minSdkVersion < 20 必須使用。
    implementation 'androidx.multidex:multidex:2.0.1'
}
複製代碼

注意到上面的配置中存在 debugImplementation 和 releaseImplementation 。這是對不一樣構建類型單獨指定的依賴方式。

產品特性也可使用上面的配置,可是須要先聲明,方式以下:

configurations {
    // 添加多個依賴項配置
    devMinApi21DebugImplementation {}
    devMinApi21Api {}
    devDebugCompileOnly {}
    minApi21DebugRuntimeOnly {}
}
複製代碼

篇幅有限 ,以上介紹均是經常使用配置。更多配置請見源碼

  1. 修改生成的 apk 文件目錄。
  2. 修改生成的 apk 文件名稱。
  3. 動態修改版本號。
  4. 替換 build 緩存目錄。
  5. AndroidManifest.xml 注入變量。

關於 AndroidManifest.xml 合併衝突,有機會在後面的文章中講。

6. 常見問題

6.1. 問題1

主 Module 有 inner 的構建類型,庫 Module 中、沒有 ,構建 app 時會報錯。

可在庫 Module 中添加 inner 的構建類型,或者在主 Module 的 inner 類型中,添加下面的配置。

// inner 匹配失敗 , debug 匹配成功, release 忽略
    matchingFallbacks = ['inner', 'debug', 'release']
複製代碼

如需匹配 release 時,將 release 放在 debug 前,或者直接刪除 debug 。

6.2. 問題2

主 Module 有的產品特性,庫 Module 中沒有,構建 app 時會報錯。

錯誤緣由同 問題 1 。如,庫 Module 中存在其餘相似的產品特性,可以使用匹配的方式 ,以下:

android{
    productFlavor{
        minApi21 {
            minSdkVersion 21
            // 庫 module 沒有 minApi21 ,匹配較高版本
            // 同時須要在 AndroidManifest.xml 中使用 overrideLibrary
            matchingFallbacks = ['minApi23']
        }
        minApi26 {
            minSdkVersion 26
            // 庫 module 沒有 minApi26 ,匹配較低版本,可兼容
            matchingFallbacks = ['minApi23']
        }
    }
}
複製代碼

如,庫 Module 中沒有定義任何產品特性,能夠直接在 defauleConfig 中,忽略對維度的依賴 。

android{
    defauleConfig{
        // 庫 module 沒有開發進度的維度,所以忽略
        missingDimensionStrategy 'dev', 'pro'
    }
}
複製代碼

6.3. 問題3

主 Module 的 minSdk 小於 庫 Module 的 minSdk。

使用 overrideLibrary ,指定複寫庫 Module 的 packageName 。

<manifest xmlns:tools="http://schemas.android.com/tools">
    <!-- minSdkVersion 存在衝突的解決方案-->
    <uses-sdk tools:overrideLibrary="com.flueky.library" />
</manifest>
複製代碼

源碼地址

以爲有用?那打賞一個唄。去打賞

相關文章
相關標籤/搜索