Android系統原理與源碼分析(1):利用Java反射技術阻止通過按鈕關閉對話框

原文出處:博主宇宙的極客http://www.cnblogs.com/nokiaguy/archive/2010/07/27/1786482.html

??? 眾所周知,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變量的值。

???? 從本文可以看出,雖然使用普通方法控制對話框的某些功能,但通過反射技術可以很容易地做到看似不可能完成的任務。當然,除了控制對話框的關閉功能外,還可以控制對話框其他的行為,剩下的就靠讀者自己挖掘了。

轉載于:https://www.cnblogs.com/Free-Thinker/p/4186760.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/376381.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/376381.shtml
英文地址,請注明出處:http://en.pswp.cn/news/376381.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

160 - 40 DaNiEl-RJ.1

環境 Windows xp sp3 工具 1.exeinfo PE 2.ollydbg 查殼 無殼Delphi程序 測試&#xff1a; 按照說明點到這個注冊窗口。 OD載入搜字符串&#xff0c;直接可以定位到這里 0042D4A8 /. 55 push ebp 0042D4A9 |. 8BEC mov ebp,esp 0042D4…

IOS詳解TableView——選項抽屜(天貓商品列表)

在之前的有篇文章講述了利用HeaderView來寫類似QQ好友列表的表視圖。 這里寫的天貓抽屜其實也可以用該方法實現&#xff0c;具體到細節每個人也有所不同。這里采用的是點擊cell對cell進行運動處理以展開“抽屜”。 最后完成的效果大概是這個樣子。 主要的環節&#xff1a; 點擊…

Unicode與JavaScript詳解 [很好的文章轉]

上個月&#xff0c;我做了一次分享&#xff0c;詳細介紹了Unicode字符集&#xff0c;以及JavaScript語言對它的支持。下面就是這次分享的講稿。 一、Unicode是什么&#xff1f; Unicode源于一個很簡單的想法&#xff1a;將全世界所有的字符包含在一個集合里&#xff0c;計算機只…

編輯器使用說明

歡迎使用Markdown編輯器寫博客 本Markdown編輯器使用StackEdit修改而來&#xff0c;用它寫博客&#xff0c;將會帶來全新的體驗哦&#xff1a; Markdown和擴展Markdown簡潔的語法代碼塊高亮圖片鏈接和圖片上傳LaTex數學公式UML序列圖和流程圖離線寫博客導入導出Markdown文件豐…

關于產品的一些思考——百度之百度百科

百度百科最近改版了&#xff0c;發現有些地方不符合一般人的行為習慣。 1.新版本排版 首先應該將摘要&#xff0c;簡介&#xff0c;目錄什么的放在左側&#xff0c;而不是右側&#xff0c;因為我們都是從左到右&#xff0c;從上到下觀察事物的&#xff0c;而且百科的東西我們不…

Python3.6 IDLE 使用 multiprocessing.Process 不顯示執行函數的打印

要運行的程序&#xff1a; import os from multiprocessing import Process import timedef run_proc(name):print(Child process %s (%s) Running...%(name,os.getpid()))# time.sleep(5)if __name__ __main__:print("Show Start:")print(Parent process %s. % os…

復制控制

復制構造函數、賦值操作符和析構函數總稱為復制控制。編譯器自動實現這些操作&#xff0c;但類也可以定義自己的版本。 實現復制控制操作最困難的部分&#xff0c;往往在于識別何時需要覆蓋默認版本。有一種特別常見的情況需要類定義自己的復制控制成員&#xff1a;類具有指針成…

python Requests登錄GitHub

工具&#xff1a; python 3.6 Fiddler4 所需要的庫&#xff1a; requests BeautifulSoup 首先抓包&#xff0c;觀察登錄時需要什么&#xff1a; 這個authenticity_token的值是訪問/login后可以獲取&#xff0c;值是隨機生成的&#xff0c;所以登錄前要獲取一下。 注…

你必須懂的 T4 模板:深入淺出

示例代碼&#xff1a;示例代碼__你必須懂的T4模板&#xff1a;淺入深出.rar (一)什么是T4模板&#xff1f; T4&#xff0c;即4個T開頭的英文字母組合&#xff1a;Text Template Transformation Toolkit。 T4文本模板&#xff0c;即一種自定義規則的代碼生成器。根據業務模型可生…

