前一篇文章介紹了反射功能的基本介紹。
今天寫這篇文章是爲了依據筆者項目的經驗,講講反射在Android的三個具體用途。歡迎大家一起補充討論。
- 獲取系統編譯後隱藏的方法,比如源碼中使用
/*hide*/
修飾的函數。 - 確認方法是否存在。這些方法有可能是自己自定義並添加的。筆者將以Android中的
createPackageContext()
(獲取另外一個App的資源和方法)爲例講解。 - 兼容不用的Android版本,在Android系統的不斷迭代中,有很多方法都是不斷消失,不斷出現的。需要我們對其做兼容處理。其實這也是第一種情況的引申。
第一種情況舉例:
在Android5.0中,有個控制手機網絡狀態的類TelephonyMananger。它的setDataEnabled和getDataEnabled是隱藏的,但可以通過反射進行調用。
public void setMobileDataState(Context cxt, boolean mobileDataEnabled) {
TelephonyManager telephonyService = (TelephonyManager) cxt.getSystemService(Context.TELEPHONY_SERVICE);
try {
Method setMobileDataEnabledMethod = telephonyService.getClass().getDeclaredMethod("setDataEnabled", boolean.class);
if (null != setMobileDataEnabledMethod)
{
setMobileDataEnabledMethod.invoke(telephonyService, mobileDataEnabled);
}
}
catch (Exception e) {
LogHelper.v(TAG, "Error setting" + ((InvocationTargetException)e).getTargetException() + telephonyService);
}
}
public boolean getMobileDataState(Context cxt) {
TelephonyManager telephonyService = (TelephonyManager) cxt.getSystemService(Context.TELEPHONY_SERVICE);
try {
Method getMobileDataEnabledMethod = telephonyService.getClass().getDeclaredMethod("getDataEnabled");
if (null != getMobileDataEnabledMethod)
{
boolean mobileDataEnabled = (Boolean) getMobileDataEnabledMethod.invoke(telephonyService);
return mobileDataEnabled;
}
}
catch (Exception e) {
LogHelper.v(TAG, "Error getting" + ((InvocationTargetException)e).getTargetException() + telephonyService);
}
return false;
}
不過雖然可以通過反射調用執行,但這個TelephonyMananger的這兩個方法執行後是有權限問題的,感興趣的朋友可以看看我的另一篇文章。
第二種情況舉例:
目標App中的Activity中聲明getStr方法,返回一個字符串。確認安裝。
public static String getStr(){
return "String from TargetApp";
}
主App中通過反射獲取此方法,代碼如下:
try {
mContext = createPackageContext("com.baidu.www.targetapp", Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
Class mClass = mContext.getClassLoader().loadClass("com.baidu.www.targetapp.MainActivity");
Object target = mClass.newInstance();
Method method = mClass.getMethod("getStr");
Object obj = method.invoke(target);
} catch (Exception e) {
e.printStackTrace();
}
如此,便可以方便的獲取目標App的方法了。如果在代碼中我們直接使用Activity實例,肯定是不能直接調用此方法的。如果對createPackageContext不熟悉的朋友,可以看下這篇文章。
第三種情況舉例:
在SharedPreferences.Editor中有個方法:
public abstract void apply ();
public abstract boolean commit ();
其中apply方法是API9的時候擁有的,而commit()方法是API1擁有的。對此可以做如下處理:
public class ShareActionClass {
//反射
public static void setDataR(Context cxt, String str){
SharedPreferences sharedPreferences = cxt.getSharedPreferences("Test", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("strData", str);
Method mMethod = null;
if (Build.VERSION.SDK_INT > 9) {
try {
mMethod = editor.getClass().getDeclaredMethod("apply");
try {
mMethod.invoke(editor);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}else{
try {
mMethod = editor.getClass().getDeclaredMethod("commit");
try {
mMethod.invoke(editor);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
public static String getDataR(Context cxt){
SharedPreferences sharedPreferences = cxt.getSharedPreferences("Test", Context.MODE_PRIVATE);
Method method = null;
try {
method = sharedPreferences.getClass().getMethod("getString", new Class[]{String.class, String.class});
try {
Object obj = method.invoke(sharedPreferences, "strData", "");
return (String)obj;
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
}
在Editor的提交的過程中,我們依據API版本,使用了不同的方法。
以上,歡迎大家留言補充。