編輯:關於Android編程
花了幾天時間,研究了一下Java的反射機制。在這裡總結一下這幾天學習的成果,一來分享自己的學習過程和在學習中遇到的問題,二來是給像我一樣不太了解Java反射機制的同學做一個簡單的介紹。在文章後面會鏈接一個Android反射機制的應用程序。
一、反射的概念及在Java中的類反射
反射主要是指程序可以訪問、檢測和修改它本身狀態或行為的一種能力。在計算機科學領域,反射是一類應用,它們能夠自描述和自控制。這類應用通過某種機制來實現對自己行為的描述和檢測,並能根據自身行為的狀態和結果,調整或修改應用所描述行為的狀態和相關的語義。
在Java中的反射機制,被稱為Reflection。(大家看到這個單詞,第一個想法應該就是去開發文檔中搜一下了。)它允許運行中的Java程序對自身進行檢查,並能直接操作程序的內部屬性或方法。Reflection機制允許程序在正在執行的過程中,利用ReflectionAPIs取得任何已知名稱的類的內部信息,包括:package、typeparameters、superclass、implementedinterfaces、innerclasses、outerclasses、fields、constructors、methods、modifiers等,並可以在執行的過程中,動態生成Instances、變更fields內容或喚起methods。
好,了解這些,那我們就知道了,我們可以利用反射機制在Java程序中,動態的去調用一些protected甚至是private的方法或類,這樣可以很大程度上滿足我們的一些比較特殊需求。你當然會問,反射機制在Android平台下有何用處呢?
我們在進行Android程序的開發時,為了方便調試程序,並快速定位程序的錯誤點,會從網上下載到對應版本的AndroidSDK的源碼(這裡給大家提供一個2.3.3版本的下載鏈接)。你會發現很多類或方法中經常加上了“@hide”注釋標記,它的作用是使這個方法或類在生成SDK時不可見,那麼我們的程序可能無法編譯通過,而且在最終發布的時候,就可能存在一些問題。
那麼,對於這個問題,第一種方法就是自己去掉Android源碼中的"@hide"標記,然後重新編譯生成一個SDK。另一種方法就是使用Java反射機制了,可以利用這種反射機制訪問存在訪問權限的方法或修改其域。
廢話半天,該入正題了,在進入正題之前,先給上一個反射測試類的代碼,該代碼中定義了我們需要進行反射的類,該類並沒有實際的用途,僅供做為測試類。提示:本文提供的代碼,並不是Android平台下的代碼,而是一個普通的Java程序,僅僅是對Java反射機制的Demo程序,所以大家不要放在Android下編譯啊,否則出現問題,別追究我的責任啦!
ReflectionTest.java
packagecrazypebble.reflectiontest;
importjava.awt.event.ActionEvent;
importjava.awt.event.ActionListener;
importjava.io.Serializable;
publicclassReflectionTest extendsObject implementsActionListener,Serializable{
// 成員變量
privateintbInt;
publicInteger bInteger = newInteger(4);
publicString strB = "crazypebble";
privateString strA;
// 構造函數
publicReflectionTest() {
}
protectedReflectionTest(intid, String name) {
}
// 成員方法
publicintabc(intid, String name) {
System.out.println("crazypebble ---> " + id + "-" + name);
return0;
}
protectedstaticvoidedf() {
}
@Override
publicvoidactionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
}
}
二、反射機制中需要使用到的類
我把需要使用的類列在下表中,其中對我們特別有用的類,通過著重標記顯示出來,並將在後面的使用中逐步解釋:
三、Class類
首先向大家說明一點,Class本身就是一個類,Class是該類的名稱。看以下下面這個類的定義:
publicclassMyButtonextendsButton{...}
注意到上面的class的首字母是小寫,它表示的是一種類類型,但是我們的Class是一個類,相當於上面定義的MyButton類。所以,千萬不要把這裡的Class做為一個類類型來理解。明白這一點,我們繼續。
Class類是整個Java反射機制的源頭,Class類本身表示Java對象的類型,我們可通過一個Object對象的getClass()方法取得一個對象的類型,此函數返回的就是一個Class類。獲取Class對象的方法有很多種:
在平時的使用,要注意對這幾種方法的靈活運用,尤其是對Class.forName()方法的使用。因為在很多開發中,會直接通過類的名稱取得Class類的對象。
四、獲取類的相關信息
1、獲取構造方法
Class類提供了四個public方法,用於獲取某個類的構造方法。
ConstructorgetConstructor(Class[]params) 根據構造函數的參數,返回一個具體的具有public屬性的構造函數
ConstructorgetConstructors() 返回所有具有public屬性的構造函數數組
ConstructorgetDeclaredConstructor(Class[]params) 根據構造函數的參數,返回一個具體的構造函數(不分public和非public屬性)
ConstructorgetDeclaredConstructors() 返回該類中所有的構造函數數組(不分public和非public屬性)
由於Java語言是一種面向對象的語言,具有多態的性質,那麼我們可以通過構造方法的參數列表的不同,來調用不同的構造方法去創建類的實例。同樣,獲取不同的構造方法的信息,也需要提供與之對應的參數類型信息;因此,就產生了以上四種不同的獲取構造方法的方式。
get_Reflection_Constructors()
/**
* 獲取反射類中的構造方法
* 輸出打印格式:"Modifier修飾域 構造方法名(參數類型列表)"
*/
publicstaticvoidget_Reflection_Constructors(ReflectionTest r) {
Class temp = r.getClass();
String className = temp.getName(); // 獲取指定類的類名
try{
Constructor[] theConstructors = temp.getDeclaredConstructors(); // 獲取指定類的公有構造方法
for(inti = 0; i < theConstructors.length; i++) {
intmod = theConstructors[i].getModifiers(); // 輸出修飾域和方法名稱
System.out.print(Modifier.toString(mod) + " " + className + "(");
Class[] parameterTypes = theConstructors[i].getParameterTypes(); // 獲取指定構造方法的參數的集合
for(intj = 0; j < parameterTypes.length; j++) { // 輸出打印參數列表
System.out.print(parameterTypes[j].getName());
if(parameterTypes.length > j+1) {
System.out.print(", ");
}
}
System.out.println(")");
}
} catch(Exception e) {
e.printStackTrace();
}
}
2、獲取類的成員方法
與獲取構造方法的方式相同,存在四種獲取成員方法的方式。
MethodgetMethod(Stringname,Class[]params) 根據方法名和參數,返回一個具體的具有public屬性的方法
Method[]getMethods() 返回所有具有public屬性的方法數組
MethodgetDeclaredMethod(Stringname,Class[]params) 根據方法名和參數,返回一個具體的方法(不分public和非public屬性)
Method[]getDeclaredMethods() 返回該類中的所有的方法數組(不分public和非public屬性)
get_Reflection_Method()
/**
* 獲取反射類的方法
* 打印輸出格式:"RetType FuncName(paramTypeList)"
*/
publicstaticvoidget_Reflection_Method(ReflectionTest r) {
Class temp = r.getClass();
String className = temp.getName();
/*
* Note: 方法getDeclaredMethods()只能獲取到由當前類定義的所有方法,不能獲取從父類繼承的方法
* 方法getMethods() 不僅能獲取到當前類定義的public方法,也能得到從父類繼承和已經實現接口的public方法
* 請查閱開發文檔對這兩個方法的詳細描述。
*/
//Method[] methods = temp.getDeclaredMethods();
Method[] methods = temp.getMethods();
for(inti = 0; i < methods.length; i++) {
// 打印輸出方法的修飾域
intmod = methods[i].getModifiers();
System.out.print(Modifier.toString(mod) + " ");
// 輸出方法的返回類型
System.out.print(methods[i].getReturnType().getName());
// 獲取輸出的方法名
System.out.print(" " + methods[i].getName() + "(");
// 打印輸出方法的參數列表
Class[] parameterTypes = methods[i].getParameterTypes();
for(intj = 0; j < parameterTypes.length; j++) {
System.out.print(parameterTypes[j].getName());
if(parameterTypes.length > j+1) {
System.out.print(", ");
}
}
System.out.println(")");
}
}
在獲取類的成員方法時,有一個地方值得大家注意,就是getMethods()方法和getDeclaredMethods()方法。
getMethods():用於獲取類的所有的public修飾域的成員方法,包括從父類繼承的public方法和實現接口的public方法;
getDeclaredMethods():用於獲取在當前類中定義的所有的成員方法和實現的接口方法,不包括從父類繼承的方法。
大家可以查考一下開發文檔的解釋:
getMethods()-ReturnsanarraycontainingMethodobjectsforallpublicmethodsfortheclassCrepresentedbythisClass.
MethodsmaybedeclaredinC,theinterfacesitimplementsorinthesuperclassesofC.
Theelementsinthereturnedarrayareinnoparticularorder.
getDeclaredMethods()-ReturnsaMethodobjectwhichrepresentsthemethodmatchingthespecifiednameandparametertypes
thatisdeclaredbytheclassrepresentedbythisClass.
因此在示例代碼的方法get_Reflection_Method(...)中,ReflectionTest類繼承了Object類,實現了actionPerformed方法,並定義如下成員方法:
通過這兩個語句執行後的結果不同:
a、Method[]methods=temp.getDeclaredMethods()執行後結果如下:
b、Method[]methods=temp.getMethods()執行後,結果如下:
3、獲取類的成員變量(成員屬性)
存在四種獲取成員屬性的方法
FieldgetField(Stringname) 根據變量名,返回一個具體的具有public屬性的成員變量
Field[]getFields() 返回具有public屬性的成員變量的數組
FieldgetDeclaredField(Stringname) 根據變量名,返回一個成員變量(不分public和非public屬性)
Field[]getDelcaredField() 返回所有成員變量組成的數組(不分public和非public屬性)
get_Reflection_Field_Value()
/**
* 獲取反射類中的屬性和屬性值
* 輸出打印格式:"Modifier Type : Name = Value"
* Note: 對於未初始化的指針類型的屬性,將不輸出結果
*/
publicstaticvoidget_Reflection_Field_Value(ReflectionTest r) {
Class temp = r.getClass(); // 獲取Class類的對象的方法之一
try{
System.out.println("public 屬性");
Field[] fb = temp.getFields();
for(inti = 0; i < fb.length; i++) {
Class cl = fb[i].getType(); // 屬性的類型
intmd = fb[i].getModifiers(); // 屬性的修飾域
Field f = temp.getField(fb[i].getName()); // 屬性的值
f.setAccessible(true);
Object value = (Object)f.get(r);
// 判斷屬性是否被初始化
if(value == null) {
System.out.println(Modifier.toString(md) + " " + cl + " : " + fb[i].getName());
}
else{
System.out.println(Modifier.toString(md) + " " + cl + " : " + fb[i].getName() + " = " + value.toString());
}
}
System.out.println("public & 非public 屬性");
Field[] fa = temp.getDeclaredFields();
for(inti = 0; i < fa.length; i++) {
Class cl = fa[i].getType(); // 屬性的類型
intmd = fa[i].getModifiers(); // 屬性的修飾域
Field f = temp.getDeclaredField(fa[i].getName()); // 屬性的值
f.setAccessible(true); // Very Important
Object value = (Object) f.get(r);
if(value == null) {
System.out.println(Modifier.toString(md) + " " + cl + " : " + fa[i].getName());
}
else{
System.out.println(Modifier.toString(md) + " " + cl + " : " + fa[i].getName() + " = " + value.toString());
}
}
} catch(Exception e) {
e.printStackTrace();
}
}
4、獲取類、屬性、方法的修飾域
類Class、Method、Constructor、Field都有一個public方法intgetModifiers()。該方法返回一個int類型的數,表示被修飾對象(Class、Method、Constructor、Field)的修飾類型的組合值。
在開發文檔中,可以查閱到,Modifier類中定義了若干特定的修飾域,每個修飾域都是一個固定的int數值,列表如下:
該類不僅提供了若干用於判斷是否擁有某中修飾域的方法booleanisXXXXX(intmodifiers),還提供一個StringtoString(intmodifier)方法,用於將一個表示修飾域組合值的int數轉換成描述修飾域的字符串。
五、如何調用類中的private方法
在介紹之前,先放一個代碼吧,這段代碼是參考其他文章的代碼拷貝過來的,代碼不算長,但是動態調用類的成員方法的過程講解的通俗易懂。
LoadMethod.java
packagecrazypebble.reflectiontest;
importjava.lang.reflect.Constructor;
importjava.lang.reflect.Method;
publicclassLoadMethod {
/**
* 在運行時加載指定的類,並調用指定的方法
* @paramcName Java的類名
* @paramMethodName 方法名
* @paramtypes 方法的參數類型
* @paramparams 方法的參數值
* @return
*/
publicObject Load(String cName, String MethodName, String[] types, String[] params) {
Object retObject = null;
try{
// 加載指定的類
Class cls = Class.forName(cName); // 獲取Class類的對象的方法之二
// 利用newInstance()方法,獲取構造方法的實例
// Class的newInstance方法只提供默認無參構造實例
// Constructor的newInstance方法提供帶參的構造實例
Constructor ct = cls.getConstructor(null);
Object obj = ct.newInstance(null);
//Object obj = cls.newInstance();
// 構建 方法的參數類型
Class paramTypes[] = this.getMethodTypesClass(types);
// 在指定類中獲取指定的方法
Method meth = cls.getMethod(MethodName, paramTypes);
// 構建 方法的參數值
Object argList[] = this.getMethodParamObject(types, params);
// 調用指定的方法並獲取返回值為Object類型
retObject = meth.invoke(obj, argList);
} catch(Exception e) {
System.err.println(e);
}
returnretObject;
}
/**
* 獲取參數類型,返回值保存在Class[]中
*/
publicClass[] getMethodTypesClass(String[] types) {
Class[] cs = newClass[types.length];
for(inti = 0; i < cs.length; i++) {
if(types[i] != null|| !types[i].trim().equals("")) {
if(types[i].equals("int") || types[i].equals("Integer")) {
cs[i] = Integer.TYPE;
}
elseif(types[i].equals("float") || types[i].equals("Float")) {
cs[i] = Float.TYPE;
}
elseif(types[i].equals("double") || types[i].equals("Double")) {
cs[i] = Double.TYPE;
}
elseif(types[i].equals("boolean") || types[i].equals("Boolean")) {
cs[i] = Boolean.TYPE;
}
else{
cs[i] = String.class;
}
}
}
returncs;
}
/**
* 獲取參數Object[]
*/
publicObject[] getMethodParamObject(String[] types, String[] params) {
Object[] retObjects = newObject[params.length];
for(inti = 0; i < retObjects.length; i++) {
if(!params[i].trim().equals("")||params[i]!=null){
if(types[i].equals("int")||types[i].equals("Integer")){
retObjects[i]= newInteger(params[i]);
}
elseif(types[i].equals("float")||types[i].equals("Float")){
retObjects[i]= newFloat(params[i]);
}
elseif(types[i].equals("double")||types[i].equals("Double")){
retObjects[i]= newDouble(params[i]);
}
elseif(types[i].equals("boolean")||types[i].equals("Boolean")){
retObjects[i]=newBoolean(params[i]);
}
else{
retObjects[i] = params[i];
}
}
}
returnretObjects;
}
}
要調用一個類的方法,首先需要一個該類的實例(當然,如果該類是static,就不需要實例了,至於原因,你懂得!)。
1、創建一個類的實例
在得到一個類的Class對象之後,我們可以利用類Constructor去實例化該對象。Constructor支持泛型,也就是它本身應該是Constructor
在代碼LoadMethod.java和LoadMethodEx.java中,分別給出了兩種實例化Class類的方法:一種是利用Constructor類調用newInstance()方法;另一種就是利用Class類本身的newInstance()方法創建一個實例。兩種方法實現的效果是一樣的。
// 利用newInstance()方法,獲取構造方法的實例
// Class的newInstance方法,僅提供默認無參的實例化方法,類似於無參的構造方法
// Constructor的newInstance方法,提供了帶參數的實例化方法,類似於含參的構造方法
Constructor ct = cls.getConstructor(null);
Object obj = ct.newInstance(null);
Object obj = cls.newInstance();
2、行為
Method類中包含著類的成員方法的信息。在Method類中有一個public成員函數:Objectinvoke(Objectreceiver,Object...args),參數receiver指明了調用對象,參數args指明了該方法所需要接收的參數。由於我們是在運行時動態的調用類的方法,無法提前知道該類的參數類型和返回值類型,所以傳入的參數的類型是Object,返回的類型也是Object。(因為Object類是所有其他類的父類)
如果某一個方法是Java類的靜態方法,那麼Objectreceiver參數可以傳入null,因為靜態方法從不屬於對象。
3、屬性
對類的成員變量進行讀寫,在Field類中有兩個public方法:
Objectget(Objectobject),該方法可用於獲取某成員變量的值
Voidset(Objectobject,Objectvalue),該方法設置某成員變量的值
其中,Object參數是需要傳入的對象;如果成員變量是靜態屬性,在object可傳入null。
六、對LoadMethod.java的優化處理
在上一節中給出的LoadMethod.java中,類LoadMethod對固定參數類型的方法進行了調用,並且參數類型是通過一個String[]數組傳入,然後經過方法getMethodTypesClass()解析之後,才得到了參數的具體的類型。同時在getMethodTypesClass()和getMethodParamObject()方法中,通過對傳入的字符串參數進行過濾後,再處理那些可以匹配中的參數類型,其他不能匹配的參數都做為String對象來處理。如果我們調用的方法所需要的參數不是簡單類型的變量,而是自定義的類對象,或者List列表,再如果我們只知道類名和方法名,不知道方法的參數類型,那我們該如何處理這些情況呢?
因此,我對LoadMethod類進行了一定的優化處理。先附上代碼:
LoadMethodEx.java
packagecrazypebble.reflectiontest;
importjava.lang.reflect.Constructor;
importjava.lang.reflect.Method;
publicclassLoadMethodEx {
/**
* 在運行時加載指定的類,並調用指定的方法
* @paramcName Java的類名
* @paramMethodName 方法名
* @paramparams 方法的參數值
* @return
*/
publicObject Load(String cName, String MethodName, Object[] params) {
Object retObject = null;
try{
// 加載指定的類
Class cls = Class.forName(cName); // 獲取Class類的對象的方法之二
// 利用newInstance()方法,獲取構造方法的實例
// Class的newInstance方法只提供默認無參構造實例
// Constructor的newInstance方法提供帶參的構造實例
Constructor ct = cls.getConstructor(null);
Object obj = ct.newInstance(null);
//Object obj = cls.newInstance();
// 根據方法名獲取指定方法的參數類型列表
Class paramTypes[] = this.getParamTypes(cls, MethodName);
// 獲取指定方法
Method meth = cls.getMethod(MethodName, paramTypes);
meth.setAccessible(true);
// 調用指定的方法並獲取返回值為Object類型
retObject = meth.invoke(obj, params);
} catch(Exception e) {
System.err.println(e);
}
returnretObject;
}
/**
* 獲取參數類型,返回值保存在Class[]中
*/
publicClass[] getParamTypes(Class cls, String mName) {
Class[] cs = null;
/*
* Note: 由於我們一般通過反射機制調用的方法,是非public方法
* 所以在此處使用了getDeclaredMethods()方法
*/
Method[] mtd = cls.getDeclaredMethods();
for(inti = 0; i < mtd.length; i++) {
if(!mtd[i].getName().equals(mName)) { // 不是我們需要的參數,則進入下一次循環
continue;
}
cs = mtd[i].getParameterTypes();
}
returncs;
}
}
我們通過前面幾節的一系列分析,只要我們知道了一個類的類名(包括其包的路徑),那我們就可以通過Class類的一系列方法,得到該類的成員變量、構造方法、成員方法、以及成員方法的參數類型和返回類型、還有修飾域等信息。
如果我們已經知道某個類名和需要動態調用的方法名,怎樣才能不用傳入方法的參數類型就可以調用該方法呢?
在已知類名的情況下,我們可以打印輸出該類的所有信息,當然包括類的成員方法;然後通過給定的方法名,對打印輸出的方法名進行篩選,找到我們需要的方法;再通過該方法的Method對象,得到該方法的參數類型、參數數量、和返回類型。那麼我們在外部動態調用該方法時,就不需要關心該類需要傳入的參數類型了,只需要傳入類名、方法名、參數值的信息即可。筆者實現了一個類LoadMethodEx,先從兩個類的同一個方法需要的參數方面做一個對比:
1、LoadMethodEx類,少了一個參數(方法參數類型列表),本文直接從類LoadMethod內部獲取該參數類型列表,不需要用戶傳入該信息,好處其實也不言而喻了。
2、方法的參數值:類LoadMethod是將所有的方法參數都做為一個String來傳入,在傳入再進行解析;而本文則直接使用Object類型做為參數類型,因為invoke(Objectobj,Object...args)方法本身所需要的參數類型就是Object,避免了不必要的參數類型變換。
在調用LoadMethod的Load()方法時,用戶只需要知道類名、方法名,並且將已經初始化的參數先向上轉型為Object,然後傳遞給Load()方法即可。方法的返回值為Object,這個肯定是由用戶根據自己的需要,再轉換成自己所需的類型。
執行結果
屬性:
public屬性
publicclassjava.lang.Integer : bInteger = 4
publicclassjava.lang.String : strB = crazypebble
public& 非public 屬性
privateint: bInt = 0
publicclassjava.lang.Integer : bInteger = 4
publicclassjava.lang.String : strB = crazypebble
privateclassjava.lang.String : strA
構造方法:
publiccrazypebble.reflectiontest.ReflectionTest()
protectedcrazypebble.reflectiontest.ReflectionTest(int, java.lang.String)
父類/接口:
父類: java.lang.Object
接口0: java.awt.event.ActionListener
接口1: java.io.Serializable
成員方法:
publicintabc(int, java.lang.String)
publicvoidactionPerformed(java.awt.event.ActionEvent)
publicfinalnativevoidwait(long)
publicfinalvoidwait()
publicfinalvoidwait(long, int)
publicbooleanequals(java.lang.Object)
publicjava.lang.String toString()
publicnativeinthashCode()
publicfinalnativejava.lang.Class getClass()
publicfinalnativevoidnotify()
publicfinalnativevoidnotifyAll()
反射機制調用方法:LoadMethod
crazypebble ---> 1-hello, android-1!
反射機制調用方法:LoadMethodEx
crazypebble ---> 2-hello, android-2?
返回結果:0
七、總結
關於反射機制,其實還有一個比較敏感的話題,就是反射機制帶來我們的安全性問題。由於我在這方面研究的不是很深入,所以講不好。大家有空可以跟蹤一下在本文最後提供的兩個鏈接,裡面有一些介紹。
我們介紹了Java的反射機制,但是在Android平台下,反射機制具體有沒有什麼用途呢?答案是肯定的。推薦大家看一篇文章《利用Java反射技術阻止通過按鈕關閉對話框》,這篇文章為CSDN推薦為精品文章,所以還是很值得一看的。我特地從CSDN轉載過來供大家一起學習。
原鏈接:aspx" target="_blank">http://blog.csdn.net/nokiaguy/archive/2010/07/27/5770263.aspx
轉載鏈接:http://www.cnblogs.com/crazypebble/archive/2011/04/13/2014297.html
程序實現的源碼:
AndroidReflection
packagecrazypebble.androidreflection;
importjava.lang.reflect.Field;
importandroid.app.Activity;
importandroid.app.AlertDialog;
importandroid.content.DialogInterface;
importandroid.os.Bundle;
importandroid.view.View;
importandroid.view.View.OnClickListener;
importandroid.widget.Button;
publicclassMainActivity extendsActivity {
/** Called when the activity is first created. */
privatestaticButton btnHandler = null;
privatestaticButton btnShowing = null;
AlertDialog alertDialog = null;
@Override
publicvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnHandler = (Button)findViewById(R.id.btn_mHandler);
btnHandler.setOnClickListener(newButtonListener());
btnShowing = (Button)findViewById(R.id.btn_mShowing);
btnShowing.setOnClickListener(newButtonListener());
alertDialog = newAlertDialog.Builder(this)
.setTitle("abc")
.setMessage("Content")
.setIcon(R.drawable.icon)
.setPositiveButton("確定", newPositiveClickListener())
.setNegativeButton("取消", newNegativeClickListener())
.create();
}
privateclassButtonListener implementsOnClickListener {
@Override
publicvoidonClick(View v) {
switch(v.getId()) {
caseR.id.btn_mHandler:
modify_mHandler();
alertDialog.show();
break;
caseR.id.btn_mShowing:
alertDialog.show();
break;
default:
break;
}
}
}
privateclassPositiveClickListener implementsandroid.content.DialogInterface.OnClickListener {
@Override
publicvoidonClick(DialogInterface dialog, intwhich) {
// 方法二時啟用
modify_dismissDialog(false);
}
}
privateclassNegativeClickListener implementsandroid.content.DialogInterface.OnClickListener {
@Override
publicvoidonClick(DialogInterface dialog, intwhich) {
// 方法一時啟用
//dialog.dismiss();
// 方法二時啟用
modify_dismissDialog(true);
}
}
/*
* 第一種方法:修改AlertController類的private成員變量mHandler的值
*/
publicvoidmodify_mHandler() {
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, newMyButtonHandler(alertDialog));
} catch(Exception e) {
e.printStackTrace();
}
}
/*
* 第二種方法:修改dismissDialog()方法
*/
publicvoidmodify_dismissDialog(booleanflag) {
try{
Field field = alertDialog.getClass().getSuperclass().getDeclaredField("mShowing");
field.setAccessible(true);
// 將mShowing變量設為false,表示對話框已經關閉
field.set(alertDialog, flag);
alertDialog.dismiss();
} catch(Exception e) {
e.printStackTrace();
}
}
}
MyButtonHandler.java
packagecrazypebble.androidreflection;
importjava.lang.ref.WeakReference;
importandroid.content.DialogInterface;
importandroid.os.Handler;
importandroid.os.Message;
publicclassMyButtonHandler extendsHandler{
// Button clicks have Message.what as the BUTTON{1,2,3} constant
privatestaticfinalintMSG_DISMISS_DIALOG = 1;
privateWeakReference
publicMyButtonHandler(DialogInterface dialog) {
mDialog = newWeakReference
}
@Override
publicvoidhandleMessage(Message msg) {
switch(msg.what) {
caseDialogInterface.BUTTON_POSITIVE:
caseDialogInterface.BUTTON_NEGATIVE:
caseDialogInterface.BUTTON_NEUTRAL:
((DialogInterface.OnClickListener) msg.obj).onClick(mDialog.get(), msg.what);
break;
}
}
}
看完上面這篇文章之後,希望大家明確一點的是:反射機制通過voidsetAccessible(booleanflag)方法可以得到一個類的private的方法和屬性,使用這些private的方法和屬性,已經可以做一些超越限制的事情了。所以在使用過程,還需謹慎啊!
在此感謝兩位的文章為本文提供了一定的基礎,為我學習Java的反射機制提供了很好的教程。本文也是一篇學習總結,如果本文對這兩篇文章的作者有冒犯之處,請及時與本人聯系。如果各位同學發現本文和代碼的錯誤之處的話,還請指正,一起學習交流。
附:兩個工程的下載地址,供大家一起學習研究。http://u.115.com/file/f68453a0ca
從一開始寫Android程序,就被告知這些常識1.長度寬度的數值要使用dp作為單位放入dimens.xml文件中2.字體大小的數值要使用sp作為單位,也放入dimens.
活動的啟動模式啟動模式一共有四種,分別是 standard、singleTop、singleTask 和singleInstance,可以在AndroidManifest
關於Handler的總結。 Message:消息,其中包含了消息ID,消息處理對象以及處理的數據等,由MessageQueue統一列隊,終由Handler處理。 H
前文常用的控件介紹了不少,現在就來討論一下手機開發中常用到的畫圖。要掌握Android的畫圖,首先就要了解一下,基本用到的如下一些圖形接口:1.Bitmap,可以來自資源
先占個位置,下次翻譯~ :p Properly stopping a