一文了解 Android项目中build.gradle中的 android 配置扩展

707 阅读16分钟

在Android 项目中,我们常常需要配置 build.gradle 文件中的 android {...} 配置。而在 一文了解 Gradle 插件 文章中我们可以知道,android {} 其实是 Android Gradle Plugin(简称AGP) 需要的配置扩展。这里以 AGP 8.6 为例,详细介绍Android配置扩展。

ApplicationExtension 和 LibraryExtension

在 Android 项目中,一般有一个应用模块和多个库模块。其中应用模块使用的插件是 com.android.application;而库模块使用的是 com.android.library。如下代码所示:

// 声明是Android程序,
//com.android.application 表示这是一个应用程序模块
apply plugin: 'com.android.application'
//com.android.library 标识这是一个库模块
//而这区别:前者可以直接运行,后着是依附别的应用程序运行
apply plugin: 'com.android.library'

其中 com.android.applicationcom.android.library 插件内部分别使用不同的 Extension,分别是 ApplicationExtensionLibraryExtension。关于 Extension 可以见 一文了解 Gradle 插件

ApplicationExtensionLibraryExtension 配置扩展的代码示例如下所示:

// 应用程序插件的扩展配置
interface ApplicationExtension :
    CommonExtension<
            ApplicationBuildFeatures,
            ApplicationBuildType,
            ApplicationDefaultConfig,
            ApplicationProductFlavor,
            ApplicationAndroidResources,
            ApplicationInstallation>,
    ApkExtension,
    TestedExtension {
    ...
}

// 库模块插件的扩展配置
interface LibraryExtension :
    CommonExtension<
        LibraryBuildFeatures,
        LibraryBuildType,
        LibraryDefaultConfig,
        LibraryProductFlavor,
        LibraryAndroidResources,
        LibraryInstallation>,
    TestedExtension {
    ...
}

interface CommonExtension<
        BuildFeaturesT : BuildFeatures, // 可以禁用或启用的构建功能。例如,是否启用数据绑定、视图绑定、Compose 等
        BuildTypeT : BuildType, // 用于配置项目的构建类型。构建类型决定了如何构建和打包项目的每个版本。默认定义的构建类型有 debug 和 release
        DefaultConfigT : DefaultConfig, // 用于指定所有构建变体的默认配置。可以在此块中配置应用的默认属性,如 applicationId、minSdkVersion、targetSdkVersion 等
        ProductFlavorT : ProductFlavor, // 用于配置项目的构建变体。可以在此块中配置不同版本的特性、设备要求、资源和应用 ID
        AndroidResourcesT : AndroidResources, // 用于配置 Android 资源处理相关的选项。例如,资源压缩、资源过滤等。
        InstallationT: Installation // adb 工具的本地安装选项
        > {
   ...    
}        

可以看到 ApplicationExtensionLibraryExtension 的区别主要是在 CommonExtension 设置的不同泛型上,其他的则是共用的配置。

查看配置扩展的技巧

依赖AGP源码

如果你需要查看源码,建议使用 implementation 来依赖引用。代码示例如下:

dependencies {
    implementation gradleApi()
    implementation 'com.android.tools.build:gradle:8.6.0'
}

配置 lambdas 类信息

为了方便我们查看配置的 Extension 信息,可以在 Android studio 中通过 setting -〉Inlay Hint 来让IDE 帮我们显示具体的类型。如下图所示:

屏幕截图 2025-02-12 194337.png

这样我们就可以查看 gradle kts中的相关配置信息了,如下图所示:

image.png

CommonExtension

CommonExtension 是通用的配置属性,其作用如下所示:

