Android系统原理与源码分析(1):利用Java反射技术阻止通过按钮关闭对话框

简介:

本文为原创,如需转载,请注明作者和出处,谢谢!

    众所周知,AlertDialog类用于显示对话框。关于AlertDialog的基本用法在这里就不详细介绍了,网上有很多,读者可以自己搜索。那么本文要介绍的是如何随心所欲地控制AlertDialog。
    现在我们来看看第一个需求:如果某个应用需要弹出一个对话框。当单击“确定“按钮时完成某些工作,如果这些工作失败,对话框不能关闭。而当成功完成工作后,则关闭对话框。当然,无论何程度情况,单击“取消”按钮都会关闭对话框。
    这个需求并不复杂,也并不过分(虽然我们可以自己弄个Activity来完成这个工作,也可在View上自己放按钮,但这显示有些大炮打蚊子了,如果对话 框上只有一行文本,费这么多劲太不值了)。但使用过AlertDialog的读者都知道,无论单击的哪个按钮,无论按钮单击事件的执行情况如何,对话框是 肯定要关闭的。也就是说,用户无法控制对话框的关闭动作。实际上,关闭对话框的动作已经在Android SDK写死了,并且未给使用者留有任何接口。但我的座右铭是“宇宙中没有什么是不能控制的”。
    既然要控制对放框的关闭行为,首先就得分析是哪些类、哪些代码使这个对话框关闭的。进入AlertDialog类的源代码。在AlertDialog中只 定义了一个变量:mAlert。这个变量是AlertController类型。AlertController类是Android的内部类,在 com.android.internal.app包中,无法通过普通的方式访问。也无法在Eclipse中通过按Ctrl键跟踪进源代码。但可以直接在 Android源代码中找到AlertController.java。我们再回到AlertDialog类中。AlertDialog类实际上只是一个 架子。象设置按钮、设置标题等工作都是由AlertController类完成的。因此,AlertController类才是关键。
    找到AlertController.java文件。打开后不要感到头晕哦,这个文件中的代码是很多地。不过这么多代码对本文的主题也没什么用处。下面就找一下控制按钮的代码。
    在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来处理,代码如下:

 

复制代码
代码
     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)
{
}
  显示对话框
ertDialog.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变量的值。

      从本文可以看出,虽然使用普通方法控制对话框的某些功能,但通过反射技术可以很容易地做到看似不可能完成的任务。当然,除了控制对话框的关闭功能外,还可以控制对话框其他的行为,剩下的就靠读者自己挖掘了。
本文转自银河使者博客园博客,原文链接 http://www.cnblogs.com/nokiaguy/archive/2010/07/27/1786482.html如需转载请自行联系原作者

