☰
Kotlin Android工程搭建全攻略:从Gradle KTS到构建优化
2026/9/30 16:05:16 网站建设 项目流程

1. 从零开始的 Kotlin Android 工程:为什么值得认真对待

先说明一下,这篇文章主要面向两类人:一类是刚从 Java 或者别的语言转过来、想用 Kotlin 写 Android 的小白;另一类是已经写过几个 Android 项目、但一直在 Java 的舒适区里没走出来、想系统地试试 Kotlin 的开发者。标题里的“建立 Android 工程”听起来像是最简单的一步,但恰恰是这个“第一步”最能体现 Kotlin 与老一套 Android 开发流程之间的磨合点。

我用 Kotlin 开发 Android app 的时间不算短,这几年从新建工程、配置 Gradle、写业务代码到上架都走了一遍。我个人的体会是:Kotlin 的语法只是表面,真正的门槛在于工程配置、依赖管理、以及和 Android 生态里那些老工具链的相处方式。如果你只是把 Java 代码“翻译”成 Kotlin 语法,而不去理解项目结构、构建脚本、SDK 版本这些底层逻辑,后面会踩很多莫名其妙的坑。

这篇文章从新建工程开始讲,但不止于“点几下下一步”。我会把每一步背后的原理、常见的坑、以及我现在实际在用的配置都拆开说清楚。文章内容基于我自己的开发环境(Android Studio、Gradle Kotlin DSL、新版 AGP),并会补充一些基于常见实践的选型理由。你跟着走一遍,应该能拿到一个干净、可扩展、好维护的 Kotlin Android 工程,而不是一个能编译能跑、但一加功能就出问题的“玩具项目”。

2. 环境准备与工具链选型:先弄明白这些再动手

2.1 Android Studio 与 SDK 的正确安装思路

Kotlin 开发 Android 的官方推荐 IDE 依然是 Android Studio,这一点没什么好争论的。Eclipse 时代的 Android 开发插件已经彻底退出历史舞台,IntelliJ IDEA 社区版虽然也能写 Kotlin、也能配 Android 工程,但缺少很多 Android 专属的调试工具和模板,除非你有特殊原因,否则不建议从 IDEA 起步。

安装 Android Studio 时,有两点容易被忽略:

  • SDK 目录规划:Windows 默认装到 C 盘,但 Android SDK、Gradle 缓存、模拟器镜像都很大,最好从一开始就把 SDK 路径改到非系统盘。我见过不少人因为 C 盘空间不足导致构建失败,最后只能重装。
  • 不要用便携版或绿色版:Android Studio 下载页面提供的是标准安装包,网上有一些所谓“便携版”“汉化版”,我建议一律别用。一是版本更新跟不上,二是插件和 SDK 组件的路径经常被改坏,出了问题很难排查。

版本上没有太多纠结的必要,现在 Google 官方已经不再提供 Android Studio 4.x 以下的版本下载了,你直接去官网下载最新稳定版即可。所谓稳定版指的是正式发布版,不要碰 Canary 和 Beta。Beta 版偶尔会有一些新特性,但用来建工程、跑业务代码,稳定压倒一切。

2.2 JDK 版本与 Kotlin 编译环境的匹配

Kotlin 编译和 Android Gradle Plugin(AGP)对 JDK 版本有明确要求。工程建好之后,Android Studio 会自动帮你配置 JDK,但如果你在命令行环境下跑 Gradle,或者你的机器上有多个 JDK 版本,这里就很容易出问题。

以当前的主流版本为例:

  • JDK 17 是 Android Studio 和 AGP 8.x 系列最稳妥的选择。
  • JDK 21 在较新的 AGP 版本上也能用,但部分第三方插件未必兼容。

我的做法是:在项目的gradle.properties里不强行指定 JDK 路径,而是在 Android Studio 的 Settings 里设置 Gradle JDK 为jbr-17或者本机安装的 JDK 17。这套组合我用了一年多,没出过编译层面的怪问题。

