【Android面试】Gradle专题

文章目录

  • 一、基础概念与语法
    • [1. Gradle 定义?](#1. Gradle 定义?)
    • [2. Groovy/Kotlin DSL 在 Gradle 脚本中的核心特性有哪些?](#2. Groovy/Kotlin DSL 在 Gradle 脚本中的核心特性有哪些?)
    • [3. Groovy 闭包(Closure)的定义、本质及在 Gradle 中的核心作用是什么?**](#3. Groovy 闭包(Closure)的定义、本质及在 Gradle 中的核心作用是什么?**)
    • [4. Groovy 闭包中 this、owner、delegate 的区别是什么?Gradle 中闭包委托策略(Resolve Strategy)有哪些?**](#4. Groovy 闭包中 this、owner、delegate 的区别是什么?Gradle 中闭包委托策略(Resolve Strategy)有哪些?**)
    • [5. Groovy 闭包的参数传递方式、隐式参数 it 的使用规则是什么?](#5. Groovy 闭包的参数传递方式、隐式参数 it 的使用规则是什么?)
    • [6. Groovy 中字符串(单引号 / 双引号 / 三引号)的区别及字符串插值的实现原理是什么?](#6. Groovy 中字符串(单引号 / 双引号 / 三引号)的区别及字符串插值的实现原理是什么?)
    • [7. Groovy 集合(List/Map)的简化写法与操作特性有哪些?](#7. Groovy 集合(List/Map)的简化写法与操作特性有哪些?)
    • [8. Project、Task、Action 的定义及三者关系是什么?](#8. Project、Task、Action 的定义及三者关系是什么?)
    • [9. Gradle 完整生命周期包含哪些阶段?各阶段核心职责是什么?**](#9. Gradle 完整生命周期包含哪些阶段?各阶段核心职责是什么?**)
    • [10. doFirst 与 doLast 的作用及执行逻辑区别是什么?](#10. doFirst 与 doLast 的作用及执行逻辑区别是什么?)
    • [11. Gradle 中闭包委托策略(Closure resolve strategy)有哪些?各自优先级是什么?](#11. Gradle 中闭包委托策略(Closure resolve strategy)有哪些?各自优先级是什么?)
    • [12. Gradle 脚本中,闭包的 delegate 是如何被设置的?](#12. Gradle 脚本中,闭包的 delegate 是如何被设置的?)
  • 二、配置文件与依赖管理
    • [(一)settings.gradle 核心考点](#(一)settings.gradle 核心考点)
      • [1. settings.gradle 的作用及执行生命周期阶段是什么?**](#1. settings.gradle 的作用及执行生命周期阶段是什么?**)
      • [2. include 与 includeBuild 的应用场景及核心区别是什么?](#2. include 与 includeBuild 的应用场景及核心区别是什么?)
      • [3. settings.gradle 中的 pluginManagement 作用是什么?](#3. settings.gradle 中的 pluginManagement 作用是什么?)
      • [4. Gradle 中依赖仓库(repositories)的加载顺序与优先级是什么?](#4. Gradle 中依赖仓库(repositories)的加载顺序与优先级是什么?)
    • [(二)build.gradle 核心考点](#(二)build.gradle 核心考点)
      • [1. 项目级与模块级 build.gradle 的职责划分是什么?](#1. 项目级与模块级 build.gradle 的职责划分是什么?)
      • [2. Android 中 SourceSet 的定义及常用类型有哪些?**](#2. Android 中 SourceSet 的定义及常用类型有哪些?**)
      • [3. implementation、api、compileOnly、runtimeOnly 的核心区别是什么?**](#3. implementation、api、compileOnly、runtimeOnly 的核心区别是什么?**)
      • [4. Gradle 依赖版本冲突的默认解析规则是什么?如何强制指定版本?(如何解决依赖冲突) **](#4. Gradle 依赖版本冲突的默认解析规则是什么?如何强制指定版本?(如何解决依赖冲突) **)
      • [5. Gradle 依赖传递性(transitive)的作用是什么?如何关闭?](#5. Gradle 依赖传递性(transitive)的作用是什么?如何关闭?)
      • [6. 什么是依赖锁定(dependency locking)?作用是什么?](#6. 什么是依赖锁定(dependency locking)?作用是什么?)
  • [三、Task 与 Extension 扩展](#三、Task 与 Extension 扩展)
    • [(一) Task 核心考点](#(一) Task 核心考点)
      • [1. 自定义 Task 的实现方式有哪些?](#1. 自定义 Task 的实现方式有哪些?)
      • [2. Task 依赖(dependsOn)的作用及执行顺序控制逻辑是什么?**](#2. Task 依赖(dependsOn)的作用及执行顺序控制逻辑是什么?**)
      • [3. 增量构建的定义,核心原理是什么?Gradle 如何实现增量构建?***](#3. 增量构建的定义,核心原理是什么?Gradle 如何实现增量构建?***)
      • [4. Task 中 onlyIf 的作用及应用场景是什么?](#4. Task 中 onlyIf 的作用及应用场景是什么?)
      • [5. Task 的 inputs、outputs 作用是什么?如何配置?](#5. Task 的 inputs、outputs 作用是什么?如何配置?)
      • [6. Gradle 中 Task 类型(DefaultTask、Copy、Delete 等)的作用是什么?**](#6. Gradle 中 Task 类型(DefaultTask、Copy、Delete 等)的作用是什么?**)
      • [7. 如何避免 Task 循环依赖?](#7. 如何避免 Task 循环依赖?)
      • [8. TaskContainer 是什么?**](#8. TaskContainer 是什么?**)
      • [9. Task指定执行顺序 **](#9. Task指定执行顺序 **)
    • [(二)Extension 扩展考点](#(二)Extension 扩展考点)
      • [1. Extension 的定义及核心作用是什么?**](#1. Extension 的定义及核心作用是什么?**)
      • [2. 为 Project 注册自定义 Extension 的核心方式是什么?**](#2. 为 Project 注册自定义 Extension 的核心方式是什么?**)
      • [3. Extension 与闭包的关系是什么?**](#3. Extension 与闭包的关系是什么?**)
      • [4. 如何在 Extension 中嵌套子 Extension(嵌套 DSL)?**](#4. 如何在 Extension 中嵌套子 Extension(嵌套 DSL)?**)
      • [5. Android 模块中 android {} 本质是什么?对应哪个 Extension 类型?**](#5. Android 模块中 android {} 本质是什么?对应哪个 Extension 类型?**)
      • [6. buildscript {} 的作用是什么?属于哪个 Extension?执行时机?](#6. buildscript {} 的作用是什么?属于哪个 Extension?执行时机?)
      • [7. dependencies {}、repositories {}、plugins {} 分别对应什么 Extension?](#7. dependencies {}、repositories {}、plugins {} 分别对应什么 Extension?)
      • [8. 如何在自定义插件中定义嵌套式 DSL(类似 android.defaultConfig)?](#8. 如何在自定义插件中定义嵌套式 DSL(类似 android.defaultConfig)?)
      • [9. 如何动态获取 / 修改已注册的 Extension 配置(如修改 applicationId、versionCode)?](#9. 如何动态获取 / 修改已注册的 Extension 配置(如修改 applicationId、versionCode)?)
      • [10. project.ext 与标准 Extension 的区别是什么?](#10. project.ext 与标准 Extension 的区别是什么?)
      • [11. ExtensionContainer 是什么? 如何配置不固定数量的 Extension? **](#11. ExtensionContainer 是什么? 如何配置不固定数量的 Extension? **)
  • [四、自定义插件与 AGP](#四、自定义插件与 AGP)
    • (一)自定义插件考点
      • [1. Gradle 插件的开发方式有哪几类?各方式的适用场景是什么?**](#1. Gradle 插件的开发方式有哪几类?各方式的适用场景是什么?**)
      • [2. 开发 Gradle 插件的核心步骤是什么?](#2. 开发 Gradle 插件的核心步骤是什么?)
      • [3. 插件中如何获取 Android 相关配置信息?](#3. 插件中如何获取 Android 相关配置信息?)
      • [4. buildSrc 是什么?特点与作用是什么?**](#4. buildSrc 是什么?特点与作用是什么?**)
      • [5. 插件如何跨项目共享?如何发布插件?](#5. 插件如何跨项目共享?如何发布插件?)
      • [6. 插件的 id、implementation-class 作用是什么?**](#6. 插件的 id、implementation-class 作用是什么?**)
    • [(二)AGP 核心考点](#(二)AGP 核心考点)
      • [1. AGP 与 Gradle 的关系是什么?AGP 的核心作用是什么?](#1. AGP 与 Gradle 的关系是什么?AGP 的核心作用是什么?)
      • [2. Android 构建变体(Variant)的生成逻辑是什么?**](#2. Android 构建变体(Variant)的生成逻辑是什么?**)
      • [3. Transform API 的作用及 AGP 版本演进后的替代方案是什么?](#3. Transform API 的作用及 AGP 版本演进后的替代方案是什么?)
      • [4. AGP 中 mergeResources、mergeAssets、processResources 等 Task 的作用是什么?**](#4. AGP 中 mergeResources、mergeAssets、processResources 等 Task 的作用是什么?**)
      • [5. AGP 如何处理 AndroidManifest 合并?**](#5. AGP 如何处理 AndroidManifest 合并?**)
      • [6. AGP 中 defaultConfig、buildTypes、productFlavors 本质是什么?**](#6. AGP 中 defaultConfig、buildTypes、productFlavors 本质是什么?**)
      • [7. AGP 如何通过 Extension 控制编译选项(compileOptions、kotlinOptions)?](#7. AGP 如何通过 Extension 控制编译选项(compileOptions、kotlinOptions)?)
  • 五、构建性能优化
    • [1. Gradle 构建慢的常见原因及核心优化手段有哪些?](#1. Gradle 构建慢的常见原因及核心优化手段有哪些?)
    • [2. Configuration Cache 的作用及解决的核心问题是什么?**](#2. Configuration Cache 的作用及解决的核心问题是什么?**)
    • [3. 排查 Gradle 构建的性能瓶颈的常用方法是什么?](#3. 排查 Gradle 构建的性能瓶颈的常用方法是什么?)
    • [4. 守护进程是什么?作用 **](#4. 守护进程是什么?作用 **)
  • 六、底层原理与进阶
    • [1. Gradle 核心模型 DAG 的定义及作用是什么? **](#1. Gradle 核心模型 DAG 的定义及作用是什么? **)
    • [2. Gradle 多线程安全的实现逻辑是什么?](#2. Gradle 多线程安全的实现逻辑是什么?)
    • [3. allprojects 与 subprojects 的作用及区别是什么?](#3. allprojects 与 subprojects 的作用及区别是什么?)

相关资料 【Android Gradle】Gradle系列

一、基础概念与语法

1. Gradle 定义?

Gradle 是一个基于 Groovy 或 Kotlin DSL 的声明式、可扩展的构建工具,它结合了 依赖管理 和 项目自动化构建的功能,支持多项目构建、增量构建、灵活的插件系统,广泛用于 Java、Android 等项目的构建流程管理。

2. Groovy/Kotlin DSL 在 Gradle 脚本中的核心特性有哪些?

Groovy 可兼容 Java 语法,支持省略分号、方法括号自动省略;原生闭包编程模型、动态类型推导;支持字符串插值、集合极简初始化;具备委托机制灵活调配作用域;Kotlin DSL 强类型、编译期校验、代码提示友好,两者均实现 Gradle 结构化极简配置编程,摆脱 XML 冗余写法。

3. Groovy 闭包(Closure)的定义、本质及在 Gradle 中的核心作用是什么?**

闭包是一段可传递、可延迟执行的代码逻辑块,能够捕获当前上下文变量;底层本质是 Groovy 中 groovy.lang.Closure 特殊对象;在 Gradle 中依靠闭包实现分层 DSL 配置,例如 android {}、dependencies {} 均依赖闭包完成结构化赋值,同时用于 Task 自定义回调、逻辑编排、灵活扩展脚本能力。

4. Groovy 闭包中 this、owner、delegate 的区别是什么?Gradle 中闭包委托策略(Resolve Strategy)有哪些?**

this:指代闭包自身所属实例对象;owner:定义当前闭包的外层持有者对象;delegate:闭包属性 / 方法查找的委托代理对象,Gradle DSL 核心依赖该对象分发逻辑。委托策略包含:DELEGATE_FIRST(优先委托查找,默认策略)、OWNER_FIRST(优先持有者)、DELEGATE_ONLY(仅委托)、OWNER_ONLY(仅持有者);Gradle 默认使用 DELEGATE_FIRST,简化多层配置域调用。

5. Groovy 闭包的参数传递方式、隐式参数 it 的使用规则是什么?

闭包无声明参数时,默认内置单隐式参数 it 直接使用;单个显式参数可自定义名称,无需类型声明;多个参数必须手动定义参数列表;支持参数默认值、弱类型自动推导;Gradle 遍历集合、配置回调大量依赖 it 简化代码编写。

6. Groovy 中字符串(单引号 / 双引号 / 三引号)的区别及字符串插值的实现原理是什么?

1 、单引号 '' 定义的字符串为不可扩展字符串

2 、双引号 "" 定义的字符串为可扩展字符串,可扩展字符串里面可以使用 ${} 引用变量值,当 {} 里面只有一个变量,非表达式时,{}也可以去掉

3 、三引号 ''' ''' 定义的字符串为输出带格式的不可扩展字符串

插值底层运行时识别占位符,拼接对象 toString 结果生成最终字符串。

7. Groovy 集合(List/Map)的简化写法与操作特性有哪些?

List 无需泛型定义、字面量快速创建,支持链式遍历、过滤、映射极简 API;Map 键可省略引号,直接 key:value 声明,快速取值、批量遍历修改;相比 Java 大幅简化集合初始化与常规运算,适配 Gradle 脚本快速配置需求。

8. Project、Task、Action 的定义及三者关系是什么?

Project 对应 Gradle 构建工程实例,根工程 + 所有子模块都是独立 Project,管理全局配置、依赖、Task 集合;Task 是构建最小执行单元,代表独立执行动作;Action 是 Task 内部真实执行的代码逻辑;关系:一个 Project 聚合多个 Task,一个 Task 由一组有序 Action 组成,最终运行 Task 本质串行执行内部所有 Action。

9. Gradle 完整生命周期包含哪些阶段?各阶段核心职责是什么?**

①初始化阶段:解析 settings.gradle 文件,识别整个项目所有参与构建的子模块,构建全局 Project 树形结构,配置仓库基础信息;②配置阶段:执行所有 Project 的 build.gradle 脚本,初始化所有 Task、配置依赖关系,生成 Task 有向无环图 DAG;③执行阶段:根据 DAG 拓扑排序,按需执行目标 Task 及其前置依赖 Task 的真实业务逻辑。

10. doFirst 与 doLast 的作用及执行逻辑区别是什么?

两者均用于动态为 Task 追加自定义 Action,运行在执行阶段,不属于配置阶段;doFirst 将自定义逻辑插入到 Task 原有 Action 队列头部,优先执行;doLast 将自定义逻辑追加到 Task Action 队列末尾,后置执行;常用于自定义前置校验、后置产物拷贝等通用场景。

11. Gradle 中闭包委托策略(Closure resolve strategy)有哪些?各自优先级是什么?

四种核心策略:DELEGATE_FIRST 优先从 delegate 查找属性和方法,找不到再找 owner(Gradle 默认);OWNER_FIRST 优先查找闭包持有者;DELEGATE_ONLY 仅允许委托对象解析;OWNER_ONLY 仅依靠持有者解析;优先级决定 DSL 作用域变量调用规则,保障 Gradle 多层配置正常联动。

12. Gradle 脚本中,闭包的 delegate 是如何被设置的?

Gradle 底层自动为标准 DSL 代码块绑定专属 delegate 对象,例如 android{} 绑定 AGP 扩展实例、project{} 绑定当前 Project 对象;闭包执行时自动切换委托指向,无需手动指定上下文,直接调用 delegate 内部属性与方法,实现简洁分层 DSL 编写体验。

二、配置文件与依赖管理

(一)settings.gradle 核心考点

1. settings.gradle 的作用及执行生命周期阶段是什么?**

仅在 Gradle 初始化阶段执行;核心职责:声明当前工程需要参与构建的子模块、配置全局插件仓库与依赖仓库地址、通过 pluginManagement 统一管控插件版本、定义项目根目录与构建输出目录,是多模块工程架构的基础入口。

2. include 与 includeBuild 的应用场景及核心区别是什么?

include 用于引入当前工程目录下的本地子模块,常规 Android 多模块基础构建方式,模块耦合依赖常规编译链路;includeBuild 属于复合构建能力,引入外部独立完整 Gradle 项目,无需发布仓库即可本地源码替换远程依赖,多用于第三方框架、基础组件本地调试解耦场景。

3. settings.gradle 中的 pluginManagement 作用是什么?

统一全局管理 Gradle 插件解析规则,自定义插件查找仓库优先级,锁定各类插件固定版本号,统一管控工程所有模块插件依赖,避免多模块插件版本混乱、解析路径不一致问题,规范团队插件使用标准。

4. Gradle 中依赖仓库(repositories)的加载顺序与优先级是什么?

仓库按照脚本从上到下的顺序依次遍历查找依赖构件;整体优先级:本地 Gradle 缓存 > 本地私服 / 私有仓库 > 阿里云镜像 / JCenter > Google 官方仓库 > Maven 中央仓库;一旦在某一仓库命中对应版本构件,立即终止后续查找流程。

(二)build.gradle 核心考点

1. 项目级与模块级 build.gradle 的职责划分是什么?

根项目 build.gradle:全局统一配置依赖仓库地址、定义 classpath 基础插件依赖、统一声明全局版本变量、所有子模块通用兜底配置;

模块级 build.gradle:应用 Android 插件、配置编译基础参数、声明当前模块依赖库、定制变体渠道、资源源码路径、混淆打包等独立业务配置。

2. Android 中 SourceSet 的定义及常用类型有哪些?**

sourceSets 是 Gradle 插件(如 Java 或 Android 插件)中用于配置源代码和资源目录结构的 DSL 块,用于告诉构建系统去哪里查找源码和资源。

Android 标准常用:main 主工程源代码资源集、test 单元测试源码集、androidTest 仪器化测试源码集;同时支持自定义 debug/release 差异化 SourceSet,实现不同环境源码隔离。

3. implementation、api、compileOnly、runtimeOnly 的核心区别是什么?**

implementation:依赖隔离生效,当前模块内部可用,不会传递给依赖当前模块的上层工程,编译隔离减少重编译;

api 等同于旧版 compile,依赖具备传递性,上层模块可直接使用当前模块依赖;

compileOnly 仅参与编译阶段校验,不会打包进入 APK,注解框架常用;

runtimeOnly 仅运行时依赖,编译阶段不可感知。

4. Gradle 依赖版本冲突的默认解析规则是什么?如何强制指定版本?(如何解决依赖冲突) **

默认采用最高版本优先仲裁策略,自动择优加载依赖树中最高版本库;

  • 手动解决依赖冲突的方式
  1. 手动强制统一版本可通过配置 resolutionStrategy.force 强制锁定某一构件固定版本,也可通过排除冲突子依赖、统一版本管理 BOM 规范约束全工程依赖一致性。
  2. 排除特定依赖项 exclude group: 'com.fasterxml.jackson.core', module: 'jackson-databind'

5. Gradle 依赖传递性(transitive)的作用是什么?如何关闭?

Gradle 默认开启依赖传递,引入一个库时自动下拉其所有底层间接依赖,简化开发;传递泛滥易引发版本冲突、包体积膨胀;关闭方式:单个依赖配置 transitive = false,或全局统一 exclude 指定 group/module 剔除冗余子依赖。

6. 什么是依赖锁定(dependency locking)?作用是什么?

依赖锁定会固化当前构建所有直接 / 间接依赖的精准版本清单,生成锁定文件纳入版本控制;杜绝动态版本号浮动、仓库更新导致的构建结果不一致,保障团队协作、CI/CD 流水线、不同机器构建产物完全可复现稳定。

三、Task 与 Extension 扩展

(一) Task 核心考点

1. 自定义 Task 的实现方式有哪些?

第一种:直接在当前 build.gradle 脚本临时定义简易 Task,仅单文件生效;

第二种:继承 Gradle 基础抽象类 DefaultTask,注解声明 Input/Output 规范化开发;

第三种:依托 buildSrc 目录编写公共 Task,全局所有模块共享;

第四种:独立 Gradle 插件工程封装 Task,发布私服多项目复用。

2. Task 依赖(dependsOn)的作用及执行顺序控制逻辑是什么?**

通过 dependsOn 指定当前 Task 前置依赖 Task,构建 Task 之间先后关联关系;Gradle 在配置阶段扫描所有依赖,生成无环 DAG 结构图;最终执行阶段严格按照拓扑排序串行 / 并行执行,保障前置逻辑完成后再运行当前 Task,精准把控整体构建时序。

3. 增量构建的定义,核心原理是什么?Gradle 如何实现增量构建?***

  • 增量构建是指在构建过程中,仅重新编译或执行那些受源码变更影响的部分任务,而跳过未发生变化的输入,从而提高构建效率。
  • 核心依托 Task 声明的 Inputs 输入数据 / 文件、Outputs 输出产物做哈希指纹校验;
  • 每次构建比对前后指纹信息,若输入输出无任何内容变更,标记 Task 为 UP-TO-DATE 直接跳过执行;
  • 仅变更链路 Task 重新运行,大幅减少冗余编译、文件处理耗时,是 Gradle 高性能基础核心。

4. Task 中 onlyIf 的作用及应用场景是什么?

onlyIf 接收布尔类型闭包条件判断;返回 true 正常执行当前 Task,返回 false 直接跳过标记为 SKIPPED;常用于多渠道差异化构建、环境变量校验、debug/release 按需裁剪 Task、特定系统条件拦截无效构建动作。

5. Task 的 inputs、outputs 作用是什么?如何配置?

inputs 定义 Task 依赖的源码、配置文件、属性参数;

outputs 定义 Task 生成的产物目录 / 文件;

两者明确 Task 数据边界,不仅支撑增量构建指纹比对,同时为 Gradle 构建缓存、并行安全校验、生命周期监控提供基础依据,通过 Task 内置 API 直接挂载文件与属性声明即可配置。

6. Gradle 中 Task 类型(DefaultTask、Copy、Delete 等)的作用是什么?**

  • DefaultTask:基础抽象父类,所有自定义 Task 标准继承源头,提供通用生命周期与 API;
  • Copy 内置文件拷贝 Task,支持过滤、重命名、目录递归复制;
  • Delete 标准清理 Task,删除指定构建缓存、产物目录;
  • Sync 同步目录差异更新,常用于资源同步、部署文件对齐,封装通用文件操作能力无需手写原生逻辑。

7. 如何避免 Task 循环依赖?

设计阶段保证 Task 依赖单向指向,禁止 A dependsOn B 同时 B dependsOn A;配置完成后校验 DAG 结构图排查环路;拆分重量级大 Task 为原子单一职责小 Task;统一规范依赖声明顺序,利用 Gradle 原生环路检测报错提前拦截不合理依赖定义。

8. TaskContainer 是什么?**

TaskContainer 是 Gradle 中用于管理项目所有任务的容器,它提供了创建、访问、遍历和配置任务的能力。通过它可以动态添加任务、设置依赖关系,是构建脚本中控制任务流程的重要工具。

9. Task指定执行顺序 **

  • Gradle 中通过 dependsOn、mustRunAfter 和 shouldRunAfter 来控制任务顺序。
  • dependsOn 表示强依赖,mustRunAfter 保证顺序但不强制依赖,shouldRunAfter
    是软性顺序建议。通过这些机制,可以灵活控制任务执行流程。

(二)Extension 扩展考点

1. Extension 的定义及核心作用是什么?**

  • Extension 是 Gradle 提供的扩展容器体系,允许开发者在原生 Project/Task 基础上自定义配置节点;脱离原生固定 API 限制,实现结构化、层级化、自定义 DSL 配置,解耦配置逻辑与核心构建逻辑,让脚本配置可读性与扩展性大幅提升。
  • 另一种通俗的答案
    Extension 是 Gradle 插件中用于接收用户配置的对象。它让用户可以通过 DSL 的方式为插件设置参数,从而控制插件的行为。例如 Android 插件中的 android { ... } 就是一个典型的 Extension。

2. 为 Project 注册自定义 Extension 的核心方式是什么?**

获取 Project 全局 Extensions 容器,调用 extensions.create(名称, 实体Class) 方法绑定自定义 Java/Groovy 实体类;将实体挂载到 Project 上下文,后续即可通过自定义名称 + 闭包完成 DSL 赋值绑定。

3. Extension 与闭包的关系是什么?**

Extension 承载结构化配置实体模型,存储所有配置字段数据;闭包依靠 delegate 委托机制将外层 DSL 代码块作用域指向 Extension 实例;直接在闭包内读写 Extension 属性,两者结合构成 Gradle 优雅分层配置语法体系。

4. 如何在 Extension 中嵌套子 Extension(嵌套 DSL)?**

  • 顶层主 Extension 内部定义二级子 Extension 对象成员;
  • 初始化时手动为子对象注册扩展容器绑定;
  • 依靠闭包委托穿透作用域,最终实现类似 android.defaultConfig 多层级嵌套 DSL 编写模式,适配复杂工程分级配置需求。

5. Android 模块中 android {} 本质是什么?对应哪个 Extension 类型?**

android {} 并非 Gradle 原生语法,是 AGP 插件预先注册的顶层扩展节点;App 应用模块对应 AppExtension ,Library 库模块对应 LibraryExtension;Android 所有编译、变体、资源、混淆、SDK 版本配置全部依托该扩展载体承载管理。

6. buildscript {} 的作用是什么?属于哪个 Extension?执行时机?

buildscript 是早期 Gradle 核心构建脚本扩展配置;

用于预先配置构建阶段所需仓库、classpath 插件依赖,为后续加载 Android 插件、自定义插件提供依赖支撑;

在整体配置阶段最优先执行,属于 Project 顶层原生扩展能力,新版推荐切换为 plugins 块替代。

7. dependencies {}、repositories {}、plugins {} 分别对应什么 Extension?

dependencies、repositories 是 Gradle 内核原生 Project 标准扩展,管控依赖引入与仓库源;plugins 是 Gradle 新版插件体系专属扩展节点,统一替代传统 buildscript 方式,简化插件版本引用与落地配置。

8. 如何在自定义插件中定义嵌套式 DSL(类似 android.defaultConfig)?

先定义顶层 Extension 实体,内部组合嵌套子 Extension 实体类;

插件 apply 阶段依次注册父、子两级扩展容器;

绑定闭包 delegate 分发策略,逐层穿透配置域;最终实现层级清晰、贴近原生 Android DSL 的自定义结构化配置语法。

9. 如何动态获取 / 修改已注册的 Extension 配置(如修改 applicationId、versionCode)?

在插件或脚本中通过 project.extensions.getByType() 获取 AGP 的 AppExtension 实例;拿到扩展对象后直接读写内部字段,动态覆盖 applicationId、versionCode、编译 SDK 版本、混淆开关等基础配置,实现运行时动态灵活改参构建。

10. project.ext 与标准 Extension 的区别是什么?

  • project.ext 是简易弱类型拓展容器,仅支持简单键值对临时存储数据,无结构化分层、无编译校验,适合零散全局变量临时共享;
  • 标准 Extension 强类型定义、支持嵌套 DSL、规范生命周期、适配增量构建与插件体系,企业级正式工程标准化开发首选方案。

11. ExtensionContainer 是什么? 如何配置不固定数量的 Extension? **

ExtensionContainer 是 Gradle 中用于注册和管理插件配置对象的容器。通过它,插件可以提供 DSL 风格的配置入口。当需要配置不固定数量的 Extension 时,可以使用 NamedDomainObjectContainer 动态添加多个可命名的配置对象,实现灵活的配置方式。

四、自定义插件与 AGP

(一)自定义插件考点

1. Gradle 插件的开发方式有哪几类?各方式的适用场景是什么?**

①脚本插件:直接写在 build.gradle 内部,仅当前脚本生效,快速临时简单逻辑使用;②buildSrc 目录插件(对象插件):工程私有源码目录,自动编译、全局模块可见,无需发布私服,团队通用基础逻辑沉淀首选;

③独立 Java/Groovy/Kotlin 插件工程:单独项目开发打包 Jar,发布到 Maven 私服,多项目跨工程统一复用、商业化通用组件使用。

2. 开发 Gradle 插件的核心步骤是什么?

第一步:创建基础工程结构,引入 Gradle/AGP 开发依赖;

第二步:自定义类实现 Plugin< Project> 顶层接口;

第三步:重写 apply 核心方法,完成插件初始化逻辑;

第四步:注册自定义 Extension 承载配置、注册自定义 Task 绑定业务动作;

第五步:监听 AGP Variant 生命周期回调,联动 Android 构建流程;

第六步:引用插件测试联动整体构建链路。

3. 插件中如何获取 Android 相关配置信息?

插件运行上下文拿到 Project 对象,通过 project.extensions.getByType(AppExtension::class.java) 精准获取 Android 全局扩展实例;从中读取 applicationId、版本号、BuildType、ProductFlavor、编译选项、资源路径、混淆配置等所有模块核心信息。

4. buildSrc 是什么?特点与作用是什么?**

buildSrc 是 Gradle 约定特殊源码目录,放置插件、通用 Task、公共工具类;

特点:工程构建时自动预编译处理,整个项目所有子模块直接依赖可见,无需手动配置依赖与发布仓库;隔离通用构建逻辑与业务代码,统一规范团队构建能力,维护简洁高效。

5. 插件如何跨项目共享?如何发布插件?

完成插件编码打包生成 Jar 构件;

配置 Maven Publish 插件,定义仓库地址、坐标、版本信息;上传至本地 / 公司私有 Maven 仓库;

其他工程远程依赖该插件坐标,配置 plugins 引用即可全局跨项目共享整套插件能力。

6. 插件的 id、implementation-class 作用是什么?**

插件 id 是 DSL 引用唯一标识,开发者在 plugins 块通过 id 快速引用插件;

implementation-class 配置插件全限定路径类名;

Gradle 根据 id 映射关系反射实例化对应插件实现类,执行 apply 内部核心初始化逻辑,解耦引用与底层实现。

(二)AGP 核心考点

1. AGP 与 Gradle 的关系是什么?AGP 的核心作用是什么?

Gradle 是通用跨平台构建内核引擎,定义生命周期、Project/Task 基础模型;

AGP 是谷歌基于 Gradle API 定制的 Android 专属插件;

封装 Android 全链路构建流水线:资源合并编译、AAPT 打包、Java/Kotlin 编译、混淆压缩、Dex 转化、签名打包、产物输出,原生补齐 Gradle 无 Android 专属能力短板。

2. Android 构建变体(Variant)的生成逻辑是什么?**

以 BuildType(debug/Release 基础构建类型)+ ProductFlavor(渠道定制维度)双向组合;叠加自定义 Flavor 维度、版本配置、资源差异化替换;

AGP 在配置阶段自动合成对应 Variant 对象,每个变体绑定独立 Task 链路、资源配置、签名策略,实现多环境、多渠道并行差异化打包构建。

3. Transform API 的作用及 AGP 版本演进后的替代方案是什么?

Transform API 是 AGP 旧版提供的字节码全局遍历入口,在 class 编译完成、Dex 生成之前拦截所有字节码文件;广泛用于埋点 AOP、热修复加固、字节码插桩无痕改造;

AGP 7.0 后彻底废弃 Transform,官方推荐基于 ASM 字节码操作框架 + Gradle Instrumentation 全新增量字节码编织 API,兼顾性能与兼容性。

4. AGP 中 mergeResources、mergeAssets、processResources 等 Task 的作用是什么?**

mergeResources:合并主工程、依赖库、变体差异化所有 XML / 图片资源,去重冲突校验;mergeAssets:统一合并 assets 原始静态资源目录文件;

processResources:资源合法性校验、资源 ID 索引生成、AAPT 编译预处理,为后续打包生成 R 文件、资源归档提供标准基础产物。

5. AGP 如何处理 AndroidManifest 合并?**

采用多层级合并策略:基础主清单 + BuildType 差异化清单 + Flavor 渠道清单 + 所有依赖库清单逐层融合;自动处理权限叠加、组件声明合并、占位符变量动态替换;针对冲突节点按照优先级覆盖策略处理,同时校验清单规范合法性,最终输出唯一完整有效 AndroidManifest 文件参与打包。

6. AGP 中 defaultConfig、buildTypes、productFlavors 本质是什么?**

三者全部是 AGP Extension 内部分层结构化配置实体;defaultConfig 全局基础通用默认配置(包名、版本、权限、最小 SDK);

buildTypes 管控 debug/Release 调试、混淆、压缩、签名模式;

productFlavors 多渠道差异化定制配置,共同支撑 Variant 变体体系构建底层数据来源。

7. AGP 如何通过 Extension 控制编译选项(compileOptions、kotlinOptions)?

android 扩展下内置 compileOptions 指定 Java 编译版本、源码兼容性、编码格式、增量编译开关;kotlinOptions 管控 Kotlin JVM 目标版本、增量编译、额外编译参数;统一依托 Extension 容器配置生效,贯穿整个 Java/Kotlin 编译 Task 全流程管控编译标准。

五、构建性能优化

1. Gradle 构建慢的常见原因及核心优化手段有哪些?

慢因:配置阶段重复解析脚本、依赖仓库检索慢、Task 无增量校验、多模块串行构建、Daemon 未启用、冗余插件 / Task 无效运行;

优化:常驻 Gradle Daemon 守护进程、开启并行多模块构建、按需配置缩减解析范围、启用本地 / 远程 Build Cache 缓存 Task 产物、高版本开启 Configuration Cache、精简无效依赖、淘汰废弃 Task、镜像加速依赖下载。

2. Configuration Cache 的作用及解决的核心问题是什么?**

常规构建每次都会重新解析脚本、初始化 Project、重建 Task DAG,配置阶段耗时极高;Configuration Cache 持久化缓存配置阶段所有模型数据与 DAG 结构;下次构建直接加载缓存快照,跳过重复解析初始化,大幅降低冷构建、增量构建的配置耗时,是高版本 Gradle 旗舰级性能优化方案。

3. 排查 Gradle 构建的性能瓶颈的常用方法是什么?

执行 ./gradlew build --scan 生成在线可视化 Build Scan 报告;

精准查看初始化 / 配置 / 执行三阶段耗时明细、每个 Task 执行时长、依赖检索耗时、GC 波动、仓库请求延迟、线程调度瓶颈;

同时结合 Profiler 分析脚本冗余逻辑、插件卡顿点,定向精准落地优化整改。

4. 守护进程是什么?作用 **

Gradle 守护进程(Daemon)是一个后台运行的 JVM 进程,用于加速构建过程。它通过复用已经初始化好的构建环境,避免每次构建都重新加载类和插件,从而显著提升构建效率。默认情况下是开启的,可以通过 gradle.properties 进行配置。

六、底层原理与进阶

1. Gradle 核心模型 DAG 的定义及作用是什么? **

DAG 即 Task 组成的有向无环图;配置阶段扫描所有 Task 依赖关系生成拓扑结构图;

核心作用:保证 Task 无循环依赖、严格按照前置后置顺序有序执行;同时是增量构建、缓存命中、并行调度执行的底层核心数据支撑模型。

2. Gradle 多线程安全的实现逻辑是什么?

配置阶段全程单线程串行执行,隔离全局状态避免并发冲突污染;

执行阶段依托 Gradle Worker API 做资源隔离、进程隔离;

要求自定义 Task 设计无共享可变全局状态,仅依赖私有 Input/Output 数据;

满足规范后不同 Task 可安全多线程并行运行,提升整体构建速度。

3. allprojects 与 subprojects 的作用及区别是什么?

  • allprojects:遍历作用域包含根 Project + 工程下全部子模块,可统一修改根工程与子工程全局配置;
  • subprojects:仅遍历所有层级子模块,不会操作根 Project 本身;日常规范优先用 subprojects 隔离业务模块,避免误修改根工程核心配置引发风险。
相关推荐
深蓝轨迹2 小时前
Redis 消息队列
java·数据库·redis·缓存·面试·秒杀
二本咕咕-机械转码2 小时前
STM32是怎么跑起来的?启动流程 + 时钟树一次讲透(面试高频)
stm32·嵌入式硬件·面试
东离与糖宝2 小时前
零基础Java学生面试通关手册:项目+算法+框架一次搞定
java·人工智能·面试
向上_503582913 小时前
配置Protobuf输出Java文件或kotlin文件
android·java·开发语言·kotlin
陆业聪3 小时前
AI 时代最被低估的工程师技能:把需求写清楚
android·人工智能·aigc
夏沫琅琊3 小时前
Android 的 Activity 启动模式
android
zh_xuan4 小时前
Android compose Navigation 页面导航
android·compose
程序员buddha4 小时前
Java面试八股文框架篇
java·开发语言·面试
编程一生4 小时前
面试问到的那些技术细节
面试·职场和发展