当前位置: 首页 > article >正文

Android 问题00_IncompatibleComposeRuntimeVersionException

Android 问题00 —— IncompatibleComposeRuntimeVersionException

问题背景

在真实项目过程中开发 SDK,需要相应的开发一个 DEMO App,一方面测试 SDK 功能,另一方面根据实际场景构建一个可用的 App,模拟现实用途。这个过程中发现 App 开发用的还是老旧的 XML 布局格式,然后使用 Activity/Fragment 形式加载(Android 刚出生时的布局/页面加载方式)。我打算引入 Compose,一种较新的 UI 构建库,目的是想组内的开发同事来逐步认识更高效的布局/页面开发方式,同时期望项目内的组员可以多关注一些 Android 的新技术。为什么引入 Compose? 在文章最后说明。这里讲引入过程中遇到的问题。

先来看项目结构,是一个简单的两个 modules 组成的 Project。

项目结构

这个 Project 中 app module 依赖于 sdk module,sdk 包含主要的业务功能。现在主要针对这个 Project 作 Compose 的引入,原始的工程只应用 Java 语言,layout 的加载方式是最老的 xml 方式。Compose 已经推出了很久,Kotlin 也是 Android 首推的开发语言,用以构建 layout,确是高效,且更加安全。

下来就是引入 Compose 的问题了。

开始情况,考虑的是只在 app 模块中引入 Compose,sdk 有 Kotlin 业务代码,没有 Compose 构建的 UI,因此一开始时就有应用 Kotlin。

// sdk module
plugins {
    id 'com.android.library'
    // ...
    id 'org.jetbrains.kotlin.android' // Kotlin
}

/// ....

具体问题

各层级的 build.gradle 文件内 kotlin/compose 相关配置如下。

在 Project 项目级 build.gradle 中配置 KGP

// Root Project
buildscript {
    repositories {
		// ...
    }

    dependencies {
        classpath libs.androidx.navigation.navigation.safe.args.gradle.plugin

        classpath libs.google.services
        classpath libs.fat.aar
		// ...
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.0" // 建议与 IDE settings 中的版本一致
    }
    // ...
}

plugins {
    id 'com.android.application' version '7.4.2' apply false
    id 'com.android.library' version '7.4.2' apply false
	// ...
    id 'org.jetbrains.kotlin.android' version '1.9.0' apply false // 指定 android kotlin 版本
}

// ...

如上面的文件配置,在 buildscript{}dependencies{} block 中配置 KGP 版本 org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.0。 这个版本值建议与 “Android Studio > File > Settings > Other Settings > Kotlin Compiler” 设置标签页中的默认版本一致。

Kotlin Compiler


app 模块级 build.gradle 文件配置

plugins {
    id "com.android.application"
    id 'org.jetbrains.kotlin.android' // 应用 KGP 插件
}

android {
    // ...
    buildFeatures {
        compose true  // 开启 Compose
    }
    composeOptions {
        kotlinCompilerExtensionVersion = "1.5.1"  // 显示指定 Kotlin compiler 版本。
    }
    kotlinOptions {
        jvmTarget =  "11"
    }
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib:1.9.0"  // 显示设置 Kotlin 版本 1.9.0.
    implementation platform('androidx.compose:compose-bom:2024.01.00')  // 设置 BoM 库管理版本。设置了这个库之后,如下面的 compose ui 库就不需要指定版本号,均由 BoM 库管理版本。
    implementation 'androidx.compose.ui:ui'
    implementation 'androidx.compose.compiler:compiler:1.5.1'
    implementation 'androidx.activity:activity-compose:1.6.0'
    implementation 'androidx.compose.material3:material3'
	// ...
}

上面的设置中

  • plugins{} 中设置应用 kotlin 插件。
  • buildFeatures{} 中开启 compose。
  • composeOptions{} 中显示设置 kotlin 编译器版本,以兼容 kotlin 1.9.0 版本。
  • kotlinOptions{} 中设置目标 JDK 版本,用以生成字节码规范的版本。
  • dependencies{} 中设置了 compose 有关的库。这里重点提下 compose-bom 这个库。

sdk 模块级 build.gradle 文件配置