如果你在命令行里跑构建,注意JAVA_HOME环境变量必须指向 JDK 17。很多人遇到过“Unsupported class file major version”之类的报错,基本都是 JAVA_HOME 指向了系统自带的旧 JDK,而 Android Studio 内嵌的 JBR 版本反而更高。

2.3 Gradle 与 Kotlin DSL 的选择:从 Groovy 到 KTS 的迁移理由

Gradle 构建脚本早期只用 Groovy,文件后缀是.gradle。Kotlin 成为 Android 官方语言之后,Gradle 也开始支持 Kotlin DSL(.gradle.kts后缀),而且 AGP 官方文档逐渐把 KTS 作为示例格式。

我强烈建议你新建工程时直接选 Kotlin DSL,原因很简单:

  • KTS 有类型检查,写配置时会自动补全,拼错属性名会编译报错而不是运行期才炸。
  • Kotlin DSL 本身是 Kotlin 代码,和你的项目语言一致,心智负担小。
  • 官方新模板和文档都以 kts 为主,跟着学不会过时。

不过老实说,KTS 也有一个让人想骂人的地方:首次同步时编译脚本比较慢,而且报错信息有时候比 Groovy 更加隐晦。比如括号不匹配这种错误,Groovy 会提示在哪一行,KTS 可能只给你一个“编译脚本失败”的大概范围。这个只能慢慢习惯,遇到报错先往下翻,看具体 cause,而不是只看堆栈头部。

3. 新建 Kotlin Android 工程的完整实操

3.1 从 IDE 模板创建项目:每一步都在干什么

打开 Android Studio,选择 New Project。模板选择上,对初次学习 Kotlin 的人来说,推荐选Empty Views Activity,而不是 Compose 模板。

为什么?Compose 是新的 UI 框架,学习曲线本身就陡,如果同时学习 Kotlin 和 Compose,变量太多容易劝退。先选 Views(也就是传统的 XML 布局 + Activity),把 Kotlin 语法和 Android 组件生命周期先吃透,之后再学 Compose 会更容易,因为思维模型可以平移。

填写项目信息时,有几个字段值得注意:

  • Package name:这是应用的唯一标识,一旦上架就不能改。命名规则一般是com.你的域名.项目名。有人喜欢用com.example,我建议还是认真起一个,不然以后要改包名,涉及的地方不止一处,非常麻烦。
  • Minimum SDK:这个字段决定你的 app 最低支持到哪个 Android 版本。选太高会丢失用户,选太低会被一些新 API 限制。我一般默认选 API 24(Android 7.0),这个版本覆盖了当前绝大多数存量设备,而且很多现代库的最低要求也是 24。
  • Build configuration language:看到这个选项,选 Kotlin DSL,理由上面已经说了。

点 Finish 之后,Android Studio 会开始创建工程并自动执行 Gradle Sync。这一步如果卡很久,基本都是在下载依赖,和网络环境有关。你可以检查 Gradle 的下载源是否配置了阿里云或其他国内镜像,关于这块,后文会专门讲。

3.2 手动建一个 Kotlin Android 工程:不理解原理就复制不了

IDE 的模板会帮你处理掉 90% 的细节,但我强烈建议你至少手动建过一次工程。理解目录结构和 Gradle 脚本之间的关系,你才能真正诊断自己的构建问题。

一个最简 Kotlin Android 工程,文件结构如下:

MyApplication/ ├── app/ │ ├── src/ │ │ ├── main/ │ │ │ ├── java/com/example/myapplication/ │ │ │ │ └── MainActivity.kt │ │ │ ├── res/ │ │ │ │ ├── layout/activity_main.xml │ │ │ │ └── values/strings.xml │ │ │ └── AndroidManifest.xml │ ├── build.gradle.kts │ └── proguard-rules.pro ├── build.gradle.kts ├── settings.gradle.kts ├── gradle.properties └── gradle/wrapper/ ├── gradle-wrapper.jar └── gradle-wrapper.properties

