在android開發中經常會用到反射的知識,舉幾個反射的例子分析反射。
在android中有些源碼中的類是hide的or私有的,我們不能夠直接得到具體的對象,但是這些類在我們應用程序運行之後他的對象確實是被創建並且存在於內存中的,只是是私有的,我們直接拿不到,所以就需要通過反射去得到這個對象,但是我們通過反射得到的對象並不是我們運行程序中代碼使用的對象,反射得到的是新的對象,所以我們就需要把這個新的對象和我們運行程序相關聯進而得到我們運行程序中的對象。
1. 反射修改TabLayout的下劃線線寬
/**
* Set tabLayout indicator width by reflection.
*
* @param tabLayout tabLayout.
*/
public static void setTabLayoutIndicator(final TabLayout tabLayout) {
// The width of the line is set according to the width of the tabView.
tabLayout.post(new Runnable() {
@Override
public void run() {
try {
Field field = tabLayout.getClass().getDeclaredField("mTabStrip");
field.setAccessible(true);
//Get the attributes of tabLayout mTabStrip.
for (int i = 0, count = tabStrip.getChildCount(); i < count; i++) {
View tabView = tabStrip.getChildAt(i);
//Get the attributes of tabView mTextView.
Field textViewField = tabView.getClass().getDeclaredField("mTextView");
textViewField.setAccessible(true);
TextView textView = (TextView) textViewField.get(tabView);
tabView.setPadding(0, 0, 0, 0);
// Measure the width of indicator according to text view.
int textWidth = 0;
textWidth = textView.getWidth();
if (textWidth == 0) {
textView.measure(0, 0);
textWidth = textView.getMeasuredWidth();
}
int tabWidth = 0;
tabWidth = tabView.getWidth();
if (tabWidth == 0) {
tabView.measure(0, 0);
tabWidth = tabView.getMeasuredWidth();
}
// Set tabView layoutParams.
LinearLayout.LayoutParams tabViewParams = (LinearLayout.LayoutParams) tabView
.getLayoutParams();
int margin = (tabWidth - textWidth) / 2;
tabViewParams.leftMargin = margin;
tabViewParams.rightMargin = margin;
tabViewParams.topMargin = INDICATOR_MARGIN_TOP;
tabView.setLayoutParams(tabViewParams);
}
} catch (NoSuchFieldException exception) {
Log.e(TAG, "setIndicatorWidth: get tabStrip failed.");
} catch (IllegalAccessException exception) {
Log.e(TAG, "setIndicatorWidth: get LinearLayout failed.");
}
}
});
}
通過對tabLayout反射拿到他的一個屬性tabStrip對象,然後通過 LinearLayout tabStrip = (LinearLayout) field.get(tabLayout); 進行關聯,得到的就行我們運行程序中TabLayout中的tabStrip對象,就是運行程序中當前使用的對象。
2.反射控制status bar的顯示隱藏
public static void setStatusBarState(Context context, int disableFlag) {
Object service = context.getSystemService("statusbar");
try {
Class<?> statusbarManager = Class.forName("android.app.StatusBarManager");
Method disable = statusbarManager.getMethod(disable, int.class);
disable.invoke(service, disableFlag);
} catch (IllegalAccessException exception) {
Log.e(TAG, "IllegalAccessException: ", exception);
} catch (InvocationTargetException exception) {
Log.e(TAG, "InvocationTargetException: ", exception);
} catch (ClassNotFoundException exception) {
Log.e(TAG, "ClassNotFoundException: ", exception);
} catch (NoSuchMethodException exception) {
Log.e(TAG, "NoSuchMethodException: ", exception);
}
}
StatusBarManager我們正常是訪問不到這個類的,他是hide的。所以我們通過得到object對象接收,最後通過disable.invoke(service, disableFlag);進行和我們context中的對象關聯。
3.動態代理替換
public void replaceActivitytat(Activity activity){
//得到 Activity 的 minstrumentation 字段
Field field = Activity.class.getDeclaredField("minstrumentation");
//取消 Java 的權限控制檢查
field.setAccessible(true) ;
Instrumentation instrumentation= (Instrumentation) field.get(activity);
Instrumentation instrumentationProxy = new InstrumentationProxy(instrumentation);
field.set(activity , instrumentationProxy);
}
field.get(activity)通過得到當前Activity中的 minstrumentation 實例對象,傳遞給代理類。最後用instrumentationProxy把minstrumentation實例替換掉。在程序運行的時候運行的就是instrumentationProxy。
總結:
- 直接反射的來的對象並不是我們當前運行內存中使用的對象,他是一個新對象。
- 跟我們當前能處理的對象,與反射得到的新對象進行關聯。
關聯的方式 1.如果是屬性 field.set(object,value) 和 field.get(object)
2.如果是方法 medthod.invoke(object,value); - 關聯之後的對象就是我們當前內存中使用的對象