plugins {
    id 'com.android.library'
	// ...
    id 'org.jetbrains.kotlin.android'  // 应用 KGP 插件
}

上述 3 个 build.gradle 文件中的初始配置就这样完成了。


接着,在 IDE 中发起构建(build)后,等待构建结果。会在 console 中看到错误信息,下面是完整的 exception 信息。


> Task :sdk:compileDebugKotlin FAILED
e: androidx.compose.compiler.plugins.kotlin.IncompatibleComposeRuntimeVersionException: The Compose Compiler requires the Compose Runtime to be on the class path, but none could be found. The compose compiler plugin you are using (version 1.5.1) expects a minimum runtime version of 1.0.0.
        at androidx.compose.compiler.plugins.kotlin.VersionChecker.noRuntimeOnClasspathError(VersionChecker.kt:172)
        at androidx.compose.compiler.plugins.kotlin.VersionChecker.check(VersionChecker.kt:149)
        at androidx.compose.compiler.plugins.kotlin.ComposeIrGenerationExtension.generate(ComposeIrGenerationExtension.kt:68)
        at org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory.convertToIr$lambda$1(JvmIrCodegenFactory.kt:222)
        at org.jetbrains.kotlin.psi2ir.Psi2IrTranslator.generateModuleFragment(Psi2IrTranslator.kt:107)
        at org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory.convertToIr(JvmIrCodegenFactory.kt:255)
        at org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory.convertToIr(JvmIrCodegenFactory.kt:59)
        at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.convertToIr(KotlinToJVMBytecodeCompiler.kt:224)
        at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:101)
        at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli$default(KotlinToJVMBytecodeCompiler.kt:47)
        at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:168)
        at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:53)
        at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:100)
        at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:46)
        at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:101)
        at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:460)
        at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:62)
        at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.doCompile(IncrementalCompilerRunner.kt:476)
        at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl(IncrementalCompilerRunner.kt:399)
        at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileNonIncrementally(IncrementalCompilerRunner.kt:280)
        at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:124)
        at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:636)
        at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:101)
        at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1598)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:360)
        at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:200)
        at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:197)
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:712)
        at java.rmi/sun.rmi.transport.Transport.serviceCall(Transport.java:196)
        at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:587)
        at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:828)
        at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:705)
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
        at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:704)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
        at java.base/java.lang.Thread.run(Thread.java:833)

Errors were stored into <path_to_project>/.gradle/kotlin/errors/errors-1737440966298.log

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':sdk:compileDebugKotlin'.
> A failure occurred while executing org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction
   > Internal compiler error. See log for more details

按照错误提示,理解的应该是 Kotlin Compiler 找不到匹配的 runtime 库,且在 classpath 路径中没有找到需要的 runtime。 此时可以从两个角度考虑这个问题。

  1. 在 classpath 路径中确是了 kotlin 编译器需要的 runtime 环境。

    因为 Kotlin 运行在 JVM 平台上,会考虑到可能在 classpath 路径下缺少相应的 runtime 库。从 Android IDE 灵活性上来讲,这个 runtime 库不应该直接放到 JVM 的 classpath 路径中,也不会手动将 runtime 库文件放到 classpath 路径下。因此这个错误原因可能性不大。

  2. dependencies{} 配置中缺少了 runtime 的库依赖项。

    BoM 库管理的版本中有 compose runtime 库,在 app 模块级 build.gradle 文件的 dependencies{} 块中显示设置 compose runtime 库。

    dependencies {
         // ...
         implementation 'androidx.compose.runtime:runtime:1.5.1'  // 显示指定 runtime 库和版本值,版本值与 Kotlin Compiler 值一致。
    }
    

解决方案

根据错误信息提示,我只在 app/build.gradle 中添加了 runtime 库依赖。然后继续构建(build),遇到上面同样的提示信息。这个时候就与项目的结构有关系了,sdk/build.gradle 开始时配置了 kotlin android 插件的应用,没有指定任何 runtime 等库的依赖项。尝试在 sdk/build.gradle 中配置 compose runtime 等依赖项。