关键文件的作用:

  • settings.gradle.kts:声明项目名称和包含哪些模块。模块多了之后,这个文件就是你的模块索引。
  • build.gradle.kts(根目录):配置全局插件版本,但不在这里配置应用自己的依赖。
  • app/build.gradle.kts:这是整个工程的核心,应用插件、SDK 版本、依赖、签名配置全在这里。
  • gradle.properties:JVM 参数、AndroidX 开关等全局属性。
  • gradle wrapper:用来固定 Gradle 版本,保证团队开发和 CI 构建环境一致。

如果你只是点了 IDE 的“下一步”,这些事情都是自动完成的。但你至少要知道每个文件是干嘛的,因为你迟早会遇到“依赖加不上”“版本冲突”“构建慢”这类问题,到时候你必须能定位到具体文件。

3.3 根构建文件与模块构建文件的配置示例

这里给一份我现在常用的app/build.gradle.kts配置,里面加了注释,方便你理解每一项的作用:

plugins { id("com.android.application") id("org.jetbrains.kotlin.android") } android { namespace = "com.example.myapplication" compileSdk = 34 defaultConfig { applicationId = "com.example.myapplication" minSdk = 24 targetSdk = 34 versionCode = 1 versionName = "1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { isMinifyEnabled = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) } } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } kotlinOptions { jvmTarget = "17" } } dependencies { implementation("androidx.core:core-ktx:1.12.0") implementation("androidx.appcompat:appcompat:1.6.1") implementation("com.google.android.material:material:1.11.0") implementation("androidx.constraintlayout:constraintlayout:2.1.4") testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") }

几个容易困惑的点:

  • namespace和applicationId不一样。namespace用于 R 类和 BuildConfig 的包名,一般和你的包名一致;applicationId是上架后的唯一 ID。混乱的时候可以把 namespace 理解成“代码层面的身份”,applicationId 是“商店层面的身份”。
  • compileSdk和targetSdk不一样。compileSdk 是编译用的 API 版本,targetSdk 是你声明的兼容目标。targetSdk 高会触发系统一些行为变更,比如权限策略更严格,但这也是应用跟上系统演进的唯一方式。
  • isMinifyEnabled是代码混淆和收缩的开关,release 包一般打开,debug 包关掉,不然断点调试会被混淆影响。

3.4 配置国内镜像源:不让网络成为第一道坎

如果你在国内使用默认的 Maven Central 和 Google Maven,Gradle 同步大概率会慢到让你怀疑人生。这不是项目配置的问题,是网络链路的问题。解决方式是配置镜像仓库。

在settings.gradle.kts的dependencyResolutionManagement块里,添加阿里云镜像:

dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { maven("https://maven.aliyun.com/repository/public") maven("https://maven.aliyun.com/repository/google") maven("https://maven.aliyun.com/repository/gradle-plugin") google() mavenCentral() } }

注意顺序:阿里云镜像尽量写在前面,这样会优先被请求。google()和mavenCentral()我通常还会保留,因为镜像偶尔会有同步延迟,尤其是一些新发布的库版本,镜像源里可能还没有。

镜像源只是治标,治本的办法是确保 Gradle Wrapper 分发的 Gradle 包也走国内源。在gradle-wrapper.properties里,默认地址是services.gradle.org/distributions,如果你每次下载 Gradle 都卡在 99%,可以改手动下载后放到本地。

4. Gradle 同步、依赖管理与常见构建问题排查

4.1 Gradle 同步到底在做什么

很多人点了一次 Sync Now,看它转了几分钟就以为只是“在下载东西”。实际上,Gradle 同步的过程包含了几件事:

  • 读取 settings 文件,确定有哪些模块。
  • 解析插件版本,并下载对应插件。
  • 解析项目的依赖树,把所有直接和间接依赖都下载到本地缓存。
  • 生成 IDE 需要的项目模型,供代码跳转和提示使用。

