Android反射介紹

一、什么是反射

反射機制:允許運行中的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中相關的包,進行避混處理。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容