dependencies {
    // ...
    implementation "org.jetbrains.kotlin:kotlin-stdlib:1.9.0"  // 显示指定 Kotlin 版本
    implementation platform('androidx.compose:compose-bom:2024.01.00')  // BoM 版本
    implementation 'androidx.compose.runtime:runtime:1.5.1'  // 增加 runtime 配置
}

这样之后再构建(build),就可以通过了。


这里有一个更加优地使用 BoM 库的方法。从 BoM 2024.01.00 管理的库列表中看到 runtime 库的版本号是 1.6.0。

Compose Runtime

这个版本的 runtime 库所支持的 Kotlin Compiler 版本高于设置的 kotlinCompilerExtensionVersion Kotlin Compiler 版本 1.5.1,在不显示指定 runtime 库的版本号的情况下,构建(build)无法通过。因此最好的方式是配置支持 Kotlin Compiler 1.5.1 的 BoM 版本,这个 BoM 版本是 2023.09.01。 配置这个版本的 BoM 之后,在配置 runtime 时就不需要再指定版本号。

dependencies {
    // ...
    implementation "org.jetbrains.kotlin:kotlin-stdlib:1.9.0"  // 显示指定 Kotlin 版本
    implementation platform('androidx.compose:compose-bom:2023.09.01')  // BoM 版本
    implementation 'androidx.compose.runtime:runtime'
}

这样发起构建(build)也可以通过编译。

总的来讲,在模块级的 build.gradle 文件中 apply kotlin android 插件后,同时设置 runtime 库提供运行时环境。

另外,这里设置的 KT/Compose 不是 2.0 版本,因为 2.0 版本开始 Compose, Compiler 需要分开独立开始管理。


为什么 Compose

Jetpack Compose 是用于构建原生 Android 界面的新工具包。它使用更少的代码、强大的工具和直观的 Kotlin API,可以帮助您简化并加快 Android 界面开发,打造生动而精彩的应用。它可让您更快速、更轻松地构建 Android 界面。在使用后,你会明显体会到它的优势。

  • 精简代码
  • 直观易读
  • 加速开发
  • 功能强大

BoM(Bill of Materials)物料清单

借助 Compose 物料清单 (BOM),开发时只需要设置了 BoM 的版本,即可管理所有 Compose 库版本。BoM 本身包含指向不同 Compose 库的稳定版的链接,以便它们能够很好地协同工作。在应用中使用 BoM 时,开发者无需向 Compose 库依赖项本身添加任何版本。更新 BoM 版本时,开发者使用的所有库都会自动更新到新版本。

更详细信息,在这里查看。


http://www.kler.cn/a/515440.html

相关文章:

  • postgresql15的停止
  • 寒假刷题记录
  • 软件测试—— 接口测试(HTTP和HTTPS)
  • 2024年博客之星主题创作|2024年度感想与新技术Redis学习
  • 基于微信小程序的科创微应用平台设计与实现(LW+源码+讲解)
  • C++入门基础篇:域、C++的输入输出、缺省参数、函数重载、引用、inline、nullptr
  • Fastapi + vue3 自动化测试平台(4)-- fastapi分页查询封装
  • 前端jquery 实现文本框输入出现自动补全提示功能
  • yolov11 推理保存json
  • Windows 环境下 Docker Desktop + Kubernetes 部署项目指南
  • 免费SSL证书申请,springboot 部署证书
  • 【自动化测试】—— Appium使用保姆教程
  • SoftGNSS软件接收机源码阅读(一)程序简介、运行调试、执行流程
  • 数据结构——树和二叉树
  • Linux 下注册分析(1)
  • 用AI生成PPT,办公效率提升新方式
  • 基于 Vue3 + Canvas + Web Worker 实现高性能图像黑白转换工具的设计与实现
  • Linux通过docker部署京东矩阵容器服务
  • canvas基础
  • 【EXCEL_VBA_实战】多工作薄合并深入理解
  • Vue.js 配置路由:基本的路由匹配
  • grid 布局react组件可以循数据自定义渲染某个数据 ,或插入某些数据在某个索引下
  • docker部署flask项目后,请求时总是报拒绝连接错误
  • 某大厂一面:Java 构造器是否可以被重写
  • Node.js——express中间件(全局中间件、路由中间件、静态资源中间件)
  • 【中国电信-安全大脑产品介绍】