android {
    // 指定 Android 项目的唯一标识符,替代旧的编译时包名概念。从 Android Gradle 插件 7.0 开始引入,使得在多模块项目中可更灵活管理包名
    // 示例:定义一个名为 com.example.android.recipes.recipe 的命名空间
    namespace = "com.example.android.recipes.recipe"

    // 指定编译应用代码时所使用的 Android SDK 版本。编译器会依据该版本的 SDK 提供的 API 进行代码检查和编译
    // 示例:使用 Android SDK 版本 34 来编译代码
    compileSdk = 34

    // 用于指定使用 Android 预览版 SDK 进行代码编译。当想提前使用未正式发布的新版本特性时使用,但预览版可能不稳定
    // 示例:使用代号为 UpsideDownCake 的预览版 SDK 进行编译
    compileSdkPreview = "UpsideDownCake"

    // 支持 Android 平台扩展,可在不依赖完整 Android 系统版本更新的情况下使用新功能和 API
    // 示例:使用 Android SDK 版本 31 编译,并使用平台扩展版本 1 提供的 API
    compileSdkExtension = 1

    // 为项目中的资源 ID 设定一个前缀,在大型或多模块项目中可避免资源 ID 冲突
    // 示例:所有资源 ID 都会以 recipes_ 开头
    resourcePrefix = "recipes_"

    // 指定 Android NDK(Native Development Kit)的路径,用于进行 C、C++ 等原生代码开发
    // 示例:指定 NDK 的安装路径
    ndkPath = "/Users/user/Library/Android/sdk/ndk/25.1.8937393"

    // 指定项目使用的 NDK 版本,确保项目在不同环境下的稳定性和兼容性
    // 示例:使用 NDK 版本 25.1.8937393
    ndkVersion = "25.1.8937393"

    // 指定用于构建 Android 应用的构建工具版本,包含编译、打包、签名等工具
    // 示例:使用构建工具版本 34.0.0
    buildToolsVersion = "34.0.0"

    // 支持随Android SDK附带的可选平台库
    // 示例:引入 org.apache.http.legacy 系统库
    useLibrary("org.apache.http.legacy")

    // 配置 Java 编译选项,可指定源代码和编译生成的字节码的 Java 版本
    // 示例:指定源代码和字节码的 Java 版本为 1.8
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }

    // 数据绑定功能的配置
    // 示例:启用数据绑定功能
    dataBinding {
        enable = true
    }

    // 启用或禁用视图绑定功能,会为每个布局文件生成一个绑定类,简化视图引用
    // 示例:启用视图绑定功能
    viewBinding {
        enable = true
    }

    // 代码测试覆盖率功能的配置
    testCoverage {
        
    }

    // 配置 Lint 工具,Lint 是静态代码分析工具,可检查代码中的潜在问题
    // 示例:当 Lint 检查出错误时不终止构建
    lint {
        abortOnError = false
    }

    // 配置打包选项,可排除不需要打包到 APK 中的文件,避免文件冲突或减小 APK 体积
    // 示例:排除 /META-INF 目录下的 AL2.0 和 LGPL2.1 文件
    packaging {
        resources {
            excludes.add("/META-INF/{AL2.0,LGPL2.1}")
        }
    }

    // 配置应用的签名信息,发布应用时需要对 APK 进行签名
    // 示例:配置发布版本的签名信息
    signingConfigs {
        create("release") {
            storeFile = file("my-release-key.jks")
            storePassword = "password"
            keyAlias = "my-key-alias"
            keyPassword = "password"
        }
    }

    // 配置外部原生构建系统,如 CMake 或 ndk-build,用于 C、C++ 等原生代码开发
    // 示例:使用 CMake 构建原生代码,指定 CMakeLists.txt 文件路径
    externalNativeBuild {
        cmake {
            path = file("CMakeLists.txt")
        }
    }

    // 配置测试选项,可设置单元测试的相关参数
    // 示例:在单元测试中包含 Android 资源
    testOptions {
        unitTests {
            isIncludeAndroidResources = true
        }
    }

    // 配置 APK 拆分选项,可根据不同的 CPU 架构(ABI)、屏幕密度等条件将 APK 拆分成多个更小的 APK
    // 示例:根据 ABI 进行拆分,包含 armeabi-v7a 和 x86 架构,不生成通用 APK
    splits {
        abi {
            isEnable = true
            reset()
            include("armeabi-v7a", "x86")
            isUniversalApk = false
        }
    }

    // Compose功能的可选设置。比如指定Kotlin Compiler版本,一般用默认
    // 示例:指定 Kotlin 编译器用于 Compose 的扩展版本为 1.4.3
    composeOptions {
        kotlinCompilerExtensionVersion = "1.4.3"
    }

    // 配置项目的源集,可指定 Java 代码、资源文件等的源目录
    // 示例:指定主源集的 Java 代码和资源文件的源目录
    sourceSets {
        getByName("main") {
            java.srcDirs("src/main/java")
            res.srcDirs("src/main/res")
        }
    }
}

