天天看點

(轉載)JAVA反射機制在Android應用開發中的應用

想必學過JAVA的人一定接觸過“反射”(Reflection)這個名詞,簡單的來說,反射機制就是允許程式設計人員在程式運作時來改變程式的結構或者變量的類型。通過這個特性,我們可以在運作時得知某個類的所有成員,包括其屬性和方法,同時也能夠調用這些方法。請注意反射機制的特殊之處就在于可以使用編譯期間完全未知的類,也就是通過反射機制可以加載一個在運作時才得知名字的類,進而取得其内部的成員函數并調用。

下面來通過我準備比賽的過程中遇到的兩個例子來說明JAVA強大的反射機制在Android開發中的應用。

第一個例子。我想大家在做一些Android App時,App最常用到的一個就是登陸界面,當然登陸界面的設計可以在layout中完成,通過使用Eclipse中的插件來繪制我們的UI。但一日我突然發現和不利用Android外觀不錯的AlertDialog?在其内嵌入兩個EditText和兩個Button(其實兩個Button無需嵌入,因為AlertDialog可以很友善的使用setPositiveButton、setNeutralButton以及setNegativeButton來添加按鈕并配置其listener)

先放上一張效果圖

(我個人覺得在EditText前加上使用者名和密碼的TextView是個累贅,為何不試試看TextView的hint屬性呢?很清爽吧)

(轉載)JAVA反射機制在Android應用開發中的應用

可能大家看起來簡單的一個功能,可給我帶來不曉得麻煩。我想實作的功能是,當使用者點選登陸按鈕,如果驗證成功,跳入下一個Activity,如果驗證失敗,原地不動。

使用過AlertDialog的朋友肯定都清楚一個事實,無論你怎麼設定按鍵響應,這個Dialog都會被關閉。

為了解決它,我的第一感覺是,是否可以自己寫一個類來繼承AlertDialog,通過重寫其中的一些關鍵函數來實作我們的功能。(我感覺這方法過于萬能J)

首先我們檢視一下AlertDialog的源碼,可以通過在Eclipse下按住Ctrl的同時點選AlertDialog來跳轉到其源代碼,前提是你已經下載下傳了android的源碼并且放在了sdk的正确目錄下,例如*\android-sdk-windows\platforms\android-10\sources\,我們打開其代碼,800行左右,粗略的浏覽一下,發現幾個重點,首先很惹眼的,800行代碼的類中,隻定義了一個變量:

private AlertController mAlert;

我想這個足夠引起我們的注意,AlertController類是Android的内部類,在包com.android.internal.app中,無法通過普通的方式通路。也無法在Eclipse中通過按Ctrl鍵跟蹤進源代碼,是以我們手動找一下源代碼包中的AlertController.java檔案

(位于android-sdk-windows\platforms\android-10\sources\com\android\internal\app)

打開後,我們隻尋找我們感興趣的部分,比如關鍵詞Button,或者Cancel之類的字眼,在該檔案的開頭,我們看到定義了一個按鈕mButtonPositive,難道不就是開頭提到的setPositiveButton?恩,現在隻能說也許,我們繼續ctrl+f尋找一下mButtonPositive的蹤迹,

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();
        }
    };
      

很快能發現這段比較惹眼的定義,并且最後出現DISMISS是關鍵。我們細看可以發現,if-else的部分其實是綁定了按鈕及其觸發的消息,最後注釋後面的代碼才是關鍵,無論我們按下哪個按鈕,都會執行後面的這句,其實可以猜到了這就是令Dialog Dismiss的部分。

再往下看,我們會發現ButtonHandler的定義

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();
        }
    }
}
      

簡單分析一下,很顯然可以看到,在switch中的MSG_DISMISS_DIALOG分支,((DialogInterface) msg.obj).dismiss(); 這就因該是對話框總是消失的原因了吧。

到此我們找到了根本原因,現在我們考慮一個問題,需要重新寫類來繼承AlertDialog嗎?有沒有更簡單地方法?

我們可以隻重新定義ButtonHandler類,通過JAVA反射機制來使得AlertController中調用我們自己定義的這個handler即可。(注意不通過反射機制是行不通的,因為我們看到handler定義為私有,并且沒有相應的接口~)

下面我們在dialog顯示之前,執行下面的反射即可。

Field field = dialogBuilder.getClass().getDeclaredField("mAlert");
            field.setAccessible(true);
            Object obj = field.get(dialogBuilder);
            field = obj.getClass().getDeclaredField("mHandler");
            field.setAccessible(true);
            field.set(obj,new ButtonHandler(dialogBuilder));
//設定我們自己定義的ButtonHandler
      

OK,大功告成,現在看看我們的Dialog還會驗證失敗後消失麼?

第二個例子就簡單的說一下好了,跟上面的差不多。

在我的應用中,需要手機端實作自動建立WIFI無線熱點,供身邊的人來使用(我的目的是為了和身邊的人交換大量資料,藍牙是不行的)

搜遍了Android SDK 隻發現了一些與WiFi連接配接有關的API,絲毫沒有提及WiFiAP相關的内容。後來在StackOverflow上得到一個前輩的指導,解決的問題。下面我總結一下。其實事後我才發現跟我上面的思路是一樣的。首先我們打開源代碼中的WifiManager.java,位于*android-sdk-windows\platforms\android-10\sources\android\net\wifi下。

/**
	*
	*@hide Dont open up yet
	*/
public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled){
	try{
		return mService.setWifiApEnabled(wifiConfig,enabled);
	}catch(RemoteException e){
		return false;
	}
}
      

直接搜尋WifiAP,我們定位到一個函數setWifiApEnabled,看名字就知道使我們想要的。首先我解釋一下,在源代碼中,有一些API标記為hide,這些API是不允許在程式中調用的。Hidden API之是以被隐藏,是想阻止開發者使用SDK中那些未完成或不穩定的部分(接口或架構)。舉個例子,Bluetooth API在API 5(Android 2.0)上才開放;在API 3 和4上都是用@hide屬性隐藏了。當這些API被驗證和清理後,Google的開發者會移除@hide屬性,并讓其在API 5官方化。很多地方在API 4 和5之間發生了變化。如果你的程式依賴某些隐藏的API,當其部署到新的平台上時,就有可能陷入困境。

回到剛才定位到的函數,我們看到已經标記為hide,既然這樣,如果我們需要再次利用反射機制來調用這個函數,進而實作我們建立Wifi-AP的目的。

具體的跟上面的思路一樣。我隻貼出反射部分代碼:

Method method1 = wifi.getClass().getMethod("setWifiApEnabled",WifiConfiguration.class, boolean.class);
            WifiConfiguration netConfig = new WifiConfiguration();
            netConfig.SSID = "\"Express Sensor\"";
            netConfig.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
            netConfig.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
            netConfig.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
            netConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
            netConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
            netConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
            netConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
            netConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
            netConfig.preSharedKey = "11111111";
            method1.invoke(wifi, netConfig, enabled);
            Method method2 = wifi.getClass().getMethod("getWifiApState");
            state = (Integer) method2.invoke(wifi);
      

 WifiConfiguration 是網絡配置類,用來配置我們的熱點密碼類型,秘密等等。不要忘記在打開AP後更改WP狀态。即method2。

原文:

http://blog.csdn.net/z103594643/article/details/6755369 http://www.ericyue.info/archive/java-reflection-on-android

繼續閱讀