28、构建系统迁移:从Eclipse Ant迁移到Gradle、从Groovy DSL迁移到Kotlin DSL、从单模块迁移到多模块、迁移常见问题

构建系统迁移,说白了就是给项目换引擎。我经历过好几次这样的迁移,每次都觉得「这次应该很简单吧」,结果总有几个坑等着我。今天我把这些经验整理出来,希望能帮你少走弯路。

28.1 从Eclipse Ant迁移到Gradle

Ant时代,build.xml写得跟裹脚布似的。我记得第一次接手一个Ant项目,光看那几百行的XML就花了半天。Gradle的出现,简直是解放生产力。

28.1.1 迁移的核心思路

Ant到Gradle,不是简单的语法翻译。你得理解Gradle的哲学——约定优于配置。Ant里你事无巨细地告诉它每一步怎么做,Gradle则默认知道Android项目该怎么构建。

核心迁移步骤:

  1. 删除build.xml和project.properties
  2. 创建build.gradle文件
  3. 配置android和dependencies闭包
  4. 处理自定义Ant任务

28.1.2 典型迁移示例

Ant的构建脚本长这样:

<!-- Ant build.xml -->
<project name="MyApp" default="debug">
    <target name="clean">
        <delete dir="bin"/>
    </target>
    <target name="compile" depends="clean">
        <javac srcdir="src" destdir="bin/classes"/>
    </target>
</project>

迁移到Gradle后:

// build.gradle
android {
    compileSdk 34
    defaultConfig {
        applicationId "com.example.myapp"
        minSdk 21
        targetSdk 34
    }
}

tasks.register('clean', Delete) {
    delete rootProject.layout.buildDirectory
}

嗯,这里要注意:Ant里你手动管理依赖,Gradle里用dependencies闭包搞定。我曾经遇到一个项目,Ant里引了十几个jar包,迁移时发现Gradle仓库里都有,直接一行implementation搞定。

28.1.3 自定义Ant任务的迁移

Ant里经常有自定义任务,比如代码混淆、资源压缩。Gradle里这些都有现成的插件。如果真有特殊需求,可以用Gradle的Task API重写。

我的经验:Ant里90%的自定义任务,Gradle都有官方或社区插件替代。别自己造轮子,先搜搜Gradle Plugin Portal。

28.2 从Groovy DSL迁移到Kotlin DSL

Groovy DSL用了好几年,突然要切到Kotlin DSL,说实话一开始我是拒绝的。但用了一段时间后,真香——类型安全、IDE补全、编译时检查,这些Groovy给不了。

28.2.1 迁移前的准备

先确认你的项目能用Kotlin DSL。Gradle 5.0以上版本才支持,建议用6.8+。我建议先在一个小模块上试水,别一上来就全量迁移。

注意:Kotlin DSL的语法和Groovy DSL有差异,特别是闭包和lambda的写法。别直接复制粘贴,会报错。

28.2.2 语法对比

场景 Groovy DSL Kotlin DSL
依赖声明 implementation 'com.squareup.okhttp3:okhttp:4.9.0' implementation("com.squareup.okhttp3:okhttp:4.9.0")
插件声明 apply plugin: 'com.android.application' plugins { id("com.android.application") }
变量赋值 def version = "1.0" val version = "1.0"
条件判断 if (project.hasProperty('release')) { ... } if (project.hasProperty("release")) { ... }

28.2.3 迁移步骤

  1. 将build.gradle重命名为build.gradle.kts
  2. 修改settings.gradle为settings.gradle.kts
  3. 逐个文件迁移,从最底层的模块开始
  4. 运行./gradlew build验证

为什么会这样?因为Kotlin DSL是静态类型,Groovy是动态类型。你想想看,写Groovy时经常因为拼写错误导致运行时才报错,Kotlin DSL在编译时就帮你揪出来了。

避坑指南:我曾经在迁移时遇到一个坑——Kotlin DSL里不能直接用def关键字,得用val或var。还有,字符串拼接要用+号,Groovy里可以省略括号,Kotlin不行。

28.3 从单模块迁移到多模块

项目大了,单模块的build.gradle能写到上千行。每次编译都像在等火车。多模块化是必经之路。

28.3.1 为什么要多模块

  • 编译速度:只编译改动的模块,增量编译更快
  • 代码复用:公共库可以独立发布
  • 团队协作:不同团队负责不同模块,互不干扰

28.3.2 迁移策略

我建议采用「渐进式迁移」,别想着一天搞定。先抽离最独立的模块,比如网络库、工具类。

// 项目结构示例
MyApp/
├── app/                    # 主应用模块
├── core/                   # 核心库
│   ├── network/            # 网络模块
│   └── common/             # 公共工具
├── feature/                # 功能模块
│   ├── login/              # 登录模块
│   └── profile/            # 个人中心
└── build.gradle.kts        # 根构建文件

28.3.3 模块间通信

多模块最头疼的是依赖管理。我习惯用version catalog统一管理版本号,避免各个模块版本不一致。

// libs.versions.toml
[versions]
okhttp = "4.12.0"
retrofit = "2.9.0"

[libraries]
okhttp-core = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }
retrofit-core = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }

我的习惯:每个模块的build.gradle.kts里只声明它自己的依赖,公共配置放在根项目的build.gradle.kts里用allprojects或subprojects统一处理。

28.4 迁移常见问题

迁移过程中,我踩过不少坑。下面这些是高频问题,你大概率也会遇到。

28.4.1 依赖冲突

多模块后,不同模块可能依赖同一个库的不同版本。Gradle会默认选最高版本,但有时候会出问题。

// 强制指定版本
configurations.all {
    resolutionStrategy {
        force("com.squareup.okhttp3:okhttp:4.12.0")
    }
}

28.4.2 资源命名冲突

两个模块里都有R.string.app_name,编译时会报错。解决方案是给每个模块加资源前缀。

android {
    namespace "com.example.feature.login"
    resourcePrefix "login_"
}

28.4.3 编译顺序问题

模块A依赖模块B,但B还没编译完,A就开始编译了。Gradle会自动处理依赖顺序,但如果你用了自定义任务,可能会打乱顺序。

注意:别在自定义Task里直接依赖其他模块的Task,用dependsOn或者taskGraph来控制顺序。

28.4.4 Kotlin DSL编译慢

Kotlin DSL第一次编译确实比Groovy慢。我建议开启Gradle守护进程,并且把.gradle目录加到.gitignore里。

// gradle.properties
org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.caching=true

28.5 知识体系总览

下面这张图总结了本章的核心内容,你可以对照着梳理自己的迁移计划。

构建系统迁移知识体系 Ant → Gradle XML → 声明式 Groovy → Kotlin DSL 动态 → 静态类型 单模块 → 多模块 单体 → 组件化 常见问题 避坑指南 删除build.xml 创建build.gradle 处理自定义任务 重命名 .kts 语法转换 类型安全 渐进式拆分 Version Catalog 资源前缀 依赖冲突 资源命名冲突 编译顺序 迁移核心原则 渐进式迁移 · 小步快跑 · 充分测试 · 善用工具

迁移这件事,说白了就是「长痛不如短痛」。刚开始可能会觉得麻烦,但一旦完成,你会发现构建速度、代码可维护性都上了一个台阶。我个人建议,如果你还在用Ant或者Groovy DSL,别犹豫了,早点迁移吧。

公众号:蓝海资料掘金营,微信deep3321