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
相关产品推荐
相关产品推荐