理解了同步是“下载依赖 + 建立模型”之后,你就能解释很多怪现象了。比如你改了依赖版本,Sync 报错;比如明明没动代码,Sync 却要跑几十秒。这些都是因为依赖树需要重新解析并验证一致性。

4.2 依赖版本冲突的排查思路

Kotlin Android 工程最常见的构建错误不是语法错误,而是依赖冲突。最常见的表现形式是:More than one file was found with OS independent path 'META-INF/xxx'。

排查这类问题的标准思路是使用./gradlew :app:dependencies --configuration debugRuntimeClasspath,把完整的依赖树打出来,看看哪些库拉入了重复的东西。

还有一种常见冲突是 Kotlin 版本不一致。你的项目用 Kotlin 1.9.x,某个库强制依赖 Kotlin 1.8.x,Gradle 会自动选用较高版本,看起来没问题,但实际上如果库是用旧版本 Kotlin 编译的,可能会出现 runtime 时的kotlin.KotlinNullPointerException或NoClassDefFoundError。

我的建议是:

  • 依赖库尽量选维护活跃的,版本发布不超过两年。
  • 如果 AAR 库是你自己维护的,统一 Kotlin 版本。
  • 遇到诡异报错,先检查是不是 Kotlin 标准库版本冲突。

4.3 Android 构建缓存与增量编译的优化

Kotlin 编译速度在大型项目里确实是个痛点。有一些配置可以明显改善体验。

在gradle.properties里,我会配置:

org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 org.gradle.parallel=true org.gradle.caching=true kotlin.incremental=true
  • org.gradle.parallel=true:多模块并行构建,对大型项目有效。
  • org.gradle.caching=true:构建缓存,Gradle 会缓存任务输出,下次构建跳过不变的部分。
  • kotlin.incremental=true:Kotlin 增量编译开关,改动单个文件不需要全量编译。

不过这些配置不是玄学,它们的效果和项目结构高度相关。如果你的项目只有一个 app 模块,并行构建的提升几乎为零;如果拆了 library 模块,收益会明显一些。

5. 从“能编译”到“好用”的工程化配置:这也算第一篇的干货

5.1 AndroidX 与 Material Components 的正确引入方式

如果你在 2018 年左右写过 Android 项目,应该还记得那个 androidx 迁移的阵痛。现在新模板默认启用 AndroidX,但你最好还是确认gradle.properties里有这两行:

android.useAndroidX=true android.enableJetifier=true
  • android.useAndroidX=true:启用 AndroidX 库,没有它,用了 androidx 依赖会告警。
  • android.enableJetifier=true:让旧的支持库自动转换成 AndroidX,主要是兼容一些老库。

如果你新建的工程里依赖全是新版库,Jetifier 的作用不大,可以关闭,能省一点构建时间。但如果项目里还有老 AAR,比如从同事那边拷来的老模块,先开着,不然会报Failed to resolve: androidx.core:core-ktx之类的错误。

Material Components 库引入后,你需要在主题里继承Theme.Material3.DayNight.NoActionBar这类 Material 主题,按钮、文本输入框这些组件才能用上 Material 的样式。原生Theme.AppCompat不会自动获得 Material 组件的主题支持。

5.2 ViewBinding:让 Kotlin 代码告别 findViewById

传统 Android 开发里,需要在 Activity 里写findViewById<TextView>(R.id.tv_name),或者在 ViewHolder 里反复 findViewById。这个写法不仅啰嗦,而且容易写错 ID,运行期才闪退。

Kotlin 时代的工程,推荐直接用 ViewBinding。在app/build.gradle.kts的android块里添加:

buildFeatures { viewBinding = true }

然后用起来是这样的:

class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) binding.tvMessage.text = "Hello Kotlin" } }

你会注意到,ActivityMainBinding是根据你的 XML 文件名activity_main.xml自动生成的类。如果你的布局里有控件tv_message,那就会生成一个tvMessage属性。这是编译期生成的代码,不是运行时反射,所以性能没有损失。

