1.反射的基礎(chǔ)知識:
Java的反射機(jī)制是指在程序的運(yùn)行狀態(tài)中,可以構(gòu)造任意一個(gè)類的對象,可以了解任意一個(gè)對象所屬的類,可以了解任意一個(gè)類的成員變量和方法,可以調(diào)用任意一個(gè)對象的屬性和方法。這種動態(tài)獲取程序信息以及動態(tài)調(diào)用對象的功能稱為Java語言的反射機(jī)制。
反射的基本操作:
獲取類對象
獲取一個(gè)類的 Class 對象有多種方法:
1.1.1使用 Class.forName(String className) 方法,需要傳入類的全限定名,會返回該類的 Class 對象。
1.1.2使用類名.class,例如 String.class。
1.1.3使用對象.getClass() 方法,返回對象所屬的類的 Class 對象。
獲取類的屬性
1.2使用 Class 類的 getField(String name) 方法可以獲取類的指定公有屬性;使用 getDeclaredField(String name) 方法可以獲取類的指定屬性(無論是否公有)。
1.3獲取類的方法
使用 Class 類的 getMethod(String name, Class… parameterTypes) 方法可以獲取類的指定公有方法;使用 getDeclaredMethod(String name, Class… parameterTypes) 方法可以獲取類的指定方法(無論是否公有)。
1.4獲取類的構(gòu)造函數(shù)
使用 Class 類的 getConstructor(Class… parameterTypes) 方法可以獲取類的指定公有構(gòu)造函數(shù);使用 getDeclaredConstructor(Class… parameterTypes) 方法可以獲取類的指定構(gòu)造函數(shù)(無論是否公有)。
1.5動態(tài)調(diào)用方法
通過反射可以動態(tài)調(diào)用類的方法,例如:
Class clazz = Class.forName(“com.example.MyClass”);
Object obj = clazz.newInstance();
Method method = clazz.getMethod(“myMethod”, String.class);
method.invoke(obj, “parameter”);
上述代碼中,我們先獲取 MyClass 的 Class 對象,然后創(chuàng)建 MyClass 的一個(gè)實(shí)例,接著獲取 myMethod 方法的 Method 對象,最后使用 invoke 方法調(diào)用 myMethod 方法并傳入?yún)?shù)。
2.Retrofit源碼
講上圖之前你要本身會使用Retrofit,我們知道Retrofit 的接口是統(tǒng)一集成在一個(gè)接口文件中的,所以上圖中的service參數(shù)就是
類似下圖的接口類
動態(tài)代理主要就是利用Proxy.newProxyInstance方法生成代理類,其必須實(shí)現(xiàn)InvocationHandler invoke方法,邏輯是如果接口中的方法,記住是所有方法,這也是和使用接口繼承去實(shí)現(xiàn)的區(qū)別,接口中定義的方法統(tǒng)一操作,是動態(tài)代理 接口類的一大特征,如果方法是object的方法就直接invoke傳入當(dāng)前對象和參數(shù),如果不是接著往下走,判斷當(dāng)前平臺是java,android,還是linux的,如果是系統(tǒng)默認(rèn)的方法就invoke系統(tǒng)默認(rèn)的方法,否則用loadServiceMethod來invoke,然后我們看一下下圖的loadServiceMethod方法,最后調(diào)用了ServiceMethod.parseAnnotations
然后是HttpServiceMethod.parseAnnotations
經(jīng)過上圖的幾次跳轉(zhuǎn)后,下圖紅色箭頭的就是最后的CallAdapter接口承接的對象即來自于 callAdapterFactories
由下圖我們找到了是platform.createDefaultCallAdapterFactories產(chǎn)生的
那我們平臺是android 就是android24了,CompletableFutureCallAdapterFactory
和 DefaultCallAdapterFactory
然后就是代理的實(shí)質(zhì)代碼了call.enqueue
最后會在build里面execute,完成整個(gè)流程,代理執(zhí)行完畢。
提一句,從下圖可以看出來Proxy.newProxyInstance動態(tài)代理里面的實(shí)現(xiàn)用到了反射。
3.關(guān)于Hook的我做了一個(gè)demo,英文翻譯過來是鉤子,用一個(gè)成語表達(dá)就是偷梁換柱。用kotlin實(shí)現(xiàn)的,比起java會有點(diǎn)不一樣的:
看下圖,先說要干什么,hook 一下 view的setOnClicklistener的View.OnclickListener。那怎么做呢,因?yàn)镺nclickListener是接口,可以用動態(tài)代理生成,比繼承重寫接口方便一些??赡苣氵€會有疑問那我直接用一個(gè)View.OnclickListener繼承類來set進(jìn)行不就行了,我要說的是這個(gè)測試用例只是為了方便描述,這是這個(gè)接口有set ,對外是開放的,你可以按你想的那樣弄,假如它是不對外開放的呢,hook是不是就有意義了,好了,開始做吧。
首先我們先看一下view.setOnClickListener方法里面的情況。下圖中我們要hook的對象被getListenerInfo()所持有,
那就看看getListenerInfo()返回的對象是
是view的一個(gè)屬性mListenerInfo
ListenerInfo是View的一個(gè)靜態(tài)內(nèi)部類,而它的屬性mOnClickListener正是我們要找的hook對象了。
那思路就是利用反射來拿到mListenerInfo對象,拿到它的屬性mOnClickListener并替換成 我們動態(tài)代理生成的接口。需要注意的是args,kotlin是需要加的。查了查好像說是數(shù)組展開,沒理解,知道的可以評論區(qū)評論。文章來源:http://www.zghlxwxcb.cn/news/detail-647562.html
然后我們回過頭來看反射機(jī)制是 基礎(chǔ),動態(tài)代理里面也是用反射來實(shí)現(xiàn)的,動態(tài)代理對類里面的所有方法統(tǒng)一動態(tài)加入操作代碼,java使用更多是對接口的,是生成了你想往里面塞的代碼,hook把hook對象勾出來,換成自己想塞進(jìn)去的,是一種手法。文章來源地址http://www.zghlxwxcb.cn/news/detail-647562.html
到了這里,關(guān)于Java反射機(jī)制,動態(tài)代理,hook以及在Retrofit源碼中的應(yīng)用的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!