namespace 和 applicationid 的区别?

  • namespace:
    • 是在编译过程中用于确定代码中的包结构和资源的命名空间。它影响的是 Android 项目中类、资源等的编译和组织方式。例如,在生成 R 文件(资源索引文件)时,namespace 会作为 R 文件的包名。
    • 在多模块项目中,namespace 可以帮助清晰地划分各个模块的作用域,避免命名冲突。不同模块可以有不同的 namespace,使得代码结构更加清晰和易于管理。
  • applicationId:则是用于唯一标识一个应用,它是设备和应用商店识别应用的关键信息。

splits 和构建变体的区别?

  • splits:splits 主要用于将 APK 按照特定的维度进行拆分,目的是减小 APK 的体积,让用户可以根据自己设备的需求下载更小、更适配的 APK 文件,提高下载和安装效率。常见的拆分维度包括 CPU 架构(ABI)、屏幕密度等。
  • 构建变体(Build Variants):构建变体是不同构建类型(Build Types)和产品风味(Product Flavors)的组合。它允许开发者为不同的场景和目标用户创建多种版本的应用,例如开发版、测试版、正式版,或者面向不同市场、不同客户群体的版本

BuildFeatures

默认的 BuildFeatures 内部定义了构建功能相关属性,可以禁用或启用对应的构建功能,属性及其作用如下所示:

// 配置 BuildFeatures
buildFeatures {
    // 启用 AIDL 编译,默认为 false
    aidl = true 
    // 启用 Jetpack Compose,默认为false
    compose = true 
    // 启用 BuildConfig 类生成,默认为false
    buildConfig = true 
    // 启用从 AAR 导入 Prefab 依赖项,Prefab是一个为预构建的C/C++库生成构建系统集成的工具
    prefab = true 
    // 启用 RenderScript 编译,默认为 false
    renderScript = true 
    // 启用资源值生成,默认为 true
    resValues = true 
    // 启用着色器编译,默认为 true
    shaders = true 
    // 启用视图绑定,默认为 false
    viewBinding = true 
}

而在 application 和 library 插件中使用的是 ApplicationBuildFeatures 和 LibraryBuildFeatures。它们分别扩展了 BuildFeatures 的属性。

ApplicationBuildFeatures 增加的扩展属性如下:

// ApplicationBuildFeatures 增加的属性
buildFeatures {
    // 启用数据绑定,默认为 false
    dataBinding = true 
    // 启用启用机器学习模型绑定,默认为false
    mlModelBinding = true 
}

LibraryBuildFeatures 增加的扩展属性如下:

// LibraryBuildFeatures 增加的属性
buildFeatures {
    // 启用数据绑定,默认为 false
    dataBinding = true 
    // 启用启用机器学习模型绑定,默认为false
    mlModelBinding = true 
    // 是否禁用 Android 资源处理,默认为 true
    // 如果为 false,会连带禁用数据绑定、视图绑定和 RenderScript 相关功能
    androidResources = true
    // 是否启用为 AAR 生成 Prefab 包的功能,默认为 false
    prefabPublishing = true
}

prefab的作用和使用