银河使者
相关文章
|
10天前
|
存储 监控 安全
单位网络监控软件:Java 技术驱动的高效网络监管体系构建
在数字化办公时代,构建基于Java技术的单位网络监控软件至关重要。该软件能精准监管单位网络活动,保障信息安全,提升工作效率。通过网络流量监测、访问控制及连接状态监控等模块,实现高效网络监管,确保网络稳定、安全、高效运行。
38 11
|
20天前
|
XML Java 编译器
Java注解的底层源码剖析与技术认识
Java注解(Annotation)是Java 5引入的一种新特性,它提供了一种在代码中添加元数据(Metadata)的方式。注解本身并不是代码的一部分,它们不会直接影响代码的执行,但可以在编译、类加载和运行时被读取和处理。注解为开发者提供了一种以非侵入性的方式为代码提供额外信息的手段,这些信息可以用于生成文档、编译时检查、运行时处理等。
57 7
|
1月前
|
监控 Java API
如何使用Java语言快速开发一套智慧工地系统
使用Java开发智慧工地系统,采用Spring Cloud微服务架构和前后端分离设计,结合MySQL、MongoDB数据库及RESTful API,集成人脸识别、视频监控、设备与环境监测等功能模块,运用Spark/Flink处理大数据,ECharts/AntV G2实现数据可视化,确保系统安全与性能,采用敏捷开发模式,提供详尽文档与用户培训,支持云部署与容器化管理,快速构建高效、灵活的智慧工地解决方案。
|
2天前
|
移动开发 前端开发 Java
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
JavaFX是Java的下一代图形用户界面工具包。JavaFX是一组图形和媒体API,我们可以用它们来创建和部署富客户端应用程序。 JavaFX允许开发人员快速构建丰富的跨平台应用程序,允许开发人员在单个编程接口中组合图形,动画和UI控件。本文详细介绍了JavaFx的常见用法,相信读完本教程你一定有所收获!
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
|
20天前
|
JavaScript 安全 Java
java版药品不良反应智能监测系统源码,采用SpringBoot、Vue、MySQL技术开发
基于B/S架构,采用Java、SpringBoot、Vue、MySQL等技术自主研发的ADR智能监测系统,适用于三甲医院,支持二次开发。该系统能自动监测全院患者药物不良反应,通过移动端和PC端实时反馈,提升用药安全。系统涵盖规则管理、监测报告、系统管理三大模块,确保精准、高效地处理ADR事件。
|
26天前
|
设计模式 消息中间件 搜索推荐
Java 设计模式——观察者模式:从优衣库不使用新疆棉事件看系统的动态响应
【11月更文挑战第17天】观察者模式是一种行为设计模式,定义了一对多的依赖关系,使多个观察者对象能直接监听并响应某一主题对象的状态变化。本文介绍了观察者模式的基本概念、商业系统中的应用实例,如优衣库事件中各相关方的动态响应,以及模式的优势和实际系统设计中的应用建议,包括事件驱动架构和消息队列的使用。
|
1月前
|
监控 前端开发 Java
【技术开发】接口管理平台要用什么技术栈?推荐:Java+Vue3+Docker+MySQL
该文档介绍了基于Java后端和Vue3前端构建的管理系统的技术栈及功能模块,涵盖管理后台的访问、登录、首页概览、API接口管理、接口权限设置、接口监控、计费管理、账号管理、应用管理、数据库配置、站点配置及管理员个人设置等内容,并提供了访问地址及操作指南。
|
1月前
|
运维 自然语言处理 供应链
Java云HIS医院管理系统源码 病案管理、医保业务、门诊、住院、电子病历编辑器
通过门诊的申请,或者直接住院登记,通过”护士工作站“分配患者,完成后,进入医生患者列表,医生对应开具”长期医嘱“和”临时医嘱“,并在电子病历中,记录病情。病人出院时,停止长期医嘱,开具出院医嘱。进入出院审核,审核医嘱与住院通过后,病人结清缴费,完成出院。
98 3
|
1月前
|
JSON 前端开发 JavaScript
java-ajax技术详解!!!
本文介绍了Ajax技术及其工作原理,包括其核心XMLHttpRequest对象的属性和方法。Ajax通过异步通信技术,实现在不重新加载整个页面的情况下更新部分网页内容。文章还详细描述了使用原生JavaScript实现Ajax的基本步骤,以及利用jQuery简化Ajax操作的方法。最后,介绍了JSON作为轻量级数据交换格式在Ajax应用中的使用,包括Java中JSON与对象的相互转换。
53 1
|
1月前
|
安全 搜索推荐 Android开发
揭秘安卓与iOS系统的差异:技术深度对比
【10月更文挑战第27天】 本文深入探讨了安卓(Android)与iOS两大移动操作系统的技术特点和用户体验差异。通过对比两者的系统架构、应用生态、用户界面、安全性等方面,揭示了为何这两种系统能够在市场中各占一席之地,并为用户提供不同的选择。文章旨在为读者提供一个全面的视角,理解两种系统的优势与局限,从而更好地根据自己的需求做出选择。
108 2