You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 16:05:57