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

Android中无法将MutableList存入DataStore的问题求助

解决Android DataStore存储自定义对象列表的异常问题

问题描述

尝试将MutableList<TvChannel>存入Android Preferences DataStore时,抛出如下异常:

java.lang.IllegalArgumentException: Type not supported: interface java.util.List
    at com.alclabs.waltv.MainActivity.saveOnDataStore(MainActivity.kt:133)
    at com.alclabs.waltv.MainActivity.access$saveOnDataStore(MainActivity.kt:20)
    at com.alclabs.waltv.MainActivity$saveChannelsLocalStorage$1.invokeSuspend(MainActivity.kt:90)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
    at android.os.Handler.handleCallback(Handler.java:958)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loopOnce(Looper.java:230)
    at android.os.Looper.loop(Looper.java:319)
    at android.app.ActivityThread.main(ActivityThread.java:8893)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:608)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103)
    Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@7c5ee8a, Dispatchers.Main.immediate]

相关核心代码:

  • TvChannel数据类:
@Parcelize
data class TvChannel(
    val channel_url: String,
    val channel_logo: String,
) : Parcelable
  • 报错的存储方法:
private suspend fun saveOnDataStore(key: String, value: MutableList<TvChannel>) {
    val immutableList: List<TvChannel> = value.toList()

    val dataStoreKey = preferencesKey<List<TvChannel>>(key)
    dataStore.edit { settings ->
        settings[dataStoreKey] = immutableList
    }
}

当前使用的DataStore版本:1.0.0-alpha01

原因分析

Android Preferences DataStore(即datastore-preferences依赖)仅支持有限的基础数据类型,比如String、Int、Boolean、Float等,不支持自定义对象(如TvChannel)以及自定义对象的集合类型,因此直接存储List<TvChannel>会触发类型不支持的异常。

解决方案

方案一:使用Proto DataStore(推荐)

Proto DataStore支持自定义复杂数据类型,通过Protocol Buffers定义数据结构,类型安全且性能更优。

  1. 替换依赖
    移除原有的datastore-preferences依赖,添加Proto DataStore相关依赖:
implementation("androidx.datastore:datastore:1.0.0")
implementation("com.google.protobuf:protobuf-javalite:3.21.12")
  1. 定义Proto文件
    在app/src/main/proto/目录下创建tv_channels.proto文件:
syntax = "proto3";

option java_package = "com.alclabs.waltv"; // 替换为你的项目包名
option java_multiple_files = true;

message TvChannels {
  repeated TvChannel channel = 1; // 定义TvChannel的集合
}

message TvChannel {
  string channel_url = 1;
  string channel_logo = 2;
}
  1. 创建DataStore管理类
class TvChannelsDataStore(private val context: Context) {
    private val dataStore = context.createDataStore(
        fileName = "tv_channels.pb",
        serializer = TvChannelsSerializer
    )

    // 实现Serializer接口处理序列化/反序列化
    object TvChannelsSerializer : Serializer<TvChannels> {
        override val defaultValue: TvChannels = TvChannels.getDefaultInstance()

        override suspend fun readFrom(input: InputStream): TvChannels {
            try {
                return TvChannels.parseFrom(input)
            } catch (exception: InvalidProtocolBufferException) {
                throw CorruptionException("无法读取Proto数据", exception)
            }
        }

        override suspend fun writeTo(t: TvChannels, output: OutputStream) {
            t.writeTo(output)
        }
    }

    // 保存频道列表
    suspend fun saveChannels(channels: List<TvChannel>) {
        dataStore.updateData { current ->
            current.toBuilder()
                .clearChannel() // 清空原有数据
                .addAllChannel(channels.map {
                    // 将自定义TvChannel转为Proto定义的TvChannel
                    com.alclabs.waltv.TvChannel.newBuilder()
                        .setChannelUrl(it.channel_url)
                        .setChannelLogo(it.channel_logo)
                        .build()
                })
                .build()
        }
    }

    // 以Flow形式暴露频道列表
    val channelsFlow: Flow<List<TvChannel>> = dataStore.data
        .map { protoChannels ->
            protoChannels.channelList.map { protoChannel ->
                // 将Proto TvChannel转为自定义TvChannel
                TvChannel(protoChannel.channelUrl, protoChannel.channelLogo)
            }
        }
}
  1. 在MainActivity中使用
private lateinit var tvChannelsDataStore: TvChannelsDataStore

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ActivityMainBinding.inflate(layoutInflater)
    setContentView(binding.root)

    tvChannelsDataStore = TvChannelsDataStore(this)

    // ...其他初始化逻辑
}

private fun saveChannelsLocalStorage(listOfChannels: MutableList<TvChannel>) {
    lifecycleScope.launch {
        tvChannelsDataStore.saveChannels(listOfChannels)
    }
}

方案二:将列表序列化为JSON字符串存储

通过Gson/Moshi等序列化库,把List<TvChannel>转为JSON字符串,再存入Preferences DataStore,读取时反序列化即可。

  1. 添加Gson依赖
implementation("com.google.code.gson:gson:2.10.1")
  1. 修改存储与读取方法
// 保存方法
private suspend fun saveOnDataStore(key: String, value: List<TvChannel>) {
    val gson = Gson()
    val channelsJson = gson.toJson(value)
    val dataStoreKey = preferencesKey<String>(key)
    dataStore.edit { settings ->
        settings[dataStoreKey] = channelsJson
    }
}

// 读取方法示例
private suspend fun loadChannelsFromDataStore(key: String): List<TvChannel> {
    val gson = Gson()
    val dataStoreKey = preferencesKey<String>(key)
    val channelsJson = dataStore.data.first()[dataStoreKey] ?: ""
    // 使用TypeToken处理泛型反序列化
    return gson.fromJson(channelsJson, object : TypeToken<List<TvChannel>>() {}.type)
}
  1. 调用修改后的存储方法
private fun saveChannelsLocalStorage(listOfChannels: MutableList<TvChannel>) {
    lifecycleScope.launch {
        saveOnDataStore("channels_list", listOfChannels)
    }
}

注意事项

  • 若使用Preferences DataStore,仅能存储基础数据类型,自定义对象必须先序列化;
  • Proto DataStore在类型安全、性能和扩展性上更优,适合存储复杂数据结构;
  • 建议升级DataStore到稳定版本,避免alpha版本的已知bug。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 14:42:07