1. 项目概述
作为一名长期奋战在Android开发一线的老兵,最近在迁移项目到AGP8(Android Gradle Plugin 8)时遇到了TheRouter编译耗时暴涨的问题。经过两周的深度优化,成功将编译时间从原来的3分12秒降低到1分45秒。这篇文章将完整记录整个优化过程的技术细节和实战经验。
TheRouter作为阿里开源的Android路由框架,在大型项目中应用广泛。但随着AGP版本的升级,其Transform API的变更导致编译性能显著下降。这个问题在模块化项目中尤为明显,每次代码改动后的增量编译都需要等待漫长的构建过程,严重影响了开发效率。
2. 核心问题定位
2.1 AGP8编译体系变化
AGP8最大的变革在于对Transform API的废弃和替换。原先的Transform机制允许我们在.class文件转换为.dex文件前对字节码进行操作,但这种全局扫描的方式存在严重性能问题:
- 全量处理:即使只修改一个文件,也需要处理所有class
- 无法增量:缺乏精细化的变更检测机制
- 串行执行:多个Transform需要顺序处理
AGP8引入了新的AsmClassVisitorFactory API,它具备:
- 增量处理能力
- 并行化支持
- 精确的输入变更检测
2.2 TheRouter的编译瓶颈
通过--profile参数生成的构建报告显示,TheRouter的编译耗时主要集中在两个阶段:
- 路由表收集阶段:需要扫描所有模块的注解处理器输出
- 代码注入阶段:通过Transform修改Activity的onCreate方法
在AGP7环境下,这两个阶段耗时约40秒;升级到AGP8后,时间延长至2分10秒,主要因为:
- 兼容层转换开销:AGP8需要将旧Transform转换为新API
- 资源冲突检测:新增的严格模式检查
- 类加载隔离:每个Transform使用独立ClassLoader
3. 优化方案设计
3.1 架构级改造
首先需要对TheRouter的编译插件进行架构升级:
// 旧版Transform实现 class RouterTransform : Transform() { override fun transform(...) { // 全量处理逻辑 } } // 新版AsmClassVisitorFactory实现 abstract class RouterClassVisitor : AsmClassVisitorFactory<...>() { override fun createClassVisitor(...): ClassVisitor { return RouterCodeGenerator(original) } }关键改进点:
- 增量处理:实现
isIncremental()方法 - 输入过滤:通过
getParameters()配置扫描范围 - 并行化:设置
isParallelizable()为true
3.2 分级缓存策略
引入三级缓存机制加速路由表生成:
- 内存缓存:使用ConcurrentHashMap存储最近使用的路由表
- 本地缓存:将路由表序列化到
build/router_cache目录 - 模块缓存:每个模块独立缓存自己的路由信息
缓存键设计:
String cacheKey = moduleName + ":" + files.md5() + ":" + gradle.hashCode();3.3 并行化处理
通过Gradle Worker API实现多线程处理:
android { compileOptions { // 启用并行编译 allWarningsAsErrors false incremental true } }在插件中配置:
tasks.withType<JavaCompile>().configureEach { options.compilerArgs.addAll( listOf( "--parallel", "--threads=${Runtime.getRuntime().availableProcessors()}" ) ) }4. 具体实现步骤
4.1 环境准备
确保开发环境符合要求:
- Gradle 8.0+
- AGP 8.0.0+
- JDK 17
在gradle.properties中添加:
org.gradle.parallel=true org.gradle.caching=true kotlin.incremental=true4.2 插件改造
- 替换Transform入口:
class RouterPlugin : Plugin<Project> { override fun apply(project: Project) { project.extensions.create("router", RouterExtension::class.java) val android = project.extensions.getByType(AppExtension::class.java) android.registerAsmVisitor( RouterClassVisitor::class.java, RouterConfig::class.java ) } }- 实现增量处理器:
abstract class RouterClassVisitor : AsmClassVisitorFactory<...>() { override fun isIncremental(): Boolean = true override fun createClassVisitor(...): ClassVisitor { return if (isRouteClass()) { RouterInjector(original) } else { original } } private fun isRouteClass(): Boolean { // 通过类名和注解判断是否需要处理 } }4.3 性能调优
- 类过滤配置:
android { router { // 只处理这些包名下的类 includePackages = ["com.example.ui", "com.feature.*"] // 排除第三方库 excludeClasses = ["androidx/**", "com.google.**"] } }- 并行度控制:
// 在模块的build.gradle中 tasks.withType(JavaCompile).configureEach { options.forkOptions.with { memoryMaximumSize = "2g" jvmArgs = ["-XX:ActiveProcessorCount=${Runtime.getRuntime().availableProcessors() - 1}"] } }5. 实测效果对比
优化前后在相同工程下的对比数据:
| 指标 | AGP7+旧版 | AGP8+旧版 | AGP8+优化版 |
|---|---|---|---|
| 全量编译时间 | 2m45s | 3m12s | 1m45s |
| 增量编译时间 | 1m10s | 2m05s | 35s |
| CPU平均占用 | 45% | 60% | 75% |
| 内存峰值 | 3.2GB | 4.1GB | 3.8GB |
测试环境:
- MacBook Pro M1 Pro 32GB
- 项目规模:12个模块,286个Activity
6. 避坑指南
6.1 常见问题解决
- 类冲突问题:
> Duplicate class com.example.RouteTable found in modules解决方案:
android { packagingOptions { pickFirst("**/RouteTable.class") } }- 缓存失效问题: 当发现路由表没有更新时,执行:
./gradlew cleanRouterCache- 增量编译异常: 如果遇到增量状态下代码注入不生效,检查:
override fun getParameterConfiguration(): Iterable<...> { return listOf( // 确保所有影响代码生成的参数都被声明 inputChanges, routeConfig ) }6.2 调试技巧
- 生成编译分析报告:
./gradlew assembleDebug --scan- 查看路由处理详情:
./gradlew :app:compileDebugJavaWithJavac \ -Drouter.debug=true \ -Dorg.gradle.debug=true- 内存分析工具:
android { buildTypes { debug { testCoverageEnabled true jvmArgs "-XX:+HeapDumpOnOutOfMemoryError" } } }7. 进阶优化方向
7.1 分布式编译
对于超大型项目,可以配置Gradle远程缓存:
// settings.gradle buildCache { remote(HttpBuildCache) { url = "http://build-cache.example.com/cache/" credentials { username = "user" password = "pass" } } }7.2 预编译路由表
在CI环节预生成路由表:
./gradlew generateReleaseRouterTable然后在开发时直接使用:
router { usePrebuiltTable = true prebuiltTableDir = "${rootDir}/prebuilt/router" }7.3 模块化隔离
为每个动态模块独立配置路由处理:
androidComponents { onVariants(selector().withBuildType("debug")) { variant -> val moduleName = variant.name variant.routerConfig { enable = moduleName != "base" scanPackage = "com.$moduleName.**" } } }经过这次优化,我们不仅解决了AGP8下的编译性能问题,还建立起了一套可持续优化的编译加速体系。最关键的是要理解AGP新架构的设计理念,将全局处理转变为精准增量处理,同时充分利用Gradle的并行化能力。