请求协助构建Gradle中compileOnly与runtimeOnly的对比示例
我来帮你拆解compileOnly和runtimeOnly的核心区别,以及如何写出能直观体现它们特性的测试代码——你当前的问题其实是用反了它们的使用方式,咱们一步步来调整:
先明确两个依赖配置的核心特性
compileOnly:只在编译阶段提供依赖类,编译完成后不会将该依赖打包进最终的APK/产物中。也就是说,编译器能看到这些类,但运行时JVM/Android Runtime找不到它们。runtimeOnly:只在运行阶段提供依赖类,编译阶段编译器完全看不到这些类,不会参与编译检查,但会被打包进最终产物,运行时可以正常加载。
调整你的测试示例,直观体现差异
保持你原有的app.gradle依赖配置不变:
dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$rootProject.kotlin_version" implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.core:core-ktx:1.2.0' compileOnly project(":compileonlylibrary") runtimeOnly project(":runtimeonlylibrary") }
1. 针对compileOnly的正确测试示例
在compileonlylibrary中定义一个接口或注解(这类只在编译时需要定义,运行时不需要依赖原类的内容):
// compileonlylibrary/src/main/kotlin/.../CompileOnlyInterface.kt interface CompileOnlyInterface { fun performAction() } // 或者定义一个编译时注解 @Target(AnnotationTarget.FUNCTION) annotation class LogMethod
然后在MainActivity中使用这些编译时可见的内容:
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 1. 使用compileOnly的接口:编译时能识别接口,可正常编写实现 val myImplementation = object : CompileOnlyInterface { override fun performAction() { Log.d("CompileOnlyTest", "Action performed from local implementation") } } myImplementation.performAction() // 运行时正常执行,因为我们的代码里包含了实现逻辑 // 2. 使用compileOnly的注解:编译时生效,运行时不需要注解类存在 logTest() } @LogMethod // 编译时编译器能识别这个注解,不会报错 private fun logTest() { Log.d("CompileOnlyTest", "Annotated method called") } }
这里的关键是:compileOnly的依赖只用于编译阶段的语法检查/接口定义,运行时我们不需要依赖原库的类——因为我们自己的代码已经实现了接口,或者注解只是编译时用的标记。
2. 针对runtimeOnly的正确测试示例
在runtimeonlylibrary中定义一个普通类:
// runtimeonlylibrary/src/main/kotlin/.../BarRuntime.kt class BarRuntime { fun sayHello() = "Hello from runtime-only library!" }
然后在MainActivity中通过反射调用这个类(因为编译时编译器看不到BarRuntime,不能直接实例化):
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // ... 前面compileOnly的代码 ... // 使用runtimeOnly的类:通过反射加载,编译时不需要直接引用 try { // 注意替换成你实际的类全限定名 val barClass = Class.forName("com.example.runtimeonlylibrary.BarRuntime") val barInstance = barClass.newInstance() val sayHelloMethod = barClass.getMethod("sayHello") val result = sayHelloMethod.invoke(barInstance) as String Log.d("RuntimeOnlyTest", result) // 运行时正常输出,因为runtimeOnly依赖已打包进APK } catch (e: Exception) { Log.e("RuntimeOnlyTest", "Failed to call BarRuntime", e) } }
这里的关键是:runtimeOnly的类编译时不可见,所以不能直接写BarRuntime(),但运行时因为依赖被打包了,反射可以正常加载并调用。
总结你的原有问题
- 你直接实例化
FooCompile():因为compileOnly依赖不会被打包,运行时找不到这个类,所以崩溃。 - 你直接写
BarRuntime():因为runtimeOnly依赖编译时不可见,编译器找不到这个类,所以编译报错。
通过上面的调整,就能清晰看到两个配置的差异啦!
内容的提问来源于stack exchange,提问作者Ricardo
相关产品推荐
相关产品推荐

