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

Kotlin多平台共享代码能否读取commonMain资源目录下的JSON文件

Kotlin多平台读取commonMain资源文件通用解决方案

问题根源

你当前的实现存在两个核心问题:

  • 使用了JVM平台专属的ClassLoader API,无法兼容iOS等非JVM平台
  • Android端返回null大概率是因为资源未正确配置打包路径、或是资源读取方式不够健壮

实现步骤

1. 先配置shared模块的Gradle打包规则

在shared模块的build.gradle.kts中添加如下配置,确保commonMain下的资源会被打包到各平台产物中:

kotlin {
    // 你的现有平台配置,比如android、iosX64、iosArm64等
    sourceSets {
        val commonMain by getting {
            resources.srcDirs("src/commonMain/resources")
        }
        // Android端可选配置,确保资源不会被意外排除
        val androidMain by getting {
            resources.srcDirs("src/commonMain/resources")
        }
    }

    // iOS端配置,将common资源打包进Framework的bundle中
    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach { iosTarget ->
        iosTarget.binaries.framework {
            baseName = "shared"
            embedBitcode(org.jetbrains.kotlin.gradle.plugin.mpp.BitcodeEmbeddingMode.DISABLE)
            // 绑定common资源到iOS bundle
            val resourcesPath = file("src/commonMain/resources")
            processResources.configure {
                from(resourcesPath)
            }
        }
    }
}

如果用的是Groovy写法的gradle,自行对应语法调整即可。

2. 实现expect/actual跨平台读取逻辑

commonMain下的声明

expect object JsonStringResource {
    fun getResourceContent(fileName: String): String?
    fun getString(key: String): String?
}

你可以把json解析逻辑也放在common层,只需要各个平台实现读取文件内容的能力即可。

Android端actual实现

import java.io.InputStream

actual object JsonStringResource {
    actual fun getResourceContent(fileName: String): String? {
        return try {
            // 从classLoader读取资源流,比getResource更稳妥
            val inputStream: InputStream? = JsonStringResource::class.java.classLoader
                ?.getResourceAsStream(fileName)
            inputStream?.bufferedReader()?.use { it.readText() }
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
    }

    actual fun getString(key: String): String? {
        val jsonContent = getResourceContent("strings-test.json") ?: return null
        // 这里可以用kotlinx-serialization解析json取对应key的值,示例省略解析逻辑
        return null
    }
}

如果Android端还是读不到,可以尝试把路径改成"/$fileName",也就是加前导斜杠从资源根路径查找。

iOS端actual实现

import platform.Foundation.NSBundle
import platform.Foundation.NSString
import platform.Foundation.NSUTF8StringEncoding

actual object JsonStringResource {
    actual fun getResourceContent(fileName: String): String? {
        return try {
            // 拆分文件名和后缀
            val nameWithoutExtension = fileName.substringBeforeLast('.')
            val extension = fileName.substringAfterLast('.', "")
            // 从main bundle查找资源路径
            val path = NSBundle.mainBundle.pathForResource(
                name = nameWithoutExtension,
                ofType = extension
            ) ?: return null
            NSString.stringWithContentsOfFile(
                path = path,
                encoding = NSUTF8StringEncoding,
                error = null
            ) as String?
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
    }

    actual fun getString(key: String): String? {
        val jsonContent = getResourceContent("strings-test.json") ?: return null
        // 同Android端,json解析逻辑放在common层即可
        return null
    }
}

注意事项

  • 文件名大小写必须完全匹配,iOS文件系统大小写敏感,写错会直接找不到资源
  • 建议使用kotlinx-serialization来解析json内容,它也是KMP全平台兼容的序列化库
  • 如果需要支持桌面端等其他平台,参照iOS的思路,调用对应平台的资源读取API实现actual方法即可

内容的提问来源于stack exchange,提问作者Radim Janda

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 21:45:04