ViewBinding 相比旧式 findViewById 有几个好处:

  • 类型安全,不会出现强转错误。
  • 空安全,如果 XML 里删了某个 ID,访问也不算为空,编译期就报错。
  • 自动处理 include 布局的绑定。

5.3 把项目模块化:从单模块到多模块的演进时机

第一篇文章不讨论复杂的模块拆分,但你最好在初始阶段就养成“按功能分模块”的意识。不是说一上来就拆成 model / network / ui 三四个模块,而是当你发现app模块下的代码超过两三千行且还有继续增长的趋势时,就该考虑拆 Module 了。

模块化带来的优势很实际:

  • 编译隔离:只改 network 模块时,不需要重新编译整个 app,增量编译范围小。
  • 依赖控制:一些 internal 级别的接口,只对模块内可见,避免了类爆炸式的互相依赖。
  • 功能复用:如果将来要做平板适配或另一个应用,直接复用模块。

但拆模块不是没有代价。模块多了以后,Gradle 同步时间变长,依赖版本管理也需要抽出来单独处理。所以小项目还是老老实实单模块,等痛点出现了再拆。

5.4 版本目录:统一管理依赖版本的现代做法

如果你坚持手动建工程,你会遇到一个问题:多个模块里都写implementation("androidx.core:core-ktx:1.12.0"),版本号散落在各处,升级 SDK 版本时得人工搜索替换,特别容易漏。

Gradle 提供了一种叫 Version Catalog 的机制,在gradle/libs.versions.toml里统一管理依赖版本。我建议你从新工程开始就用起来,好处是:

  • 版本集中,升级依赖只改一个文件。
  • IDE 有自动补全。
  • 多模块之间的版本天然一致。

一个简单的libs.versions.toml示例:

[versions] agp = "8.2.2" kotlin = "1.9.22" coreKtx = "1.12.0" appcompat = "1.6.1" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }

然后在app/build.gradle.kts里这样引用:

plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) } dependencies { implementation(libs.androidx.core.ktx) implementation(libs.androidx.appcompat) }

看着比硬编码版本号繁琐,但你多维护两个模块之后,就会明白 Version Catalog 对“避免版本失控”的价值。

6. 常见问题与排查技巧实录

6.1 新建工程后 Gradle Sync 一直失败的常见原因

这是新手问得最多的问题。通常分为几类:

第一类:网络问题。表现是 Sync 卡在> Could not resolve org.jetbrains.kotlin:kotlin-stdlib:1.9.22等依赖下载位置。解决方式就是配镜像源,上文已经说过。

第二类:JDK 版本不对。表现是报Unsupported class file major version或Unsupported Java. Your build is currently configured to use Java XX.0.0 and Java 17.0.0 required.去 Settings 里把 Gradle JVM 换成 17。

第三类:AGP 版本和 Gradle 版本不匹配。AGP 8.2 需要 Gradle 8.2 以上;AGP 8.5 需要 Gradle 8.7。如果你手滑在gradle-wrapper.properties里写过错误的 Gradle 版本,Sync 会有很长一段红色的错误提示,会直接点名版本不匹配。按照提示换成对应版本就行。

6.2 Kotlin 编译时报“Unresolved reference”的排查思路

在 Kotlin 工程里,Unresolved reference 是最高频的编译错误。它不一定真的是你代码写错了,更多时候是依赖没有引入。

比如你用androidx.lifecycle.lifecycle-runtime-ktx时,写了lifecycleScope.launch { },但没在依赖里加lifecycle-runtime-ktx,IDE 就会报 Unresolved referencelifecycleScope。这时候你不应该怀疑 Kotlin 语法,而是去检查依赖。

还有一种情况比较隐蔽:你把一个类写在了错误包名下面,然后在别的文件 import 时用了旧包名,也报 Unresolved reference。我自己的习惯是,报这种错误先Build -> Clean Project,再 Sync 一次。如果还报错,再用Ctrl + Shift + O(mac 是Cmd + Shift + O)搜索类名,看它究竟存在于哪个包。

6.3 构建成功但安装失败:常见设备相关错误

