Compose Desktop发布版中Thread获取资源文件夹失败求助
问题
在Compose Desktop项目中,执行runDistributable或关闭ProGuard的runReleaseDistributable时,通过Thread.currentThread().contextClassLoader.getResource("test")可正常获取src/main/resources下test文件夹的路径;但开启ProGuard的发布版本中,即使添加了保留规则,仍输出“no found”,无法获取该资源路径。
相关代码与配置
Main.kt
@Composable @Preview fun App() { MaterialTheme { Button( onClick = { val resource = Thread.currentThread() .contextClassLoader.getResource("test") if (resource != null) { println("path: ${resource.path}") } else { println("no found") } } ) { Text("check") } } } fun main() = application { Window(onCloseRequest = ::exitApplication) { App() } }
build.gradle.kts
import org.jetbrains.compose.desktop.application.dsl.TargetFormat plugins { kotlin("jvm") id("org.jetbrains.compose") id("org.jetbrains.kotlin.plugin.compose") } group = "com.example" version = "1.0-SNAPSHOT" repositories { mavenCentral() maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") google() } dependencies { implementation(compose.desktop.currentOs) implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0") } compose.desktop { application { mainClass = "MainKt" buildTypes.release.proguard { isEnabled = true configurationFiles.from("compose-desktop.pro") version.set("7.4.0") } nativeDistributions { targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) packageName = "ResourceTest" packageVersion = "1.0.0" appResourcesRootDir.set(project.layout.projectDirectory.dir("src/main/resources")) } } }
原ProGuard规则
-keep public class java.lang.Thread { public static java.lang.Thread currentThread(); public java.lang.ClassLoader getContextClassLoader(); } -keep public class java.lang.ClassLoader { public java.net.URL getResource(java.lang.String); } -keep class **.resources.** { *; } -keepnames class **.resources.** { *; }
解决方案
1. 修正ProGuard规则,保留资源目录结构
ProGuard默认会优化资源打包,可能删除空目录或重命名资源路径,需添加资源保留规则到compose-desktop.pro:
# 保留resources目录下所有文件与目录结构 -keepresources dirs **/resources/** -keepresources files **/resources/** # 强制保留test目录及其中内容 -keepresources dirs test/** -keepresources files test/** # 确保类加载器核心方法不被混淆 -keep class java.lang.ClassLoader { public java.net.URL getResource(java.lang.String); public java.util.Enumeration getResources(java.lang.String); }
-keepresources指令用于阻止ProGuard删除或修改指定的资源文件与目录,是解决资源丢失的核心规则。
2. 更换资源获取方式
线程上下文类加载器在ProGuard处理后可能存在路径映射问题,可改用当前类的类加载器,或通过具体文件推导目录:
// 方式1:使用当前类的类加载器 val resource = MainKt::class.java.classLoader.getResource("test/") // 方式2:通过目录内具体文件推导路径(需确保test下有文件,比如test/.keep) val fileResource = MainKt::class.java.classLoader.getResource("test/.keep") if (fileResource != null) { val folderPath = fileResource.path.substringBeforeLast("/") println("folder path: $folderPath") }
3. 确保资源被完整打包到发布包
在build.gradle.kts的nativeDistributions中添加强制复制资源的配置,避免打包过程中遗漏:
nativeDistributions { // 原有配置... files { from("src/main/resources") { into("resources") } } }
之后可通过应用安装目录直接获取资源路径:
val appDir = System.getProperty("user.dir") val testFolder = File("$appDir/resources/test") if (testFolder.exists()) { println("path: ${testFolder.absolutePath}") }
4. 保证test目录非空
ProGuard默认会忽略空目录,若test目录为空,需添加一个占位文件(比如.keep),确保目录被打包进发布版本。
内容的提问来源于stack exchange,提问作者m'g'x
相关产品推荐
相关产品推荐

