一、什么是反射
反射機制:允許運行中的Java程序對自身進行檢查,并能直接操作程序的內部屬性或方法。允許程序在正在執行的過程中,利用Reflection?APIs取得任何已知名稱的類的內部信息,包括:package、?type?parameters、?superclass、?implemented?interfaces、?inner?classes、?outer?classes、?fields、?constructors、?methods、?modifiers等,并可以在執行的過程中,動態生成Instances、變更fields內容或喚起methods。
簡單來說,我們可以利用反射機制在Java程序中,動態的去調用一些protected甚至是private的方法或類。
二、為什么要用反射
1、Android?SDK的源碼中,很多類或方法中經常加上了“@hide”注釋標記,它的作用是使這個方法或類在生成SDK時不可見,那么我們的程序可能無法編譯通過,而且在最終發布的時候,就可能存在一些問題,利用反射,就可以獲取到該方法或類,從而使編譯通過并可正常使用該方法或類。
? ? ? ?2、Android的項目可以分為不同的module,例如App中:
? ? ? ? ? ? 每個module只能調用自己內部的方法,若在Amodule中想調用Bmodule里的方法,則可以采用:
? ? ? ? ? ? ?a. 在Amodule里的build.gradle中的dependencies節點內,添加compile project(':debugTool')內容,其中“debugTool”為Bmodule的模塊名。
? ? ? ? ? ? ?b. 或在Amodule中,利用反射獲取Bmodule里的類、方法及變量。
? ? ? ? 3. 舉例說明
? ? ? ? ? ?在上圖中,提bug的SDK位于debugTool下,而調用SDK的方法,在common中,即SDK所在位置和調用SDK方法的位置,不在同一個module中,所以無法直接調用SDK內容。
? ? ? ? ? ?考慮到:i. 不想入侵原有的代碼過多
? ? ? ? ? ? ? ? ? ? ? ? ?ii. 也不想改變原有代碼的結構
? ? ? ? ? ? ? ? ? ? ? ? ?iii. 需要調用SDK的內容也不是很多
? ? ? ? ? ? ? ? ? ? ? ? ?iv. 我對SDK內部很熟悉,對將來的改動也能了如指掌。
? ? ? ? ? ?所以選擇上述2中的b方法,采用反射方式調用SDK中的內容。
三、怎么實現反射
3.1、首先,此處給出反射相關的API文檔鏈接:java.lang.reflect
? ? ? ?3.2、接下來,舉例說幾個常用的方法:
? ? ? ? ? ? ?1、 找到指定的類(包括class、interface、service等)
? ? ? ? ? ? ? ? ? ?用法:Class.forName("包名.類名")。例如:
final Class c = Class.forName("xxx.createjirabug.bugEntrance.FxService");、
2、獲取指定類中的指定方法
? ? ? ? ? ? ? ? ? ?用法:c.getMethod("方法名","參數類型")。例如:
final?Class c = Class.forName("xxx.createjirabug.bugEntrance.FxService");
? ? ? ? ? ? ? ? ? ? ? ? ? ? ??Method setListener = c.getMethod("setListener",?View.OnClickListener.class);
3、 執行指定的方法
用法:method.invoke(receiver,?"具體參數")。例如:
Method setListener = c.getMethod("setListener", View.OnClickListener.class);
setListener.invoke(o, new View.OnClickListener() {
@Override
public void onClick(View v) {
});
? ? ? ? ? ? ?4、 判斷classA是否為classB的子類
? ? ? ? ? ? ? ? ? ?用法:boolean is =?classA.isInstance(classB)。例如:
Class rnBase = Class.forName("xxx.reactnative.RnBaseActivity");
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? xxxBaseFragmentActivity currentxxxBaseActivity = xxxActivityStack.getInstance().getCurrentxxxBaseActivity();
? ? ? ? ? ? ? ? ? ? ? ? ? ? ??if(rnBase.isInstance(currentxxxBaseActivity)){
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ?5、 獲取指定的變量
? ? ? ? ? ? ? ? ? ?用法:class.getField("變量名")。例如:
Class buildConfig = Class.forName("xxx.BuildConfig");//先獲取buildConfig類
Field tField =buildConfig.getField("IS_CONTAIN_BONREE");//再從類中獲取IS_CONTAIN_BONREE變量
tField.setAccessible(true);//buildConfig類中的IS_CONTAIN_BONREE成員變量為private,故必須進行此操作,忽略訪問權限。
booleanb =?tField.getBoolean(null);//獲取tField的布爾值(我已知該變量為布爾類型)
?6、以上是反射中常用的方法,若需要用到的方法不在以上提供的內容中,請查閱3.1給出的api文檔,查閱具體資料。
四、為什么反射不起作用
? ? ? ?將一大串的反射寫完后,對于新手來說,經常遇到寫完的反射不起作用的情況,查閱log又沒有發現crash內容,此時該怎么辦呢?
? ? ? ?請按照以下步驟查看具體出錯原因:
? ? ? ?1、所有的反射代碼都會被要求寫入try...cach中:
? ? ? ? ? ? ? ? ?若未找到class,會拋出ClassNotFoundException異常;
? ? ? ? ? ? ? ? ?若未找到method,會拋出NoSuchMethodException異常;
? ? ? ? ? ? ? ? ?若未找到field,會拋出NoSuchFieldException異常;
? ? ? ? ? ? ? ? ?若方法執行(invoke)出錯,會拋出InvocationTargetException異常;
若因訪問權限被禁止,會拋出IllegalAccessException異常;
? ? ? ? ? ? ? ? ?......
?請將cach住的所有異常打印出來,查閱具體原因:
? ? ? ? ? ? ? ? ?class未找到:是否class的訪問權限問題?是否class的包名或類名寫錯?是否有該類?
? ? ? ? ? ? ? ? ?method未找到:是否method的訪問權限問題?是否method的名稱寫錯?是否method的參數類型及參數個數有誤?是否reciever傳入有誤?是否有該方法?
? ? ? ? ? ? ? ? ?field未找到:是否field的訪問權限問題?是否field的名稱寫錯?是否field的類型有誤?是否有該變量?
? ? ? ? ? ? ? ? ?......
? ? ? ? ? ?若以上方法嘗試過后,發現都沒有異常情況,請查看步驟2。
? ? ? ?2、當前項目是否涉及到混淆?打包的時候,代碼是否被混淆?SDK中需要被反射的類、方法、變量是否被混淆了?
如果是,請在Bmodule的proguard-project.txt文件中,添加SDK中相關的包,進行避混處理。