美文网首页
使用 Gradle 配置 Android 工程

使用 Gradle 配置 Android 工程

作者: flueky | 来源:发表于2019-10-19 12:16 被阅读0次

    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 包不允许调试。产品特性可以根据实际需要自行定义,如:收费和免费版本,汉语、英语及日语。不同的特性之间通过维度的概念区分。

    image

    最终如上图所示,生成 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 等。用于实现不同的业务逻辑,资源图片等。

    image

    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>
    

    源码地址

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

    相关文章

      网友评论

          本文标题:使用 Gradle 配置 Android 工程

          本文链接:https://www.haomeiwen.com/subject/vqggmctx.html