Prefab 是为预构建的 C/C++ 库生成构建系统集成的工具。详情可以见 Prefab | prefab。而它具体的属性配置的使用可以见Android Gradle 插件可以使用的原生依赖项

BuildType

默认的 BuildType 用于配置项目的构建类型,属性及其作用如下所示:

    buildTypes {// 生产/测试环境配置
        release {// 生产环境
            // 往 AndroidManifest.xml 增加 meta-data 的值
            addManifestPlaceholders(mapOf("UMENG_CHANNEL_VALUE" to "xiaomi"))
            // 往资源文件增加资源值
            resValue("string", "app_token", "123457")
            // 往 BuildConfig 增加属性值
            buildConfigField("boolean", "LOG_DEBUG", "false")
            buildConfigField("String", "URL_PERFIX", "https://release.cn/")
            isMinifyEnabled = false //是否对代码进行混淆
            proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")//指定混淆的规则文件
            isPseudoLocalesEnabled = false //是否在APK中生成伪语言环境,帮助国际化的东西,一般使用的不多
            isShrinkResources = false // 指定是否为此生成类型启用缩减资源。
        }
        debug {// 测试环境
            buildConfigField("boolean", "LOG_DEBUG", "true")
            buildConfigField("String", "URL_PERFIX", "https://test.com/")
            isMinifyEnabled =  false//是否对代码进行混淆
            proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")//指定混淆的规则文件
            isJniDebuggable = false//是否可以调试NDK代码
            isPseudoLocalesEnabled = false//是否在APK中生成伪语言环境,帮助国际化的东西,一般使用的不多
        }
    }

而在 application 和 library 插件中使用的是 ApplicationBuildType 和 LibraryBuildType。它们分别扩展了 BuildType 的属性。

NamedDomainObjectContainer 的作用是什么

屏幕截图 2025-02-13 131051.png

如上图所示,buildTypes 代码块中 this 是 NamedDomainObjectContainer<ApplicationBuildType>,而其中对应的 release 代码块中才是 ApplicationBuildType。这是因为 CommonExtension 中的 buildTypes 是带接收者的函数类型,如下图所示:

image.png

其中 NamedDomainObjectContainer<T> 是一个命名领域对象容器,它专门用于存储和管理具有唯一名称的领域对象。这个容器在 Gradle 的构建配置中非常重要,因为它允许用户以一种有序且可管理的方式组织和操作一组相关的对象。简单来说你可以把它当成一个 Set,内部调用方法等同于往里面增加元素。示例如下:

    buildTypes {
        release {
           
        }
    }
    
    等同于 ⬇️
    
    buildTypes {
        create("release") {
           
        }
    }
    
    类似于 ⬇️
    
    val set = Set<ApplicationBuildType>
    set.add(
        ApplicationBuildType(
          "release"   
        )
    )

ApplicationBuildType 增加的扩展属性如下:

    buildTypes {// 生产/测试环境配置
        release {// 生产环境
            isDebuggable = false//是否支持断点调试
            applicationIdSuffix = ".test"//在applicationId 中添加了一个后缀,一般使用的不多
            versionNameSuffix = ".test"//在applicationId 中添加了一个后缀,一般使用的不多
            isCrunchPngs = false // 是否要压缩PNG。将此属性设置为true会减少尚未最佳压缩的PNG资源。但是,此过程会增加构建时间。默认在release时会启用
            isProfileable = false // 用于生成有助于精确性能分析的 APK,但不建议与 `isDebuggable = true` 同时使用
            isDefault = false // 设置在 Android Studio 中该产品风味是否默认被选中
            isEmbedMicroApp = false // 决定是否在应用中嵌入关联的 Android Wear 应用。
            signingConfig = signingConfigs.getByName("release") //设置签名信息
        }
    }

LibraryBuildType 增加的扩展属性如下:

    buildTypes {// 生产/测试环境配置
        release {// 生产环境
            isDefault = false // 设置在 Android Studio 中该产品风味是否默认被选中
            // aar 相关的属性配置
            aarMetadata { 
                
            }
        }
    }

