跳到主要內容

使用 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:...

透明背景的 AlertDialog

        Android dialog 預設都會有一個背景底色,要怎麼把它去掉呢?其實直接改 AlertDialog 的 style 就可以了,但我改好久…以下的範例是使用了 Support v7 AppCompat 的 theme,其他 theme 我不知道可不可以,有興趣的人可以試試看。 <style name="TransparentDialog" parent="@style/Base.Theme.AppCompat.Dialog"> <item name="android:windowFrame">@null</item> <item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowIsFloating">true</item> <item name="android:windowTitleStyle">@null</item> <item name="android:background">@android:color/transparent</item> <item name="android:windowNoTitle">true</item> <item name="android:alertDialogStyle">@style/TransparentDialog.Color</item> </style> <style name="TransparentDialog.Color" parent="@style/Base.Theme.AppCompat.Dialog"> <item name="androi...

Borderless Ripple Effect using Appcompat v21

        Google 日前發表了新的設計準則: Material design, 其中定義了新的 touch effect: Ripple effect。 而在最近也發佈了新的 support v7 library,可以讓 api level 7 以上都能建立 Material design 的 UI。我自己是滿偷懶的, 都只會設計一種 layout/style, 讓所有 api level 通吃, 此時 support library 就幫了我很大的忙 XDD。         最近剛好看到如何讓 view 按下去的時候會有 ripple effect, Google 官方其實就有提供說明了, 請參考  https://developer.android.com/training/material/animations.html#Touch , 只要在 layout xml, 想出現此效果的view 加上 android:background= ?android:attr/selectableItemBackground  就可以啦~~ 要是想向下相容, 就必需使用 support v7 library, 其實也只是把上面那行改成 android:background="?attr/selectableItemBackground"         但是請注意, 使用上述產生的 ripple effect 最大範圍是 view 的大小。有注意看 Google 的一些 app, Appbar(Action bar/ Toolbar) 上的按鈕按下去的話, ripple effect 是會超出 view, 也就是說, 邊界會是圓形的而不是方形的。要達到這個效果的話, 只要把  selectableItemBackground 改成  selectableItemBackgroundBorderless   就可以啦, support v7 libaray 也可以用喔。         最後再提醒一點, ripple effect 目前...