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

Kotlin下Firebase子集合删除指定Document时误删全部且崩溃问题

问题核心原因

  • 你在getDocumentId()方法中使用了实时快照监听器addSnapshotListener,该监听器会持续监听子集合的所有数据变更,只要数据更新就会触发回调。你点击条目调用getDocumentId()会新增一个监听器,首次回调拿到对应ID执行删除后,子集合数据变更会再次触发监听器回调,此时如果position还是之前的数值,就会删除当前快照列表对应位置的新文档,循环往复直到子集合所有文档被删完。
  • 全部文档删完后snapshot.documents是空列表,你依然执行documents[position]取数据,会触发数组索引越界异常,直接导致应用崩溃。
  • 额外隐含问题:每次点击条目都会新增一个addSnapshotListener监听器,你没有做移除处理,就算本次删除完成,监听器还会常驻内存,后续只要通知集合有变更都会触发无效回调,属于内存泄漏隐患。

修复方案

最优方案是在列表渲染阶段就缓存每个条目对应的文档ID,不需要每次点击时再查询:

  1. 首先在列表Adapter的数据源中,给通知数据实体类新增documentId字段,首次拉取通知列表填充数据时,直接把每个文档的ID赋值到对应实体类中。
  2. 点击条目的时候直接取当前条目对应实体类的documentId调用删除方法,直接删掉getDocumentId()方法即可:
// 列表条目点击逻辑修改
itemView.setOnClickListener {
    // 直接取当前条目预存的文档ID
    delete(currentNotificationItem.documentId)
}

// 原delete方法无需修改
private fun delete(docNotificationID: String) {
    db.collection("users").document(auth.currentUser?.email!!)
        .collection("notification").document(docNotificationID)
        .delete()
}

如果一定要在点击时临时查询ID,就用*单次查询get()*替代实时监听器,只会触发一次回调不会循环执行,同时增加索引合法性判断避免崩溃:

private fun getDocumentId() {
    db.collection("users").document(auth.currentUser?.email!!)
        .collection("notification").get() // 单次查询仅回调一次
        .addOnSuccessListener { snapshot ->
            val documents = snapshot.documents
            // 先判断索引是否合法,避免越界崩溃
            if (position in documents.indices) {
                delete(documents[position].id)
            }
        }
        .addOnFailureListener { e ->
            Log.w(ContentValues.TAG, "查询失败", e)
        }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.14 22:24:06