resValue 和 buildConfigField

通过 resValuebuildConfigField 添加的配置值可以分别通过 resources.getXXXBuildConfig 获取。代码示例如下:

// 获取 resValue 添加的资源值
val app_token = resources.getString(R.string.app_token)
// 获取 buildConfigField 设置的属性值
val log_debug = BuildConfig.LOG_DEBUG

DefaultConfig

默认的 DefaultConfig 指定所有构建变体的默认配置,用于配置 ProductFlavor 的共享属性值,属性及其作用如下所示:

defaultConfig {
    // 指定应用所支持的最小 Android 系统版本,低于此版本设备无法安装应用
    // 示例:设置为 21 表示至少需要 Android 5.0(API 级别 21)系统
    minSdk = 21

    // 指定应用支持的最小 Android 预览版系统版本
    // 示例:设置为 UpsideDownCake 表示至少需要该预览版系统
    minSdkPreview = "UpsideDownCake"

    // 指定用于运行应用单元测试和 UI 测试的测试运行器
    // 示例:使用 androidx.test.runner.AndroidJUnitRunner 支持 JUnit 4 测试框架
    testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}    

而在 application 和 library 插件中使用的是 ApplicationDefaultConfig 和 LibraryDefaultConfig。它们分别扩展了 DefaultConfig 的属性。

ApplicationDefaultConfig 增加的扩展属性如下:

defaultConfig {
    // 项目的包名,用于唯一标识 Android 应用,在应用安装和运行时作为识别依据
    // 示例:该应用在设备和应用商店中的唯一标识符为 com.billy.myapplication
    applicationId = "com.billy.myapplication"

    // 版本号,是一个整数,用于系统内部标识应用版本,发布新版本时需递增
    // 示例:初始版本设置为 1,后续更新可依次递增
    versionCode = 1

    // 版本名称,面向用户显示的版本信息,通常采用 x.y.z 格式
    // 示例:当前版本显示为 1.0,重大更新可升级为 2.0 等
    versionName = "1.0"

    // 指定应用所针对的 Android 系统版本,决定应用在不同系统上的行为表现
    // 示例:设置为 33 表示针对 Android 13 系统优化和测试
    targetSdk = 33

    // 指定应用所支持的最大 Android 系统版本,高于此版本设备无法安装应用
    // 示例:设置为 30 表示最多支持到 Android 11 系统
    maxSdk = 30
}   

在 AGP 8.6 中 LibraryDefaultConfig 目前增加的属性都被废弃了,因此暂时不需要关注。

minSdk、maxSdk、compileSdk 和 targetSdk 的区别

属性名称作用示例及解释区别
minSdk指定应用所支持的最低 Android 系统版本。如果设备的系统版本低于该值,应用将无法安装。例如 minSdk = 21,意味着应用至少需要 Android 5.0(API 级别 21)系统才能运行。开发者可以使用 Android 5.0 及以上版本提供的新特性。是应用运行的最低门槛,主要影响应用的安装范围,不影响编译和运行时行为的适配规则。
maxSdk指定应用所支持的最大 Android 系统版本。如果设备的系统版本高于该值,应用将无法安装。不过这种情况比较少见,因为通常应用会尽量兼容较新的系统版本。例如 maxSdk = 30,表示该应用最多支持到 Android 11 系统。如果用户的设备运行的是 Android 12 或更高版本,将无法安装此应用。限制了应用可安装的最高系统版本,与编译过程无关,更多用于特殊场景下限制应用在特定系统范围运行。
compileSdk指定在编译应用代码时所使用的 Android SDK 版本。例如 compileSdk = 33,表示使用 Android SDK 版本 33 来编译应用代码。代码中如果使用了 Android 34 才有的 API,编译时就会出错。只有作用于编译过程,比如提示编译警告、编译错误。它不会被包含到 APK 中。
targetSdk指定应用所针对的 Android 系统版本。Android 系统会根据这个值来做版本兼容对于同一个API(方法),新版本的系统更改了它的内部实现逻辑,新逻辑可能会影响之前调用此API的App,为了兼容此问题,引入targetSdkVersion。当targetSdkVersion>=xx(某个系统版本)时再生效其新修改的逻辑,否则还是沿用之前的逻辑。影响应用在不同 Android 系统版本上的运行时行为,是系统判断如何应用新特性和规则的依据,与编译时使用的 API 范围无关。