stdafx.h是什么用處, stdafx.h、stdafx.cpp的作用

http://blog.csdn.net/songkexin/article/details/1750396 stdafx.h頭文件的作用 Standard Application Fram Extend沒有函數庫&#xff0c;只是定義了一些環境參數&#xff0c;使得編譯出來的程序能在32位的操作系統環境下運行。Windows和MFC的include文件都非常大&#xff0c…

python3 Connection aborted.', RemoteDisconnected('Remote end closed connection without response'

在寫爬蟲的時候遇到了問題&#xff0c;網站是asp.net寫的 requests.exceptions.ConnectionError: (Connection aborted., RemoteDisconnected(Remote end closed connection without response,)) 于是就抓包分析&#xff0c;發現只要加了’Accept-Language’就好了。。。 A…

id和instancetype的區別

id返回不確定類型的對象&#xff08;也就是任意類型的對象&#xff09;&#xff0c;- (id)arrayWithData;返回的就是不確定類型的對象&#xff0c;如果執行數組的方法&#xff0c; [- (id)arrayWithData objectOfIndex:0]編譯時不會報錯&#xff0c;但運行時會報錯&#xff0c;…

windows下Java 用idea連接MySQL數據庫

Java用idea連接數據庫特別簡單。 首先就是下載好MySQL數據庫的驅動程序。 鏈接&#xff1a;https://dev.mysql.com/downloads/connector/j/ 然后就是選下載版本了&#xff1a; 選個zip格式的嘛。。 下載完后就解壓。打開idea&#xff0c;建立個簡單的項目 找到這個: …

7-2

#include<stdio.h> int main(void) {int i;int fib[10]{1,1};for(i2;i<10;i)fib[i]fib[i-1]fib[i-2];for(i0;i<10;i){printf("%6d",fib[i]);if((i1)%50)printf("\n");}return 0; } 轉載于:https://www.cnblogs.com/liruijia199531/p/3357481.h…

歲月悄然前行,沒有停留的痕跡

歲月悄然前行&#xff0c;沒有停留的痕跡。月落烏啼&#xff0c;總是千年的風霜;濤聲依舊&#xff0c;不見當初的夜晚。走過歲月的痕跡&#xff0c;已是物是人非。我們在歲月的軌道上行走&#xff0c;不要給歲月太多的裝飾&#xff0c;不要給歲月太多的言語。給它我們隨著時光追…

160 - 41 defiler.1.exe

環境&#xff1a; Windows xp sp3 工具&#xff1a; Ollydbg stud_PE LoadPE 先分析一下。 這次的程序要求更改了&#xff0c;變成了這個&#xff1a; defilers reversme no.1 -----------------------The task of this little, lame reverseme is to add some code to…

HDU-2112 HDU Today

http://acm.hdu.edu.cn/showproblem.php?pid2112 怎樣把具體的字母的地點轉換為數字的函數為題目的重點。 HDU Today Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 11385 Accepted Submission(s): 2663 P…

AndEngine引擎之SmoothCamera 平滑攝像機

SmoothCamera:就相當于現實世界的攝像機&#xff0c;要想照到一個物體&#xff0c;要么是攝像機移動&#xff0c;要么是物體移動到攝像頭的范圍內&#xff0c;想要放大或縮小一個物體&#xff0c;要么是物體向前或向后移動&#xff0c;要么是攝像頭變焦 這里討論的就是攝像頭的…

160 - 44 defiler.1.exe

環境&#xff1a; Windows xp sp3 工具&#xff1a; 1.ollydbg 2.exeinfope 0x00 查殼 無殼就下一步 0x01 分析 隨便輸入個錯的&#xff0c;出現了不知道哪國的語言。有個6&#xff0c;應該就是name的長度要大于6吧 OD載入&#xff0c;搜字符串。 00421BD7 |. 807D…

時間與日期處理

主要有以下類&#xff1a; NSDate -- 表示一個絕對的時間點NSTimeZone -- 時區信息NSLocale -- 本地化信息NSDateComponents -- 一個封裝了具體年月日、時秒分、周、季度等的類NSCalendar -- 日歷類&#xff0c;它提供了大部分的日期計算接口&#xff0c;并且允許您在NSDate和N…