DialogFragment存在的问题
- show和dismiss方法不能在Activity.onSaveInstanceState之后调用,如果在Activity尚未创建完毕,或快要销毁前调用,可能会报错
- DialogFragment是通过FragmentManager来显示的,Dialog的创建需要一段时间,可能尚未创建完毕,用户就已经调用了关闭方法,导致了空指针问题
- 在onCreate,onResume,onHidden,用户点击等时刻,都有可能要刷新数据,弹出DialogFragment,在上个弹窗尚未关闭的情况下,其它事件又触发弹窗,就会导致FragmentManager重复添加问题
- 用户操作和代码操作同时触发,流程不好控制,导致重复关闭
- 每次弹窗显示的内容不一样,等Dialog创建完成时,内容字段可能已经被其它弹窗任务修改了
解决方案
- 用commitAllowingStateLoss和dismissAllowingStateLoss来取代show和dismiss方法
- 创建一个DialogState来记录DialogFragment的状态,只有未使用或已关闭的Dialog才能再显示,只有已经显示的Dialog才能关闭
- 创建一个stateLock来保证DialogState的同步性,防止多个线程和任务同时修改DialogState,引起DialogState数值错乱
- 创建一个showLock来保证同一时间只有一个方法能使用DialogState,比如两个show方法,可能同时进入同步块,连续显示两次,就会出问题。showLock则可以保证第一个show方法执行并关闭后,第二个show方法才能进入同步块
- 记录DialogFragment打卡和关闭的次数,如果关闭次数大于打开次数,则忽略关闭请求
- 创建一个temp变量来分别记录每次任务要显示的内容,这样不同任务之间就不会出现信息覆盖问题
实现代码 这里以一个消息框为例,里面有些代码是封装过的工具类,请理解思路后替换成自己的实现代码
//加载框
public class LoadingDialog extends DialogFragment {
final Object stateLock = new Object();
final ReentrantLock showLock = new ReentrantLock();
DialogState state = DialogState.CREATED;
CommonActivity ctx;
String bufferMessage = "";
String message = "";
Integer openTimes = 0;
//创建
public static LoadingDialog create(CommonActivity ctx) {
//创建
LoadingDialog dialog = new LoadingDialog();
dialog.ctx = ctx;
dialog.setCancelable(false);
return dialog;
}
//设置消息
public LoadingDialog message(Object message) {
bufferMessage = String.valueOf(message);
return this;
}
//显示
public LoadingDialog show() {
final String tempMessage = bufferMessage;
synchronized (openTimes) {
openTimes++;
}
Threads.post(() -> {
while (state != DialogState.CREATED && state != DialogState.CLOSED || showLock.isLocked()) Threads.sleep(10);
showLock.lock();
Console.info("show", "lock");
synchronized (stateLock) {
Console.info("show", Threads.currentThreadId(), "Lock");
ctx.post(() -> {
synchronized (stateLock) {
message = tempMessage;
FragmentManager manager = ctx.getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(this, Texts.random(false, false));
transaction.commitAllowingStateLoss();
}
});
Console.info("DialogState", "OPENING");
state = DialogState.OPENING;
showLock.unlock();
Console.info("show", "unlock");
Console.info("show", Threads.currentThreadId(), "Unlock");
}
});
return this;
}
//关闭
public LoadingDialog close() {
synchronized (openTimes) {
if (openTimes {
while (state != DialogState.OPEN || showLock.isLocked()) Threads.sleep(10);
showLock.lock();
Console.info("show", "lock");
synchronized (stateLock) {
Console.info("close", Threads.currentThreadId(), "Lock");
ctx.postLater(() -> {
synchronized (stateLock) {
dismissAllowingStateLoss();
}
}, 300);
Console.info("DialogState", "CLOSING");
state = DialogState.CLOSING;
showLock.unlock();
Console.info("show", "unlock");
Console.info("close", Threads.currentThreadId(), "Unlock");
}
});
return this;
}
//销毁
public LoadingDialog closeImmediately() {
synchronized (openTimes) {
if (openTimes {
while (state != DialogState.OPEN || showLock.isLocked()) Threads.sleep(10);
showLock.lock();
synchronized (stateLock) {
Console.info("closeImmediately", Threads.currentThreadId(), "Unlock");
ctx.post(() -> {
synchronized (stateLock) {
dismissAllowingStateLoss();
}
});
Console.info("DialogState", "CLOSING");
state = DialogState.CLOSING;
showLock.unlock();
Console.info("closeImmediately", Threads.currentThreadId(), "Unlock");
}
});
return this;
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
View root = getActivity().getLayoutInflater().inflate(R.layout.layout_loading_dialog, null);
builder.setView(root);
AlertDialog dialog = builder.create();
//设置图片动画
ImageView iv = root.findViewById(R.id.iv);
AnimationDrawable animation = (AnimationDrawable) getResources().getDrawable(R.drawable.progress_m01);
iv.setImageDrawable(animation);
animation.start();
//设置提示信息
TextView messageText = root.findViewById(R.id.text_msg);
messageText.setText(message);
//更新状态
synchronized (stateLock) {
Console.info("DialogState", "OPEN");
state = DialogState.OPEN;
}
return dialog;
}
@Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
synchronized (stateLock) {
Console.info("DialogState", "CLOSED");
state = DialogState.CLOSED;
}
}
}