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

向Room数据库添加数据时for循环内数据丢失问题求助

问题分析与解决方案

核心问题原因

  • 协程作用域不稳定:循环中每次创建MainScope(),该作用域未与组件(Activity/Fragment/ViewModel)生命周期绑定,若组件在协程执行完成前销毁,协程会被直接取消,导致部分数据写入失败。同时频繁创建独立协程会引发数据库并发写入冲突,Room虽支持并发,但无限制的并发插入可能导致部分操作被丢弃。
  • response数据访问不安全:在协程中延迟访问response.data,回调函数执行完成后response对象可能被GC回收,后续协程中访问response.data?.Feed?.asMessage会存在潜在风险(即便用!!强制非空,仍可能出现意外问题)。
  • 循环内重复创建协程:每次循环启动新协程执行数据库插入,大量协程同时运行会增加系统调度压力,部分协程可能因资源不足无法完成执行。

修复后的代码示例

步骤1:使用生命周期绑定的协程作用域

如果代码在ViewModel中,使用viewModelScope;在Activity/Fragment中使用lifecycleScope,替换不稳定的MainScope()。

步骤2:提前提取数据并批量插入

先将所有ChatAllMsg对象收集到列表,再一次性批量插入数据库,减少协程创建次数和数据库操作次数。

fun subscribeFeed() {
    try {
        val test = apolloClient(mContext!!).subscribe(FeedSubscription(Channel.NORMAL))
        val callback = object : ApolloSubscriptionCall.Callback<FeedSubscription.Data> {
            override fun onResponse(response: com.apollographql.apollo.api.Response<FeedSubscription.Data>) {
                val messageData = response.data?.Feed?.asMessage
                if (messageData?.__typename == "Message") {
                    val ids = messageData.Ids ?: emptyList()
                    val noteDateAdded = Date()
                    val text = messageData.text ?: ""
                    val type = messageData.type.toString()
                    val time = messageData.time?.toFloat()
                    val messageType = messageData.Type?.toString()
                    
                    // 先收集所有待插入对象
                    val msgList = mutableListOf<ChatAllMsg>()
                    for (id in ids) {
                        val newNote = ChatAllMsg(
                            noteDateAdded,
                            1,
                            text,
                            id,
                            type,
                            "",
                            "issent",
                            time,
                            0,
                            "",
                            messageType
                        )
                        msgList.add(newNote)
                    }
                    
                    // 使用稳定协程作用域执行批量插入
                    lifecycleScope.launch { // ViewModel中替换为viewModelScope
                        try {
                            noteDatabase.addMsgList(msgList)
                        } catch (e: Exception) {
                            Log.e("dbInsertError", e.message.toString())
                        }
                    }
                }
            }

            override fun onFailure(e: ApolloException) {
                Log.e("ApolloError", e.message.toString())
            }

            override fun onCompleted() {}
            override fun onTerminated() {}
        }
        test.execute(callback)
    } catch (exception: ApolloException) {
        Log.e("ApolloInitError", exception.message.toString())
    }
}

步骤3:Room Dao添加批量插入方法

在Dao接口中添加批量插入方法,提升操作效率:

@Dao
interface ChatMsgDao {
    @Insert
    suspend fun addMsg(msg: ChatAllMsg)

    // 新增批量插入方法
    @Insert
    suspend fun addMsgList(msgList: List<ChatAllMsg>)
}

额外注意事项

  • 规避强制非空操作:尽量用?:提供默认值,减少!!带来的崩溃风险。
  • 管理订阅生命周期:在组件销毁时调用test.cancel(),避免内存泄漏和无效订阅。
  • 补充日志排查:在数据库插入的catch块中添加详细日志,便于定位具体插入失败的数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 17:56:09