做Android开发的老哥,十有八九都遇到过这种场景:临时想验证一段Kotlin语法,跑一个算法思路,或者试试正则能不能匹配上,结果要么拿模拟器开整个App,要么新建一个Android项目,光等Gradle构建就得喝两三杯水。尤其是有时候只是想测试一个字符串处理函数、想看看某个API返回值格式,硬塞进正式工程里又嫌污染代码,放到线上模块里又不敢乱动。折腾到最后,验证一个函数比写这个函数还费劲。
这篇就来解决这个问题,专门讲怎么在Android Studio里单独编译运行一个Kotlin文件。不需要新建Android项目,不需要连接真机,不用等模拟器开机,也不用碰AndroidManifest和资源文件。跑通之后你会发现,日常写代码时验证逻辑的效率提升不是一星半点。全文不涉及编译原理的深水区,主要把Android构建链路和纯JVM运行环境的区别讲透,再给出一套能直接下手的操作流程。不管是Android开发者、Kotlin新手,还是被Gradle同步折磨到想摔键盘的朋友,都能照着做。
1. 先搞清楚:为什么Android项目不能直接右键Run Kotlin文件
很多新手第一次尝试时都会愣住:我在Android Studio里写了一个Kotlin文件,里面明明有fun main(),右键却找不到Run选项,或者点了Run之后报错,提示找不到主类。这不是你操作姿势不对,而是Android项目的构建逻辑和普通JVM项目压根就不一样。
1.1 你迟早会遇到这些“只想跑个函数”的瞬间
先聊场景。我梳理了一下平时最容易触发这个需求的情况,看看你中了几条:
- 刷算法题或做Kotlin面试题时,想本地跑几个测试用例,验证函数输出是否符合预期。热搜词里那堆“kotlin面试题”“kotlin string.format()”就是这个路子的产物。
- 做SharedPreferences封装时,想验证数据拼接、默认值解析、类型转换这些中间过程是否正确。直接在本地跑Kotlin文件,比在真机上看Logcat快太多,而且能精确控制输入输出。
- 调试正则表达式、日期格式化、字符串模板这类纯逻辑代码。每次改完正则都重新启动一个App去验证,效率低到无语。
- 测试第三方开源库的API用法。比如想知道某个库的某个方法到底怎么调、返回什么结构,在本地写个main函数跑一遍,比翻源码猜来猜去直观多了。
这种场景的共同特点是:代码不依赖Android上下文,不需要Context,不需要Activity,只需要一个能运行的入口和一段代码执行环境。说白了,它们就是普通的JVM程序。
1.2 Android项目的构建链路和“普通JVM程序”有什么不同
要理解为什么不能直接右键Run,得知道Android项目在Gradle里到底走了什么流程。
一个标准的Android App模块,apply的是com.android.application插件。这个插件会让Gradle走Android的编译打包链路:Kotlin代码先编译成class文件,然后和AndroidManifest.xml、res资源、Android SDK提供的类库打包成一个完整的APK。注意,这里面的目标产物是“能在Android设备上运行的应用”,而不是“能在电脑上跑起来的独立程序”。
右键一个Kotlin文件点Run,IDE做的事情是:寻找一个带main入口函数的类,然后按JVM的方式启动它。但在Android模块里,启动一个App的入口是AndroidManifest中指定的Activity,不是某个fun main()。就算你的Kotlin文件里写了fun main(),IDE也未必把它识别成“可运行目标”,因为整个模块的构建配置压根就没有提供JVM Application的运行方式。强行运行,要么Run按钮置灰,要么跑起来之后直接被Android Gradle Plugin的检查拦下来。
另外还有个坑:Android模块的编译依赖里包含android.jar这个桩包,里面的Activity、Context这些类只有方法签名,没有真实实现,运行时才由设备系统提供。所以你没法在电脑上直接运行一个依赖这些类的代码,一跑就崩。
1.3 解决方案的核心思路:给Kotlin一个“非Android”的JVM容器
既然Android模块不适合直接跑Kotlin文件,那就反过来:给这个Kotlin文件找一个基于纯JVM的环境。只要不依赖Android SDK的代码,在JVM环境下就能像普通Java程序一样编译和运行。
最简单可靠的方式,就是在现有Android工程里新建一个Java Library模块。这个模块本质上是纯JVM项目,不挂com.android.application插件,Gradle同步后,IDE会把它当成普通Java/Kotlin模块处理。模块里的Kotlin文件只要有main函数,右键就能出现Run选项,运行过程直接走本地JVM,几秒钟出结果。
思路就是这么简单。剩下的问题只是操作步骤的细节,以及一些常见的坑。下面我会把不同方案对比一遍,再给出完整实操流程。
2. 方案选型:四种常见跑法,优缺点一眼看懂
在动手之前,我建议你先想清楚自己到底需要哪种方式。不同场景适合不同方案,选错了容易绕弯路。
2.1 方案A:在现有工程里新建Java Library模块
这是我最推荐的方式,也是后文实操部分的主角。它的核心优势是:不离开Android Studio,不影响现有Android代码,而且模块可以长期保留,当作你的代码草稿箱。
具体好处有三个:
- 依赖管理无缝衔接。Android项目里Gradle还常崩溃,没关系,新模块和你正式模块在同一个工程里,反正都要同步,本地库用Maven本地仓库或阿里云镜像,速度不慢。
- 可以在模块里自由引入第三方库。比如你想验证Gson的某个序列化特性,直接在模块的build.gradle里加一行依赖,就能在Kotlin文件里import并使用,验证完再决定要不要挪到正式代码里。
- 不参与APK打包,放心折腾。Java Library模块默认不会进入App的发布产物,除非你手动把它加为依赖。所以里面写多少临时测试代码都不怕污染线上工程。
缺点也很明显:新模块会增加Gradle配置和同步时间;如果项目本身已经很大,多同步一个模块还是会拖慢一点整体构建速度。不过这点成本,比起直接起模拟器来简直可以忽略。
2.2 方案B:单独装一个IDEA Community
如果只是单纯学Kotlin、不想碰Android环境,那就没必要用Android Studio,装一个IntelliJ IDEA社区版就行。IDEA社区版免费,创建Kotlin项目后,配置一个JDK,就能直接运行Kotlin文件。它对纯Kotlin/JVM开发的支持比Android Studio更干净,因为Android Studio本身就是在IDEA基础上加了一堆Android插件的定制版,干纯JVM的活反而拖沓。
这个方案适合:刚开始学Kotlin语法、刷Kotlin题目、写纯逻辑小工具的人。但如果你已经是Android开发者,电脑上已经装了Android Studio,再装一个IDEA有点占硬盘,而且维护两套IDE环境也挺麻烦。
2.3 方案C:命令行kotlinc
如果你习惯了命令行操作,也可以安装Kotlin编译器,直接在终端里编译并运行Kotlin文件。流程是:先安装kotlinc,然后把.kt文件编译成jar包,再通过java -jar运行。这种方式适合脚本化、批量化操作,比如写个自动化脚本跑一堆Kotlin测试文件。
但说实话,日常开发里大多数人还是习惯可视化操作,命令行方式没有IDE的断点调试、变量监视这些功能,调试体验差很多。而且初始配置Kotlin编译器本身又是一道门槛,新手用起来容易劝退。
2.4 方案D:在线Playground
Kotlin官方有一个在线编译器,也就是Kotlin Playground,网页上写完代码直接点运行,不需要在本地装任何东西。这个方案最适合“突然想试一下某个语法点”的临时场景,比如Kotlin的let、also、run区别,随手在网页里敲几行验证一下就走。
缺点是无法引入本地依赖,不方便加载你工程里的类,也没法做文件IO、网络请求这类需要本机环境的操作。所以它只能当应急工具,不能替代本地方案。
2.5 选型总结表格
| 方案 | 操作难度 | 依赖引入 | 调试能力 | 适用场景 |
|---|---|---|---|---|
| 现有工程新建Java Library模块 | 低 | 支持 | 支持断点调试 | Android开发日常验证 |
| IntelliJ IDEA Community | 低 | 支持 | 支持断点调试 | 纯Kotlin学习与写小项目 |
| 命令行kotlinc | 中 | 手动管理 | 不支持 | 脚本化、批量编译运行 |
| 在线Playground | 极低 | 不支持 | 不支持 | 临时验证语法 |
如果你本身就在做Android开发,同时又不想切换工具,那方案A是唯一能满足“不离开Android Studio、又能快速跑Kotlin文件”的组合。下面进入实操。
3. 完整实操:在Android Studio里新建模块并运行Kotlin文件
这部分我会把每一步都写清楚,包括点哪个菜单、填什么内容、代码怎么写、右键选哪个。照着做基本不会踩坑。
3.1 新建Java Library模块的详细步骤
打开你的Android工程,点击顶部菜单File -> New -> New Module。在弹出的面板里,左侧选择Java Library。注意不是Android Library,这两者的区别很大:Android Library会创建一个依赖Android SDK的模块,而Java Library是纯Java项目的形态。
接着填写模块信息:
Library name:建议填devrunner或者kotlinrunner。我习惯用devrunner,含义是“开发期运行草稿”。Package name:填一个包名,比如com.example.devrunner。这一步不是强制,但建议填上,方便管理。Java class name:这里留空就行。如果你填了Main,IDE会默认生成一个Main.java文件,但我们是跑Kotlin的,这个Java文件后面还得删。
点击Finish后,Android Studio会开始Gradle同步。同步完成后,在Project窗口里就能看到新模块,默认目录结构是src/main/java。
如果模块自动生成了Main.java,先右键删掉,避免后面混淆。如果你在模块的build.gradle里看到的是plugins { id 'java-library' },说明模块创建成功。但这时模块还不支持Kotlin编译,需要手工加上Kotlin插件,这是很多人漏掉的一步。
打开新模块的build.gradle,初始内容大概是:
plugins { id 'java-library' } java { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 }改成下面这样:
plugins { id 'java-library' id 'org.jetbrains.kotlin.jvm' } java { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 }改完右上角会弹出一个Sync Now的提示,点击同步。如果项目里Kotlin插件的版本已经是统一管理,这一步不会报错;如果提示org.jetbrains.kotlin.jvm版本缺失,你需要检查工程根目录build.gradle里的Kotlin插件版本声明。多数情况下,Android项目根目录已经有Kotlin插件配置,新模块直接引用即可。
3.2 编写Kotlin入口:fun main的写法与要求
现在开始写Kotlin文件。在devrunner模块的src/main/java目录上右键,选择New -> Kotlin File/Class,文件名填TestMain,Kind选择File。
注意File和Class的区别:Class会创建一个空类,适合声明类型;我们要的是能直接运行的入口文件,用File会生成顶层函数的文件,更贴近脚本感。
文件里写一段最基础的代码:
fun main() { println("Hello from Kotlin Runner") val numbers = listOf(1, 2, 3, 4, 5) println(numbers.map { it * 2 }) }Kotlin的main函数可以带参数,也可以不带。在JVM环境下,fun main(args: Array<String>)和fun main()都是合法的入口。IDE识别入口的条件是:文件里存在顶层main函数,这是唯一的硬性要求。至于函数放在文件顶部、底部还是中间,都不能影响被识别。
如果你愿意,也可以提前定义一个比较复杂的功能来测试。比如写一个计算斐波那契数列的函数,然后在main里调用:
fun fibonacci(n: Int): Int { if (n <= 1) return n return fibonacci(n - 1) + fibonacci(n - 2) } fun main() { println(fibonacci(10)) }这一步相当于把你的验证代码放在main函数里,跑完直接看输出。
3.3 右键运行与运行配置手动调整
代码写完,右键点击文件,在弹出的菜单里找Run 'TestMainKt'。注意类名是TestMainKt,不是TestMain。Kotlin编译器会把顶层函数文件的字节码类名自动改成“文件名+Kt”,这是Kotlin/JVM的规则,IDE的右键菜单会直接显示这个类名,你只要认准文件名就行。
点击之后,IDE会执行Gradle构建并运行该类。如果一切正常,底部Run窗口会显示:
Hello from Kotlin Runner [2, 4, 6, 8, 10] Process finished with exit code 0每次运行都需要等Gradle增量构建,第一次大约十几秒,后面基本几秒内完成。
如果右键之后没有出现Run菜单,或者只有十几个灰色选项,不要慌,多半是IDE还没识别到这个模块是“可运行的JVM模块”。处理方式分三步排查:
- 确认新模块目录里,
src/main/java被标记为蓝色源根目录。在项目窗口里如果看到的是普通文件夹图标,右键该目录,选择Mark Directory as -> Sources Root。 - 打开Gradle工具窗口,点击刷新按钮触发同步,让IDE重新加载模块信息。
- 确认模块
build.gradle里已经加入了org.jetbrains.kotlin.jvm插件,这一步漏掉的话,IDE根本不会把Kotlin文件识别成可编译源码。
如果你想手动配置运行环境,可以打开Run -> Edit Configurations,点击左上角+号,选择Kotlin。在Main class栏填TestMainKt,Working directory填模块根目录,其他默认即可。这种手动配置一般用不到,除非右键菜单失效或你需要给运行环境加JVM参数。
3.4 在模块里引入第三方依赖
很多时候,单独跑Kotlin文件不只是为了跑标准库,还要测第三方库。比如你想试试Gson解析JSON的写法,那就在devrunner模块的build.gradle的dependencies块里加一行:
dependencies { implementation 'com.google.code.gson:gson:2.10.1' }同步完成后,在Kotlin文件里直接import使用:
import com.google.gson.Gson data class User(val name: String, val age: Int) fun main() { val json = """{"name":"Kotlin","age":7}""" val user = Gson().fromJson(json, User::class.java) println(user) }运行后控制台会打印解析出来的User对象。这个流程和普通Gradle项目引入依赖完全一样。注意Java Library模块里用implementation就够了,不会出现Android模块里那种implementation和api选择困难症。
依赖下载时如果卡在下载环节,多半是网络问题。后面第4部分会说怎么配镜像仓库,先往下看。
3.5 使用Gradle命令行run任务
右键运行适合日常点两下就跑的场景,但如果你想自动化、或者从命令行跑某个Kotlin文件,那就需要配置application插件来暴露一个任务。在devrunner模块的build.gradle里补充:
plugins { id 'java-library' id 'org.jetbrains.kotlin.jvm' id 'application' } application { mainClass = 'TestMainKt' }然后在Android Studio的Terminal窗口执行:
./gradlew :devrunner:runWindows环境下是:
gradlew.bat :devrunner:run它会编译并运行TestMainKt这个入口,输出和右键运行一致。这个方案的好处是,可以把这个Gradle命令写进脚本,一键跑模块里所有验证代码。
不过application插件会让模块更像一个独立应用,如果不想让模块的构建配置太复杂,也可以不加。日常验证用右键就够了,命令行属于加分项。
4. 写错就翻车:常见问题与排查技巧
实操过程中总会遇到各种报错,很多问题看起来吓人,其实原因很简单。我把最常见的几种情况整理成了一份速查列表,建议收藏备用。
4.1 右键没有Run菜单、运行按钮置灰
这是被问得最多的一个问题。如果你在Kotlin文件里写了fun main(),右键还是没有Run 'xxxKt',按下面顺序排查:
先看文件是不是在JVM模块里。如果你把这个Kotlin文件建在了Android App模块下,即使写了fun main(),Android Studio也会因为Android Gradle Plugin的限制,不提供直接运行入口。解决办法:把文件挪到devrunner模块里,或者重新按上一节的步骤创建模块。
再看源根目录标记。有时候Gradle同步失败导致IDE没有正确识别目录属性,src/main/java不是蓝色,IDE就不会扫描里面的源文件。右键目录,选Mark Directory as -> Sources Root。
最后看Gradle同步状态。如果新模块创建后一直没同步成功,构建面板会显示红色报错,这时候IDE处于“半瘫痪”状态,右键菜单自然不完整。去Gradle窗口点刷新,等构建成功再试。
4.2 Main method not found报错
运行配置已经建好了,但一点运行就报Main method not found in class TestMainKt,这个报错通常是两个原因。
第一个原因:你把main函数写成了类成员函数。比如:
class TestMain { fun main() { println("hello") } }这种写法不会生成JVM入口,只有顶层函数fun main()被编译成静态入口。如果你确实想用类成员函数,需要加上companion object并用@JvmStatic注解,或者用object声明,否则IDE识别不了。
第二个原因:类的全限定名和运行配置不匹配。如果你给Kotlin文件加了package声明,比如package com.example.devrunner,那运行配置里的Main class要填com.example.devrunner.TestMainKt。少了包名,JVM在类路径里找不到这个类,就会报这个错。
解决很简单:打开Run -> Edit Configurations,把Main class改成完整类名,或者干脆删掉运行配置,回到文件里重新右键Run,让IDE自动生成正确配置。
4.3 中文输出乱码
在Kotlin文件里写了一句println("你好"),运行后控制台输出乱码。这是JVM默认编码和文件编码不一致导致的。Windows系统上尤其常见,因为默认编码可能是GBK,而Android Studio的文件编码是UTF-8。
处理方法分两层:
第一层,给运行配置加JVM参数。在Run -> Edit Configurations里找到你的Kotlin运行配置,在VM options里填:
-Dfile.encoding=UTF-8第二层,如果每次新建配置都要手动加太麻烦,直接在gradle.properties里加一行:
org.gradle.jvmargs=-Dfile.encoding=UTF-8这行会全局影响Gradle守护进程的默认编码,改了之后重新同步,新建的配置也能继承。两个地方都配置好,基本就能根治乱码。
4.4 Gradle同步慢到怀疑人生
新模块创建后,Gradle同步一直卡在下载依赖或插件上,这在网络环境下很常见。办法是配置国内Maven镜像仓库。
在工程根目录的settings.gradle里,找到pluginManagement和dependencyResolutionManagement,把仓库地址改成阿里云镜像:
pluginManagement { repositories { maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } maven { url 'https://maven.aliyun.com/repository/google' } maven { url 'https://maven.aliyun.com/repository/public' } google() mavenCentral() gradlePluginPortal() } } dependencyResolutionManagement { repositories { maven { url 'https://maven.aliyun.com/repository/google' } maven { url 'https://maven.aliyun.com/repository/public' } google() mavenCentral() } }如果项目里已经配了镜像,那就不需要额外处理。改完后在Gradle窗口点刷新,下载速度会明显改善。
4.5 协程、挂起函数跑不起来
在fun main()里用delay()或者withContext(),编译能过,运行时报错,说delay只能在挂起函数或协程作用域中调用,甚至直接崩溃。这说明你还没把协程依赖加进来。
在纯JVM模块里,需要在build.gradle里引入协程核心库:
dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3' }同步后,在Kotlin文件里用runBlocking包住协程代码:
import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking fun main() { runBlocking { println("start") delay(1000) println("end") } }注意runBlocking会阻塞当前线程,等协程体执行完才返回。对本地验证代码来说,这种方式够用且稳定。千万别在main函数里直接调用GlobalScope.launch,因为协程还没跑完,main线程就已经退出,程序直接结束,你什么都看不到。
5. 更灵活的玩法:Scratch File与Kotlin脚本
如果觉得新建模块还是有点重,或者不想在工程里留下太多临时文件,还有两种更轻量的做法,实战中也很实用。
5.1 用Scratch File快速跑临时代码
Android Studio基于IDEA,自然也继承了Scratch File功能。这个功能是专门为“临时码一扔、马上跑”设计的。
操作方式:在项目任意位置右键,选择New -> Scratch File -> Kotlin。IDE会生成一个临时的.kts或.kt文件(视版本不同),你直接在里面写代码,右键运行即可。
Scratch File的好处是不会污染项目目录,不需要写进版本控制,相当于一张草稿纸。缺点是它默认不关联模块依赖,如果你想用Gson或协程这类第三方库,需要额外配置库路径,反而麻烦。所以我的习惯是:纯标准库语法验证用Scratch File,有依赖需求用devrunner模块。
5.2 用.kts脚本把常用逻辑做成命令行工具
Kotlin支持直接运行.kts脚本文件,不需要先编译。你可以在工程里建一个tools目录,放一些常用的小工具脚本,比如批量重命名文件、读取CSV、生成测试数据等等。
举个例子,创建一个file_utils.kts:
import java.io.File fun main() { val files = File("build/outputs").listFiles() files?.forEach { println(it.name) } } main()然后右键这个.kts文件,选择Run 'file_utils.kts'。如果IDE提示没有Kotlin脚本支持,检查一下模块里是否已经有org.jetbrains.kotlin.jvm插件,它一般会自动带上脚本运行能力。
这种方式适合“一次写、反复用”的工具型代码。注意脚本文件里的代码是会被顶层执行的,所以如果定义了函数,要记得在文件底部或者合适位置主动调用,不然脚本跑完等于啥也没干。
5.3 长期维护一个devrunner模块的建议
如果你决定长期采用方案A,我强烈建议把这个模块的定位规划好,否则用一段时间后,里面可能堆满几十个Test1.kt、Test2.kt之类的文件,找起来想哭。
我的维护策略是:
- 按主题分包:比如
algorithm包放算法题,json包放序列化测试,coroutine包放协程测试,regex包放正则实验。包名用英文小写加下划线,一眼能找到。 - 运行完及时清理:验证完某段逻辑后,如果确认不再需要,果断删除文件或包。别让草稿堆积成山。
- 可以用日期命名单一文件:如果只是当天快速验证,文件名叫
Test_20250214.kt这种格式,配合隔段时间清理一次,不会乱。 - 把模块的临时性写进团队约定:如果团队多人共用这套工程,记得在README里说明这个模块是本地开发用的,不要被误加到App依赖里。
结尾
我自己在Android Studio里保留一个叫devrunner的Java Library模块,已经成了标配。要测试一段API写法,就新建一个Kotlin文件,写个fun main()直接跑,跑完顺手删掉。平时写算法题、验证正则、模拟JSON数据,全在这个模块里搞定。这个模块不参与APK打包,无论里面怎么折腾,都不会影响正式工程。
最后再提醒一句:这套方法只适用于不依赖Android API的纯Kotlin/JVM代码。如果你想测试的是Context、SharedPreferences、View这些系统组件相关逻辑,那就老老实实去模拟器上跑,硬把这类代码塞进纯JVM模块里跑,结果只会是编译报错或者运行崩溃。把“能跑哪个”和“不能跑哪个”的边界弄清楚,这个技巧用起来才会顺手,不会被坑第二次。