Android屏幕旋转时LiveData(ViewModel)与ProgressDialog异常处理求助
首先咱们拆解下你遇到的核心问题:
当屏幕旋转时,BillDisplayFragment会被销毁并重建。这时候你重新注册了LiveData的观察者,但LiveData默认不会在观察者重新注册时主动发送已有的旧数据(除非数据有更新),所以onChanged方法不会触发,导致ProgressDialog一直显示无法消失。另外还要检查你的ViewModel是否每次都返回新的LiveData实例——如果是的话,重建后的Fragment拿到的LiveData根本没有之前的数据,自然也不会触发回调。
下面给你几个具体的解决方案,一步步来:
1. 确保ViewModel中的LiveData是持久化的单例实例
首先,ViewModel里的getBillGetResponseDisplayById方法不应该每次调用都创建新的MutableLiveData。正确的做法是把LiveData作为ViewModel的成员变量,只有第一次调用时才初始化:
public class BillDisplayViewModel extends ViewModel { private MutableLiveData<BillGetResponse> displayByIdLiveData; public MutableLiveData<BillGetResponse> getBillGetResponseDisplayById(TxnRequest txnRequest) { if (displayByIdLiveData == null) { displayByIdLiveData = new MutableLiveData<>(); // 在这里发起数据请求,比如调用Repository获取账单数据 fetchBillData(txnRequest); } // 如果txnRequest可能变化,这里需要判断是否需要重新发起请求 return displayByIdLiveData; } private void fetchBillData(TxnRequest txnRequest) { // 你的网络请求逻辑,请求完成后调用displayByIdLiveData.setValue(...) } }
这样,屏幕旋转时ViewModel不会被销毁,LiveData实例保持不变,里面的已加载数据也会保留。
2. 观察LiveData前先检查已有数据
在Fragment重建后,注册观察者之前,先检查LiveData当前是否已经有数据。如果有,直接执行关闭ProgressDialog和更新UI的逻辑:
final ProgressDialog pd = new ProgressDialog(getContext()); pd.setMessage(getString(R.string.updating)); pd.show(); MutableLiveData<BillGetResponse> displayByIdLiveData = viewModelForBillDisplay.getBillGetResponseDisplayById(txnRequest); // 先检查LiveData是否已有数据 BillGetResponse existingData = displayByIdLiveData.getValue(); if (existingData != null) { if (btnRecordDisableEnabled != null) btnRecordDisableEnabled.enable(); pd.dismiss(); } if (display_by_id) { displayByIdLiveData.observe(BillDisplayFragment.this, new Observer<BillGetResponse>() { @Override public void onChanged(@Nullable BillGetResponse billGetResponse) { if (btnRecordDisableEnabled != null) btnRecordDisableEnabled.enable(); pd.dismiss(); } }); }
这样一来,如果数据已经加载完成(比如旋转前就拿到了数据),重建后的Fragment会直接关闭ProgressDialog,不需要等待onChanged触发。
3. 处理ProgressDialog的生命周期,避免内存泄漏
屏幕旋转时,原来的Fragment实例会被销毁,如果ProgressDialog持有旧的Context引用,容易造成内存泄漏。建议在Fragment的onDestroyView方法中手动关闭ProgressDialog:
@Override public void onDestroyView() { super.onDestroyView(); if (pd != null && pd.isShowing()) { pd.dismiss(); pd = null; } }
这样可以保证Fragment销毁时,ProgressDialog也会被正确关闭,不会残留。
额外提示:考虑使用DialogFragment替代ProgressDialog
ProgressDialog已经被标记为废弃(deprecated),官方推荐使用DialogFragment来实现弹窗逻辑。DialogFragment会自动处理屏幕旋转等生命周期变化,不需要你手动管理,能避免很多类似的问题。
内容的提问来源于stack exchange,提问作者Santanu Sur

