【Android】【UI】解决DialogFragment反复使用引起的并发问题和状态错误问题

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 < 1) return this;
	            openTimes--;
	        }
	        Threads.post(() -> {
	            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 < 1) return this;
	            openTimes--;
	        }
	        Threads.post(() -> {
	            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;
	        }
	    }
	}

你可能感兴趣的:(android-疑难杂症研究)