Android中利用java反射机制Reflect

一、什么是反射机制

在程序运行期间,java系统始终为所有的对象维护一个运行时的类型标识,这个信息保存着每个对象所属的类的足迹,虚拟机利用运行时信息选择相应的方法执行。

然而,可以通过专门的java类访问这些信息,保存这些信息的类被称为Class(这个Class不是类名关键字的Class),这个名字很容易让人混淆。Object类中的getClass()方法将返回一个Class类型的实例!

例如,最常用的Class的方法是getName(),这个方法将返回类的名字。

Date date = new Date();
Class cl = date.getClass();
String name = cl.getName();

再比如,

getFields()将返回一个包含Field对象的数组,这些对象记录了这个类或其超类的公有域。

getDeclaredField()也将返回一个包含Field对象的数组,这些对象记录了这个类的全部域。

(如果类中没有域,或者Class对象描述的是基本类型或数组类型,这些方法将返回一个长度为0的数组)


getMethods()将返回包含Method对象的数组,这些对象包含所有的公有方法,包括从超类继承来的公有方法。

getDeclaredMethods()也将返回包含Method对象的数组,包含这个类或者接口的全部方法,但不包括由超类继承了的方法。


实际上,java安全机制只允许查看任意对象有哪些域,而不允许读取他们的值。

反射机制的默认行为受限于java的访问控制,然而,可以用setAccessible(true)来获得读取权限。


二、代码举例

下面转载一个例子加深理解(http://blog.csdn.net/nokiaguy/article/details/5770263):

现在我们来看看第一个需求:如果某个应用需要弹出一个对话框。当单击“确定“按钮时完成某些工作,如果这些工作失败,对话框不能关闭。而当成功完成工作后,则关闭对话框。当然,无论何程度情况,单击“取消”按钮都会关闭对话框。这个需求并不复杂,也并不过分(虽然我们可以自己弄个Activity来完成这个工作,也可在View上自己放按钮,但这显示有些大炮打蚊子了,如果对话框上只有一行文本,费这么多劲太不值了)。

既然要控制对放框的关闭行为,首先就得分析是哪些类、哪些代码使这个对话框关闭的。进入AlertDialog类的源代码。在AlertDialog中只定义了一个变量:mAlert。这个变量是AlertController类型。AlertController类是Android的内部类,在com.android.internal.app包中,无法通过普通的方式访问。也无法在Eclipse中通过按Ctrl键跟踪进源代码。但可以直接在Android源代码中找到AlertController.java。我们再回到AlertDialog类中。AlertDialog类实际上只是一个架子。象设置按钮、设置标题等工作都是由AlertController类完成的。因此,AlertController类才是关键。

  View.OnClickListener mButtonHandler = new View.OnClickListener() {
        public void onClick(View v) {
            Message m = null;
            if (v == mButtonPositive && mButtonPositiveMessage != null) {
                m = Message.obtain(mButtonPositiveMessage);
            } else if (v == mButtonNegative && mButtonNegativeMessage != null) {
                m = Message.obtain(mButtonNegativeMessage);
            } else if (v == mButtonNeutral && mButtonNeutralMessage != null) {
                m = Message.obtain(mButtonNeutralMessage);
            }
            if (m != null) {
                m.sendToTarget();
            }

            // Post a message so we dismiss after the above handlers are executed
            mHandler.obtainMessage(ButtonHandler.MSG_DISMISS_DIALOG, mDialogInterface)
                    .sendToTarget();
        }
    };

上面代码通过Handler发送MSG_DISMISS_DIALOG消息来关闭对话框,下面看下Handle的处理:

    private static final class ButtonHandler 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 ButtonHandler(DialogInterface dialog) {
            mDialog = new WeakReference<DialogInterface>(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();
            }
        }
    }


从上面代码的最后可以找到  ((DialogInterface) msg.obj).dismiss();。现在看了这么多源代码,我们来总结一下对话框按钮单击事件的处理过程。在AlertController处理对话框按钮时会为每一个按钮添加一个onclick事件。而这个事件类的对象实例就是上面的mButtonHandler。在这个单击事件中首先会通过发送消息的方式调用为按钮设置的单击事件(也就是通过setPositiveButton等方法的第二个参数设置的单击事件),在触发完按钮的单击事件后,会通过发送消息的方式调用dismiss方法来关闭对话框。而在AlertController类中定义了一个全局的mHandler变量。在AlertController类中通过ButtonHandler类来对象来为mHandler赋值。因此,我们只要使用我们自己Handler对象替换ButtonHandler就可以阻止调用dismiss方法来关闭对话框。下面先在自己的程序中建立一个新的ButtonHandler类(也可叫其他的名)。

class ButtonHandler extends Handler
{

    private WeakReference<DialogInterface> mDialog;

