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

Kotlin下Firebase Firestore拉取数据进度条与底部导航栏切换异常求解

问题根因
  • 底部导航栏使用replace切换Fragment,每次切回首页时都会销毁重建首页Fragment视图,重复注册Firestore快照监听器,旧监听器未手动移除会在后台触发无效回调
  • Firestore快照监听器的每次回调都会触发进度条显示,当Fragment切到后台时触发回调,此时原LoadingDialog实例已随视图销毁失效,dismiss调用不会生效,返回首页后新触发的进度条会出现残留无法关闭的问题
  • LoadingDialog未做显示状态校验和空安全处理,重复调用show、dismiss时可能出现状态异常
  • 未判断首页数据是否已加载完成,每次监听器触发都会重复显示进度条,即使本地已有全量数据
修复方案

1. 调整底部导航Fragment切换逻辑

将replace切换改为hide+show模式,避免Fragment视图频繁销毁重建,无需重复加载已有数据:

// MainActivity中替换原有replaceFragment方法
private fun replaceFragment(fragment: Fragment) {
    val transaction = supportFragmentManager.beginTransaction()
    // 先隐藏所有已添加的Fragment
    supportFragmentManager.fragments.forEach {
        if (it != fragment) transaction.hide(it)
    }
    // 未添加过的Fragment先添加再显示,已添加的直接显示
    if (!fragment.isAdded) {
        transaction.add(R.id.Fragmentcontainer, fragment, fragment.javaClass.simpleName)
    } else {
        transaction.show(fragment)
    }
    transaction.commit()
}

2. 优化Firestore监听器逻辑

绑定监听器生命周期,销毁视图时主动移除,增加页面存活判断,仅首次加载时显示进度条:

// HomeFragment中增加变量和方法修改
private var listenerRegistration: ListenerRegistration? = null
private var isFirstLoad = true

private fun EventChangeListener() {
    // 加载前清空列表避免重复数据
    productList.clear()
    db = FirebaseFirestore.getInstance()
    listenerRegistration = db.collection("FlashSalesProducts")
        .addSnapshotListener { value, error ->
            if (error != null) {
                Log.e("Firestore Error", error.message.toString())
                // 报错时也要关闭进度条
                if (isAdded && ::loadingDialog.isInitialized) {
                    loadingDialog.dismissDialog()
                }
                return@addSnapshotListener
            }
            value ?: return@addSnapshotListener

            // 仅首次加载且Fragment存活时显示进度条
            if (isFirstLoad && isAdded) {
                loadingDialog.startLoadingDialog()
            }
            
            for (dc: DocumentChange in value.documentChanges) {
                if (dc.type == DocumentChange.Type.ADDED) {
                    productList.add(dc.document.toObject(Products::class.java))
                }
            }
            productsAdapter.notifyDataSetChanged()
            
            // 首次加载完成后关闭进度条,修改加载状态
            if (isFirstLoad && isAdded && ::loadingDialog.isInitialized) {
                loadingDialog.dismissDialog()
                isFirstLoad = false
            }
        }
}

// 销毁视图时主动移除监听器、关闭dialog,避免内存泄漏和窗口泄漏
override fun onDestroyView() {
    super.onDestroyView()
    listenerRegistration?.remove()
    if (::loadingDialog.isInitialized) {
        loadingDialog.dismissDialog()
    }
}

3. 修复LoadingDialog状态异常问题

增加显示状态校验和空安全处理,避免重复创建、空指针问题:

class LoadingDialog(private val activity : Activity) {
    private lateinit var dialog: AlertDialog
    private var isShowing = false
    val TAG = "xxxxxxDialog"

    fun startLoadingDialog(){
        // 已显示则直接返回,避免重复创建
        if (isShowing) return
        val builder : AlertDialog.Builder = AlertDialog.Builder(activity)
        val inflater : LayoutInflater = activity.layoutInflater
        builder.setView(inflater.inflate(R.layout.custom_loading, null))
        builder.setCancelable(false)
        dialog = builder.create()
        dialog.show()
        isShowing = true
        Log.i(TAG,"dialog show")
    }

    fun dismissDialog(){
        // 仅dialog初始化且正在显示时执行关闭
        if (::dialog.isInitialized && dialog.isShowing) {
            dialog.dismiss()
            isShowing = false
            Log.i(TAG,"dialog dismiss")
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 22:06:02