跳到主要內容

使用 hide/internal Android API

        有時在開發 Android app 時,可能會想使用非公開的 API (就是有註解為 @hide),這時候可以使用 Java 的 Reflection 機制來達成。本文並不討論此機制的原理,有興趣的網友可以自行 GOOGLE

        在 Android 要使用 Reflection 機制有兩項前提,分別是:

1. 必須要知道要使用的 API 其完整 class name 及 method name,class name 要包含 package             name

2. 使用此 API 所需的 permission ( 加在 AndroidManifest 的)

        其實上述這兩點只要看 source code 就可以知道了,而網路上都有 source code 可以看,所以應該不是什麼大問題… 底下是一個簡單的範例:

WifiManager wifiMgr = (WifiManager) getSystemService(Context.WIFI_SERVICE);
WifiConfiguration wifiConfig = null;
try
{
    Class WifiMgrClz = Class.forName(WifiManager.class.getName());
    Method getWifiApCOnfiguration = WifiMgrClz.getMethod("getWifiApConfiguration");
    wifiConfig = (WifiConfiguration) getWifiApCOnfiguration.invoke(wifiMgr);

    Class[] paramOfClass = new Class[2];
    paramOfClass[0] = WifiConfiguration.class;
    paramOfClass[1] = Boolean.TYPE;

    Method setWifiApEnabled = WifiMgrClz.getMethod("setWifiApEnabled", paramOfClass);

    Object[] paramOfObject = new Object[2];
    paramOfObject[0] = wifiConfig;
    paramOfObject[1] = Boolean.valueOf(true);

    setWifiApEnabled.invoke(wifiMgr, paramOfObject);
} catch (Exception e)
{
    // TODO Auto-generated catch block
    e.printStackTrace();
}

        上面的程式碼用來開啟 WIFI AP,因為這些 method 是不公開的,所以要用這個 method 就要用 Reflection。我們可以看到,Class.forName 要帶的參數是要取得的 class name,一般情形可以用 class.getName() 來取得。假如整個 class 都是 hide 的話,就要從 source code 取得 class name。因此

Class WifiMgrClz = Class.forName(WifiManager.class.getName());    

可以改成以下型式:

Class WifiMgrClz = Class.forName(“android.net.wifi.WifiManager”);    

        Class 的 getMethod 就是用來取得想使用的 method。第一個參數代表 method name,之後的參數表示此 method 所傳入的參數型態

Class[] paramOfClass = new Class[2];
paramOfClass[0] = WifiConfiguration.class;
paramOfClass[1] = Boolean.TYPE;    

        代表 setWifiApEnabled 這個 method 需要兩個參數,一個是 WifiConfiguration object,另一個是 boolean 值。不帶參數及帶參數的 method 取得分別如下:

Method getWifiApCOnfiguration = WifiMgrClz.getMethod("getWifiApConfiguration");  

Method setWifiApEnabled = WifiMgrClz.getMethod("setWifiApEnabled", paramOfClass);  
        再來就是 Method 的 invoke method,第一個參數表示要 invoke 此 method 的 class object,依上面的例子就是 WifiManager instance object,假如要呼叫的是 static method,則可以帶入 null 省略。第二個參數就是表示實際要傳入此 method 的參數:

Object[] paramOfObject = new Object[2];
paramOfObject[0] = wifiConfig;
paramOfObject[1] = Boolean.valueOf(true);    

        最後就是 invoke method:

setWifiApEnabled.invoke(wifiMgr, paramOfObject);  

大概就是這樣吧...

P.S. 我個人覺得這跟 JNI 很像,懂其中一種應該另一種會學很快吧XD

參考資料

1. java.lang.Class

2. java.lang.reflect.Method

---

留言

這個網誌中的熱門文章

如何把 Status Bar 變透明

        Android 從 4.4 (KitKat, api level 19) 後才支援這個功能, 到了 5.0 (Lollipop, api level 21) 自訂性更高, 可以讓我們設定各種顏色, 當然也包含透明色。以下分別介紹如何使用這兩種版本的方法。         方法1: 利用 attribute " android:windowTranslucentStatus ", 在 style.xml 加上這個 attribute 就好。要注意的是 Android 版本要在 4.4 以上才可以用這個 attribute: <resources> <!-- Base application theme for API 19+. This theme completely replaces AppTheme from res/values/styles.xml on API 19+ devices. --> <style name="AppTheme" parent="@style/AppBaseTheme"> <!-- API 19 theme customizations can go here. --> <item name="android:windowTranslucentStatus">true</item> </style> </resources>         下面的圖分別為 4.4 跟 5.0 的手機使用這個 attribute 的結果:         因為設定了這個 attribute, 畫面會從 status bar 下方開始畫。要解決這個有兩種方法, 第一個是在 layout 畫面設定 attribute "android:fitsSystemWindows " <RelativeLayout xmlns:android="http:...

Android O 多了什麼新東西 ??

Background Execution Limits 1. 大部份在 AndroidMenifast 宣告的 Implicit BroadcastReceiver 不會被呼叫,但有些還是可以 work,如以下: BOOT_COMPL ETELOCALE_CHANGED USB_DEVICE_ATTACHED Explicit BroadcastReceiver 沒什麼改變,還是可正常發送。Runtime Implicit BroadcastReceiver 也沒什麼改變。 2. Service 也有些改變,如以下:   Run minimally after the app has been background (這要開發者怎麼決定哪些是一定要在背景執行的阿…) App 在背景時,不能使用 startService() 跟 PendingIntent Foreground service 不變,多了新 API 可以使用,NotificationManager#startServiceInForeground 3. When you app enter cache state with no active component, weak locks be automatically released. 4. Location Background Location Limited updates, few times per hour Passive Location Unchanged (這句在說啥?) 有其他 app 在前景使用 Location update,你的 background location 更新的頻率會跟前景一樣 (所以重點就是要有 app 在前景要 location 的資料!) Foreground Location 沒改變 Settings.Secure.ANDROID_ID 其實這個我看不懂他在說什麼,之前完全沒碰過… pre-O:每個 app 的 ID 都一樣 post-O:每個 app 都會有個獨立的 ID (影片說由 Google Play Service 管嗎?) Account access 不能用 GET_ACCOUNTS 這個 permis...

Deprecated API

        當透過 IDE 使用 API,或是查 Document 時,常會看到一段途述:  This method was deprecated… 這是什麼意思呢?這是告訴我們還是可以使用此 method,但未來可能會沒有用、甚至是 在新版本的 SDK 會把這個 method 移掉 。         以 API level 11 出現的 Fragment  為例子,在使用時常會 override 一個 callback 叫 onAttach (Activity activity),但到了 API level 23 (Lollipop) 時,可以發現 onAttach (Activity activity) 被標記為 deprecated 了,取而代之的是 onAttach (Context context)。         像我個人寫程式不太喜歡出現 warning,而使用到 deprecated api 會被 Android Studio 檢查出來,我在用 Fragment 時就直接都換成 onAttach (Context context),結果程式在 api level 較低的手機就出現不符合預期的行為。Debug 了一陣子才發現 onAttach (Context context) 是新的 API,舊版本 SDK 當然沒有這個  API,自然就不會執行到該 API。         因此,雖然有些 API 被標記為 deprecated,但當你的程式還需要支援較舊版本時,還是得使用這些 API,假如要用的 API 在 Support Library  也有對應版本的話,我會建議用 Support Library 版本,因為放在裡面的 API 本來就是為了向下相容而設計的。以 Support Libaray 裡的Fragment 來說,onAttach (Activity activity) 一樣被標記為 deprecated,也一樣有 onAttach (Context context) ...