跑模拟器或者真机调试时,常见报错有几种:

  • INSTALL_FAILED_UPDATE_INCOMPATIBLE:手机上已经装了签名不一致的同包名应用。卸载旧的再安装即可。
  • INSTALL_FAILED_INSUFFICIENT_STORAGE:手机存储空间不足。清理或卸载不用的应用。
  • Failure [INSTALL_PARSE_FAILED_NO_CERTIFICATES]:APK 签名有问题,常见于 Gradle 配置里签名信息错误或 debug 签名丢失。

这些大部分不需要动 Gradle 配置,先把设备环境弄干净。

6.4 build.gradle.kts 里的几个常见“语法坑”

KTS 虽然类型安全,但有几个地方容易踩坑:

坑一:在 KTS 里写字符串误用单引号。Groovy 同时支持单双引号,KTS 只支持双引号字符串。把'com.android.application'写成单引号,编译直接报错。

坑二:compileSdk不能直接写变量。某些版本下可以,但我在统一维护时还是喜欢直接写数字,避免解析顺序问题。

坑三:proguardFiles方法在 KTS 里的写法不同。Groovy 里可以直接写proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro',KTS 里需要getDefaultProguardFile(...)加上括号。上面给的模板我实测过,没问题。

坑四:在 app/build.gradle.kts 里用apply plugin: 'xxx'旧写法也没错,但和 plugins 块混用时,可能出现插件顺序问题。现在统一用plugins {}块,别混用。

6.5 真机调试时看不到设备怎么办

Windows 下最常见的两个原因:一是没有开启开发者模式和 USB 调试;二是缺少 USB 驱动。解决思路如下:

  • 在手机设置里连点版本号七次,开启开发者选项。
  • 在开发者选项里打开“USB 调试”。
  • 插上数据线后,手机弹窗询问“是否允许 USB 调试”,点允许。
  • 在终端运行adb devices,查看设备是否出现。
  • 如果显示unauthorized,说明手机端没允许;如果显示offline,换一根数据线或重启 adb server。

Mac 和 Linux 情况类似,不过 Linux 下有时候要配 udev 规则,Windows 则要装对应厂商的 USB 驱动。

7. 一个最小可用的 Kotlin 工程跑起来之后

到这里,你已经有了一个可以编译、可以安装的 Kotlin Android 工程。但工程建好只是开始,真正让你和 Java 时代说再见的是后面的 Kotlin 语法学习和 Android 组件实战。作为一个过来人,我建议接下来的学习路径是:

  • 先把 Activity 的生命周期和 Kotlin 的onCreate、onStart、onResume这些回调结合着过一遍,不要光看 DSL 语法。
  • 写一两个只操作 UI 的小例子,比如按钮点击后改变文本、根据输入框内容动态显示列表。
  • 引入 ViewModel + LiveData 或 StateFlow 这类架构组件之前,先把 Kotlin 的协程基础补上。直接调 Retrofit 的 suspend 函数,比用 callbacks 方便得多,但前提是你知道Dispatchers.Main和viewModelScope是怎么回事。

我个人在实际操作中的建议是:新建完工程后,先加一个简单的网络请求和一个简单的列表展示,把依赖注入、协程、生命周期这几块核心内容串一遍。这个“最小闭环”做完,你对 Kotlin Android 开发的信心和对工程结构的理解都会有质的飞跃。等到能独立跑通一个完整功能时,再回头深挖 Gradle 性能优化和模块化拆分,你会发现自己不再是照着模板写代码,而是真的在“搭建”一个应用。

最后再分享一个小技巧:如果你以后要给别人交付一个 Android 工程,或者在公司里参与协作,一定要把gradle-wrapper相关文件纳入版本控制,并且尽量让组内所有人用同一个 Gradle 版本。版本不一致导致的构建问题,是团队协作里最不值得花时间解决的一类问题。把这些底层的配置一次搞定,后面写业务代码的时候,你才能真正心无旁骛。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询