    public ButtonHandler(DialogInterface dialog)
    {
        mDialog = new WeakReference<DialogInterface>(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中的ButtonHandler类很像,只是支掉了switch语句的最后一个case子句(用于调用dismiss方法)和相关的代码。
 下面我们就要为AlertController中的mHandler重新赋值。由于mHandler是private变量,因此,在这里需要使用Java的反射技术来为mHandler赋值。由于在AlertDialog类中的mAlert变量同样也是private,因此,也需要使用同样的反射技术来获得mAlert变量。代码如下:


先建立一个AlertDialog对象:

AlertDialog alertDialog = new AlertDialog.Builder(this)
        .setTitle("abc")
        .setMessage("content")
        .setIcon(R.drawable.icon)
        .setPositiveButton( “确定”,
                new OnClickListener()
                {
                    @Override
                    public void onClick(DialogInterface dialog,
                            int which)
                    {

                    }
                }).setNegativeButton("取消", new OnClickListener()
        {

            @Override
            public void onClick(DialogInterface dialog, int which)
            {
                dialog.dismiss();
            } 
        }).create()


 

上面的对话框很普通,单击哪个按钮都会关闭对话框。下面在调用show方法之前来修改一个mHandler变量的值,OK,下面我们就来见证奇迹的时刻:

     try 
        {
           
            Field field = alertDialog1.getClass().getDeclaredField("mAlert");
            field.setAccessible(true);
           //  获得mAlert变量的值
            Object obj = field.get(alertDialog1);
            field = obj.getClass().getDeclaredField("mHandler");
            field.setAccessible(true);
           //  修改mHandler变量的值,使用新的ButtonHandler类
            field.set(obj, new ButtonHandler(alertDialog1));
        }
        catch (Exception e)
        {
        }
      //  显示对话框
      alertDialog.show();


 

  我们发现,如果加上try   catch语句,单击对话框中的确定按钮不会关闭对话框(除非在代码中调用dismiss方法),单击取消按钮则会关闭对话框(因为调用了dismiss方法)。如果去了try…catch代码段,对话框又会恢复正常了。
    虽然上面的代码已经解决了问题,但需要编写的代码仍然比较多,为此,我们也可采用另外一种方法来阻止关闭对话框。这种方法不需要定义任何的类。
    这种方法需要用点技巧。由于系统通过调用dismiss来关闭对话框,那么我们可以在dismiss方法上做点文章。在系统调用dismiss方法时会首先判断对话框是否已经关闭,如果对话框已经关闭了,就会退出dismiss方法而不再继续关闭对话框了。因此,我们可以欺骗一下系统,当调用dismiss方法时我们可以让系统以为对话框已经关闭(虽然对话框还没有关闭),这样dismiss方法就失效了,这样即使系统调用了dismiss方法也无法关闭对话框了。
    下面让我们回到AlertDialog的源代码中,再继续跟踪到AlertDialog的父类Dialog的源代码中。找到dismissDialog方法。实际上,dismiss方法是通过dismissDialog方法来关闭对话框的,dismissDialog方法的代码如下:

 private void dismissDialog() {
        if (mDecor == null) {
            if (Config.LOGV) Log.v(LOG_TAG,
                    "[Dialog] dismiss: already dismissed, ignore");
            return;
        }
        if (!mShowing) {
            if (Config.LOGV) Log.v(LOG_TAG,
                    "[Dialog] dismiss: not showing, ignore");
            return;
        }

        mWindowManager.removeView(mDecor);

        mDecor = null;
        mWindow.closeAllPanels();
        onStop();
        mShowing = false;
        
        sendDismissMessage();
    }


该方法后面的代码不用管它,先看if(!mShowing){}这段代码。这个mShowing变量就是判断对话框是否已关闭的。因此,我们在代码中通过设置这个变量就可以使系统认为对话框已经关闭,就不再继续关闭对话框了。由于mShowing也是private变量,因此,也需要反射技术来设置这个变量。我们可以在对话框按钮的单击事件中设置mShowing,代码如下:

try
{
    Field field = dialog.getClass()
            .getSuperclass().getDeclaredField(
                    "mShowing");
    field.setAccessible(true);
    //  将mShowing变量设为false,表示对话框已关闭
    field.set(dialog, false);
    dialog.dismiss();

}
catch (Exception e)
{

}


 

将上面的代码加到哪个按钮的单击事件代码中,哪个按钮就再也无法关闭对话框了。如果要关闭对话框,只需再将mShowing设为true即可。要注意的是,在一个按钮里设置了mShowing变量,也会影响另一个按钮的关闭对话框功能,因此,需要在每一个按钮的单击事件里都设置mShowing变量的值。

从本文可以看出,虽然使用普通方法控制对话框的某些功能,但通过反射技术可以很容易地做到看似不可能完成的任务。当然,除了控制对话框的关闭功能外,还可以控制对话框其他的行为,剩下的就靠读者自己挖掘了。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值