欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

使用 Gradle 配置 Android 工程

程序员文章站 2022-06-04 10:34:43
...

0. 前言

自从将 Android 开发工具从 eclipse 迁移到 AndroidStudio 中之后,Android 工程构建越来越方便。见过很多项目中各式各样的 Gradle 配置语法,懵懵懂懂。遂花费近两周时间将官方文档通读一遍并整理出常用 Gradle 配置 Demo,以此博客作为说明。

1. 准备工作

一个最新版本的 AndroidStudio 毋庸置疑。下载地址:请戳。此处使用 3.5 版本。

Gradle 版本 5.4.1 ,无须单独下载,在工程中配置好即可。

2. 基本概念

2.1. Module

软件工程中,通常采用模块化开发的策略,将一个项目划分成若*分,多人协作开发中,每人完成一部分,最终完成整个项目。因此 AndroidStudio 也采用模块化管理的方式编译 Android 工程 。各个模块之间的依赖关系类似于树形结构,主 Module 作为根节点,其余多个库 Module 都直接或简介的被主 Module 引用。

2.2. Project

基于前面的描述,Project 即 AndroidStudio 打开的一个目录,其中包含 N+1 个 Module ,通过 Gradle 构建多个 Module 并最终合并成一个 apk 文件。

其实,在一个 Project 目录中,可以有多个主 Module ,每个主 Module 可以依赖相同的库 Module 并负责生成一个 apk 文件。通常不建议这么使用,因为这样会破坏 Module 之间的树形依赖关系,不利于后期维护。推荐将复用频率较高的库 Module 封装成 aar 文件,通过直接依赖或远程依赖的方式用于不同的主 Module 之中。

2.3. buildType & productFlavor

通过直译的方式 ,可以理解成 构建类型产品特性 。构建类型中,默认包含 debug 和 release 两种类型。一些基本的差异如 :debug 包允许调试,release 包不允许调试。产品特性可以根据实际需要自行定义,如:收费和免费版本,汉语、英语及日语。不同的特性之间通过维度的概念区分。

使用 Gradle 配置 Android 工程

最终如上图所示,生成 2x3x2 共 12 种不同的 apk 文件。

3. 工程结构

精简后的 AndroidStudio 工程结构如下:

├── 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 示例:

// 主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 等。用于实现不同的业务逻辑,资源图片等。

使用 Gradle 配置 Android 工程

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>

源码地址

觉得有用?那打赏一个呗。去打赏

相关标签: Android Gradle