ProductFlavor

默认的 ProductFlavor 用于配置项目的构建变体。可以在此块中配置不同版本的特性、设备要求、资源和应用 ID,它的属性及其作用如下所示:

android {
    // 定义一个产品风味维度,名为 "tier"(层级)。产品风味维度用于对不同类型的产品风味进行分组和组织。
    flavorDimensions 'tier'
    // 开始定义具体的产品风味
    productFlavors {
        // 定义一个名为 "paid"(付费版)的产品风味
        paid {
            // 将 "paid" 风味归到 "tier" 这个维度下
            dimension 'tier'
            // 注释说明:由于依赖项在其 "tier" 维度中已经包含了 "paid" 这种风味,
            // 所以不需要为 "paid" 风味提供后备风味列表。也就是说,当查找依赖时,能直接找到匹配的 "paid" 风味依赖。
        }
        // 定义一个名为 "free"(免费版)的产品风味
        free {
            // 将 "free" 风味归到 "tier" 这个维度下
            dimension 'tier'
            // 注释说明:指定一个按顺序排列的后备风味列表。当依赖项在 "tier" 这个匹配维度中没有包含 "free" 风味时,
            // 构建插件会尝试使用这个列表中的风味。你可以根据需要指定任意数量的后备风味,
            // 插件会在依赖项的 "tier" 维度中按顺序选择第一个可用的风味。
            matchingFallbacks = ['demo', 'trial']
        }
    }
}

在 ProductFlavor 新增了 dimensionmatchingFallbacks 属性外,其他属性都和 DefaultConfig 属性一致。

而在 application 和 library 插件中使用的是 ApplicationProductFlavor 和 LibraryProductFlavor。它的区别也和 ApplicationDefaultConfig 和 LibraryDefaultConfig 的区别一致。

AndroidResources

默认的 AndroidResources 用于配置 Android 资源处理相关的选项,它取代了之前的 aaptOptions 配置,它的属性及其作用如下所示:

    androidResources {
        // 设置要忽略的资源文件匹配模式
        ignoreAssetsPattern = "!.svn:!.git:!.ds_store"
        // 或者通过 ignoreAssetsPatterns 添加更多忽略模式
        ignoreAssetsPatterns.add("!*.log")

        // 配置不压缩的文件扩展名
        noCompress.add("mp3")
        noCompress.add("ogg")

        // 当找不到配置项时强制 aapt 返回错误
        failOnMissingConfigEntry = true

        // 添加传递给 aapt 的额外参数
        additionalParameters.add("--verbose")
        additionalParameters.add("--no-crunch")
    }

而在 application 和 library 插件中使用的是 ApplicationAndroidResources 和 LibraryAndroidResources。它们分别扩展了 AndroidResources 的属性。但是目前这两个子类都没有扩展的属性,因此不用关心它们的区别。

Installation

默认的 Installation 用于 adb 工具的本地安装选项,它取代了 adbOptions配置,它的属性及其作用如下所示:

    installation {
        // 设置 adb 操作的超时时间为 5000 毫秒
        timeOutInMs = 5000

        // 配置 APK 安装选项
        installOptions.apply {
            // 允许覆盖已安装的应用
            add("-r")
            // 授予所有运行时权限
            add("-g")
        }
    }

而在 application 和 library 插件中使用的是 ApplicationInstallation 和 LibraryInstallation。它们分别扩展了 Installation 的属性。但是目前这两个子类都没有扩展的属性,因此不用关心它们的区别。

参考