利用Java反射技術調用Android中被隱藏的API

1.概述

在方法說明中被標記“@hide”表示該方法是被隱藏的,不能經由SDK訪問。之所以被隱藏,是想阻止開發者使用SDK中那些未完成或不穩定的部分(接口或架構)。如:

    /**
     * Returns true if the specified USB function is currently enabled when in device mode.
     * <p>
     * USB functions represent interfaces which are published to the host to access
     * services offered by the device.
     * </p>
     *
     * @param function name of the USB function
     * @return true if the USB function is enabled
     *
     * {@hide}
     */
    public boolean isFunctionEnabled(String function) {
        try {
            return mService.isFunctionEnabled(function);
        } catch (RemoteException e) {
            Log.e(TAG, "RemoteException in setCurrentFunction", e);
            return false;
        }
    }

爲了在自己的程序中調用這些被隱藏的方法有兩種辦法。第一種方法就是自己去掉Android源碼中的"@hide"標記,然後重新編譯生成一個SDK。另一種方法就是使用Java反射機制了,可以利用這種反射機制訪問存在訪問權限的方法或修改其域。

JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制(注意關鍵詞:運行狀態)換句話說,Java程序可以加載一個運行時才得知名稱的class,獲悉其完整構造(但不包括methods定義),並生成其對象實體、或對其fields設值、或喚起其methods。

反射的機制大概就是,先根據類的名字找到具體的類,然後再進入這個類中,依據特定的方法名和特定的參數類型信息來定位這個類中的具體方法(很多時候一個類裏同名不同參的方法有好幾個)。最後通過method.invoke來將這個方法加載到具體的類裏。

注:如果你正在使用這些非公開的API,你必須知道,你的程序有着極大的風險。基本上,無法保證在下一次的Android OS更新時,這些API不被破壞,也無法保證不同的運營商有着一致的行爲。


2.開發步驟

①獲得類對象。

②根據對象獲得類名。

③根據類名找到具體的類。

④獲得指定的成員方法。

⑤設置成員方法可以被訪問。

⑥通過反射調用成員方法。


3.實例

調用UsbManager類中的isFunctionEnabled方法,但該方法被“@hide”註解,因此通過Java反射來調用該方法。

    private boolean isFunctionEnabled(Context context, String function) {
        try {
            // ①獲得類對象
            UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);

            // ②根據對象獲得類名
            String usbManagerclassName = usbManager.getClass().getName();

            // ③根據類名獲得具體的類
            Class<?> usbManagerClass = Class.forName(usbManagerclassName);

            // ④獲得指定的成員方法
            Method isFunctionEnabledMethod  = usbManagerClass.getDeclaredMethod("isFunctionEnabled", String.class);

            // ⑤設置成員方法可以被訪問
            isFunctionEnabledMethod.setAccessible(true);

            // ⑥通過反射調用成員方法
            return (boolean)isFunctionEnabledMethod.invoke(usbManager, function);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

另外我們可以在源代碼中可以看到,UsbManager類中的isFunctionEnabled方法中實際上事通過IUsbManager實例化對象mService(作爲UsbManager成員變量調用IUsbManager中的isFunctionEnabled方法,因此:

    private boolean isFunctionEnabled2(Context context, String function) {
        try {
            // (1)獲得類對象
            UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);

            // (2)根據對象獲得類名
            String usbManagerclassName = usbManager.getClass().getName();

            // (3)根據類名獲得具體的類
            Class<?> usbManagerClass = Class.forName(usbManagerclassName);

            // (4)類的成員變量
            Field iUsbManagerField = usbManagerClass.getDeclaredField("mService");

            // (5)設置成員變量可以被訪問
            iUsbManagerField.setAccessible(true);

            // (6)獲得成員變量的類的實例化對象
            Object iUsbManager = iUsbManagerField.get(usbManager);

            // (7)根據對象獲得類名
            String iUsbManagerClassName = iUsbManager.getClass().getName();

            // (8)根據類名獲得具體的類
            Class<?> iUsbManagerClass = Class.forName(iUsbManagerClassName);

            // (9)獲得指定的成員方法
            Method isFunctionEnabledMethod  = iUsbManagerClass.getDeclaredMethod("isFunctionEnabled", String.class);

            // (10)設置成員方法可以被訪問
            isFunctionEnabledMethod.setAccessible(true);

            // (11)通過反射調用成員方法
            return (boolean)isFunctionEnabledMethod.invoke(iUsbManager, function);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

4.注意

①特殊情況下,成員方法可能有多個形式,因此getDeclaredMethod獲得指定的成員方法時所指定的參數類型一定要一致。

②setAccessible(true) 並不是將方法的訪問權限改成了public,而是取消java的權限控制檢查,所以即使是public方法,其accessible屬性默認也是false。



發佈了118 篇原創文章 · 獲贊 124 · 訪問量 52萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章