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

本文揭示了如何在Android应用中灵活控制AlertDialog的行为,包括实现特定需求下的按钮交互逻辑,以及通过反射技术绕过默认关闭机制。通过实例代码展示如何修改AlertDialog的关闭逻辑,以满足特定的应用场景需求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文为原创,如需转载,请注明作者和出处,谢谢!众所周知,alertdialog类用于显示对话框。关于alertdialog的基本用法在这里就不详细介绍了,网上有很多,读者可以自己搜索。那么本文要介绍的是如何随心所欲地控制alertdialog。

现在我们来看看第一个需求:如果某个应用需要弹出一个对话框。当单击“确定“按钮时完成某些工作,如果这些工作失败,对话框不能关闭。而当成功完成工作后,则关闭对话框。当然,无论何程度情况,单击“取消”按钮都会关闭对话框。

这个需求并不复杂,也并不过分(虽然我们可以自己弄个activity来完成这个工作,也可在view上自己放按钮,但这显示有些大炮打蚊子了,如果对话 框上只有一行文本,费这么多劲太不值了)。但使用过alertdialog的读者都知道,无论单击的哪个按钮,无论按钮单击事件的执行情况如何,对话框是 肯定要关闭的。也就是说,用户无法控制对话框的关闭动作。实际上,关闭对话框的动作已经在androidsdk写死了,并且未给使用者留有任何接口。但我的座右铭是“宇宙中没有什么是不能控制的”。

既然要控制对放框的关闭行为,首先就得分析是哪些类、哪些代码使这个对话框关闭的。进入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 weakreferencedialoginterface> mdialog;

public buttonhandler(dialoginterface dialog) {

mdialog = new weakreferencedialoginterface>(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 weakreferencedialoginterface> mdialog;

public buttonhandler(dialoginterface dialog)

{

mdialog = new weakreferencedialoginterface>(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();

我们发现,如果加上trycatch语句,单击对话框中的确定按钮不会关闭对话框(除非在代码中调用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变量的值。

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


======================================================
在最后,我邀请大家参加新浪APP,就是新浪免费送大家的一个空间,支持PHP+MySql,免费二级域名,免费域名绑定 这个是我邀请的地址,您通过这个链接注册即为我的好友,并获赠云豆500个,价值5元哦!短网址是http://t.cn/SXOiLh我创建的小站每天访客已经达到2000+了,每天挂广告赚50+元哦,呵呵,饭钱不愁了,\(^o^)/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值