Android 反射、代理调用系统隐藏API方法与接口类连接Wi-Fi
本文轉(zhuǎn)載自:http://www.xwdoor.net/android-reflection-proxy-call-system-hidden-api-method-and-interface-class-wi-fi-connection/
有這樣的需求:通過掃描二維碼獲取Wi-Fi的ssid和密碼,然后自動(dòng)連接。我們先后出了兩個(gè)版本,第一個(gè)版本,是通過網(wǎng)絡(luò)搜索出來融合的初略版本,代碼比較簡(jiǎn)陋,效果就是很多用戶反饋聯(lián)網(wǎng)的問題;第二個(gè)版本是我寫的,基于充分了解聯(lián)網(wǎng)邏輯的基礎(chǔ)上,用kotlin實(shí)現(xiàn)的,效果馬馬虎虎,不過時(shí)不時(shí)還是有用戶反饋聯(lián)網(wǎng)的問題,大概1%左右吧;這次痛定思痛,采用反射調(diào)用系統(tǒng)framework的隱藏方法來實(shí)現(xiàn)連接wifi的功能。
因?yàn)槭欠瓷湔{(diào)用隱藏的方法 connect,且用到了一個(gè)隱藏的回調(diào)接口類ActionListener,所以在調(diào)用connect()方法前,需要先實(shí)現(xiàn)該接口,這就用到了委托、代理的原理
Note: 本文所有代碼均是基于kotlin語言實(shí)現(xiàn)
1.實(shí)現(xiàn) ActionListener 接口類
系統(tǒng)提供的連接wifi的方法有兩個(gè),分別是:
public void connect(WifiConfiguration config, ActionListener listener) public void connect(int networkId, ActionListener listener)- 1
 - 2
 
可以看到,兩個(gè)重載方法都需要傳遞一個(gè)ActionListener接口參數(shù),這個(gè)接口的源代碼如下:
/*** Interface for callback invocation on an application action* @hide*/ public interface ActionListener {/** The operation succeeded */public void onSuccess();/*** The operation failed* @param reason The reason for failure could be one of* {@link #ERROR}, {@link #IN_PROGRESS} or {@link #BUSY}*/public void onFailure(int reason); }- 1
 - 2
 - 3
 - 4
 - 5
 - 6
 - 7
 - 8
 - 9
 - 10
 - 11
 - 12
 - 13
 - 14
 
簡(jiǎn)單來講,接口類ActionListener有兩個(gè)回調(diào)方法,分別是成功回調(diào)onSuccess和失敗回調(diào)onFailure,跟我們平時(shí)使用的網(wǎng)絡(luò)訪問回調(diào)很像,麻煩在于它是隱藏的。
第一步,使用反射和代理實(shí)現(xiàn) ActionListener 接口類,代碼如下:
//通過反射獲取 ActionListener 類 val actionListener = Class.forName("android.net.wifi.WifiManager\$ActionListener") //實(shí)現(xiàn) InvocationHandler 接口,所有的我們需要的回調(diào)方法,都是通過這個(gè)代理回調(diào)來實(shí)現(xiàn)的 //通過第二個(gè)參數(shù)可以判斷回調(diào)的是什么方法 val invocationHandler = InvocationHandler { proxy, method, args ->Log.i("test", "代理回調(diào): ${args.joinToString()}")//通過方法的名稱判斷回調(diào)的是什么方法if (method.name == "onSuccess") {Log.i("test", "聯(lián)網(wǎng)成功")//等待獲取ip地址Log.i("test", "wait ip address available")} else {Log.i("test", "聯(lián)網(wǎng)失敗: $args")} } //創(chuàng)建接口代理類,可以理解這個(gè)就是接口實(shí)現(xiàn)類(實(shí)現(xiàn)了接口 ActionListener 的類) val proxy = Proxy.newProxyInstance(app.classLoader, arrayOf(actionListener), invocationHandler)- 1
 - 2
 - 3
 - 4
 - 5
 - 6
 - 7
 - 8
 - 9
 - 10
 - 11
 - 12
 - 13
 - 14
 - 15
 - 16
 - 17
 
Note: ActionListener中有兩個(gè)回調(diào)方法,這個(gè)時(shí)候我們可以通過第二個(gè)參數(shù)的屬性值method.name來判斷回調(diào)的是哪個(gè)方法
總結(jié)一下,創(chuàng)建代理的步驟如下: 
1. 獲取接口類的類型。如果是公開的(public),就直接類::java.class就行,比如:ActionListener::class.java,如果是隱藏的,就通過反射獲取。 
2. 實(shí)現(xiàn)接口InvocationHandler,ActionListener中的回調(diào)方法的代碼邏輯也是在里邊實(shí)現(xiàn)。 
3. 創(chuàng)建代理,關(guān)聯(lián)ActionListener和InvocationHandler。此時(shí)的代理可以認(rèn)為是ActionListener的實(shí)現(xiàn)類
一語概之,先通過反射獲取隱藏接口類 ActionListener,然后通過代理實(shí)現(xiàn)該接口
2.獲取 connect 方法實(shí)現(xiàn)聯(lián)網(wǎng)操作
接下來就簡(jiǎn)單了,獲取隱藏方法connect()連接Wi-Fi,代碼如下:
//通過反射獲取隱藏方法`connect()` val connectMethod = context.wifiManager.javaClass.getDeclaredMethod("connect", WifiConfiguration::class.java, actionListener) //第二個(gè)參數(shù)需要自己創(chuàng)建,具體的可以參考網(wǎng)絡(luò) connectMethod.invoke(context.wifiManager, config, proxy)- 1
 - 2
 - 3
 - 4
 
通過反射獲取其中一個(gè)連接Wi-Fi的方法:public void connect(WifiConfiguration config, ActionListener listener),然后調(diào)用。需要注意的是,該方法的第一個(gè)參數(shù)是WifiConfiguration類型的,所以需要各位自己去創(chuàng)建了,創(chuàng)建過程比較麻煩,可以參考網(wǎng)絡(luò),我有空的話再寫一篇文章,哈哈。
3.總結(jié)
好了,具體實(shí)現(xiàn)方法已經(jīng)記錄完畢,剩下的估計(jì)就是一些參數(shù)的創(chuàng)建,和回調(diào)方法中的具體代碼邏輯了,最后,再歸納一下步驟吧: 
1. 獲取接口類的類型,方法:反射獲取或者類::java.class 
2. 繼承實(shí)現(xiàn)接口InvocationHandler 
3. 創(chuàng)建代理Proxy,作用是關(guān)聯(lián)第一步中的接口類和第二步中的InvocationHandler 
4. 反射獲取隱藏方法connect()并調(diào)用
總結(jié)
以上是生活随笔為你收集整理的Android 反射、代理调用系统隐藏API方法与接口类连接Wi-Fi的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: Android开源框架源码鉴赏:Fres
 - 下一篇: [Android]你不知道的Androi