知识点:

1、利用反射,阻止AlertDialog每次的dismiss事件;


在使用AlertDialog的时候,我们设置positive,negative和neutral的button,在点击之后,即使不手动调用dismiss方法,系统都会自动的帮我们dismiss掉了。

但是我这里可能点击了之后,还有一些时间比较长的工作处理之后,才能够dismiss掉此AlertDialog;那么这就是一个问题了。我们先直接看怎么来阻止这个系统的dismiss事件。

话不多述,我们直接先上代码,看看如何操作的,然后在稍微看看源码,一探究竟:

首先是来一个button,设置点击事件,弹出dialog

@Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_rDialog:
                showDialog();
                break;
        }
    }



然后在activity里头,创建showDialog方法,如下:

/**
     * 利用反射,阻止dialog点击确定或者取消按钮,总是会使得dialog消失的结果
     */
    void showDialog() {
        AlertDialog alertDialog = new AlertDialog.Builder(this)
                .setTitle("title")
                .setMessage("content")
                .setIcon(R.drawable.__leak_canary_icon)
                .setPositiveButton(R.string.ok,
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                //就算阻止了dialog的dismiss事件,这里调用dismiss也还是可以dismiss掉dialog的
//                                dialog.dismiss();
                            }
                        })
                .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                }).create();
        alertDialog.setCanceledOnTouchOutside(false);

        setDialogCancelable(alertDialog);
//        setDialogIsCanceled(alertDialog, false);
        /* 显示对话框 */
        alertDialog.show();
    }

    /**
     * 通过重新设置一个button处理类,达到点击确定按钮不dismiss掉dialog的效果
     *
     * @param alertDialog alertDialog
     */
    void setDialogCancelable(AlertDialog alertDialog) {
        try {
            Field field = alertDialog.getClass().getDeclaredField("mAlert");
            field.setAccessible(true);
            /* 获得mAlert变量的值 */
            Object obj = field.get(alertDialog);
            field = obj.getClass().getDeclaredField("mHandler");
            field.setAccessible(true);
            /* 修改mHandler变量的值,使用新的ButtonHandler类 */
            field.set(obj, new IButtonHandler(alertDialog));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 通过获取mShowing字段,修改它的值,达到点击确定按钮不dismiss掉dialog的效果
     * 但是好像是没有用的,不起效果
     *
     * @param alertDialog alertDialog
     * @param isCanceled  isCanceled false=此dialog已经关闭了,反之则是为关闭
     */
    void setDialogIsCanceled(AlertDialog alertDialog, boolean isCanceled) {
        try {
            /* 这里有一个层级关系需要记住,看字段是属于父类,还是属于父类的父类 */
            Field field = alertDialog.getClass()
                    .getSuperclass().getSuperclass().getDeclaredField("mShowing");
            field.setAccessible(true);
            /* 将mShowing变量设为false,表示对话框已关闭 */
            field.set(alertDialog, isCanceled);
            alertDialog.dismiss();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }




还有涉及到一个IButtonHandler  的类


package com.yaojt.ui.reflect;

import android.content.DialogInterface;
import android.os.Handler;
import android.os.Message;

import java.lang.ref.WeakReference;

/**
 * desc:自定义的dialog点击按钮事件处理机制
 * <p>
 * author:kuyu.yaojt (tanksu)
 * <p>
 * email:yaojt@kuyumall.com
 * <p>
 * date:17/3/7
 */

public class IButtonHandler extends Handler {

    /* 使用弱引用,避免内存泄漏 */
    private WeakReference<DialogInterface> mDialog;

    /**
     * 构造方法
     *
     * @param dialog dialog
     */
    public IButtonHandler(DialogInterface dialog) {
        mDialog = new WeakReference<>(dialog);
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case DialogInterface.BUTTON_POSITIVE:
            case DialogInterface.BUTTON_NEGATIVE:
            case DialogInterface.BUTTON_NEUTRAL:
                ((DialogInterface.OnClickListener) msg.obj).onClick(mDialog.get(), msg.what);
                break;
            /**
             * AlertController,最终都会调用以下代码,并且传入MSG_DISMISS_DIALOG标志
             * // Post a message so we dismiss after the above handlers are executed
             mHandler.obtainMessage(IButtonHandler.MSG_DISMISS_DIALOG, mDialog)
             .sendToTarget();

             然后到下面代码

             //以下是原始的ButtonHandler类 可以看到最后的最后,都会走到这一个入口里去了
             导致所有的dialog方法都会被dismiss掉,只要拦截这个就可以了。做法就是重新定义一个ButtonHandler
             设置取代原来的ButtonHandler实例即可。有一点不好那就是会影响到所有的dialog。
             但是我们在确定监听里头是调用dismiss,是可以dismiss掉dialog的
             其实就是不要dismiss的这个方法入口

             private static final class IButtonHandler extends Handler {
             // Button clicks have Message.what as the BUTTON{1,2,3} constant
             private static final int MSG_DISMISS_DIALOG = 1;

             private WeakReference<DialogInterface> mDialog;

             public IButtonHandler(DialogInterface dialog) {
             mDialog = new WeakReference<>(dialog);
             }

             @Override public void handleMessage(Message msg) {
             switch (msg.what) {

             case DialogInterface.BUTTON_POSITIVE:
             case DialogInterface.BUTTON_NEGATIVE:
             case DialogInterface.BUTTON_NEUTRAL:
             ((DialogInterface.OnClickListener) msg.obj).onClick(mDialog.get(), msg.what);
             break;

             case MSG_DISMISS_DIALOG:
             ((DialogInterface) msg.obj).dismiss();
             }
             }
             }
             */
        }
    }

}




代码就是以上这些,都有注释的了。

反射确实挺好用的,可以在运行时动态地加载一个类,然后调用执行类里面的方法,在高级开发中,这个是必须的技能,要好好学习。

另外,这个也是有风险的做法,其他人也可以使用这个来获取到你的类的相关信息,然后做一些坏事什么的。