1.反射基本概念
因为反射其实就是java的一个机制,所以说android的反射其实是对它的一个应用。
在运行状态中,
对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法和属性;
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制,可以参考这里对反射的解释。
2.反射调用的基本方法
getDeclaredField是可以获取一个类的所有字段.
getField只能获取类的public 字段.
1. public Field getDeclaredField(String name)
2. throws NoSuchFieldException, SecurityException {
3. checkMemberAccess(Member.DECLARED, ClassLoader.getCallerClassLoader());
4. Field field = searchFields(privateGetDeclaredFields(false), name);
5. if (field == null) {
6. throw new NoSuchFieldException(name);
7. }
8. return field;
9. }
10.
11.
12. private Field getField0(String name) throws NoSuchFieldException {
13. Field res = null;
14. // Search declared public fields
15. if ((res = searchFields(privateGetDeclaredFields(true), name)) != null) {
16. return res;
17. }
18. ......
getField其实调用的是getField0.
他们最后都调用了searchFields.
不过getDeclaredField传入的是privateGetDeclaredFields(false),
getField传入的是privateGetDeclaredFields(true),
1. private Field[] privateGetDeclaredFields(boolean publicOnly) {
2. checkInitted();
3. Field[] res = null;
4. if (useCaches) {
5. clearCachesOnClassRedefinition();
6. if (publicOnly) {
7. if (declaredPublicFields != null) {
8. res = (Field[]) declaredPublicFields.get();
9. }
10. } else {
11. if (declaredFields != null) {
12. res = (Field[]) declaredFields.get();
13. }
14. }
可以看到传入条件为true的时候使用的是declaredPublicFields,意思是public字段
为false的时候使用的是declaredFields.
例如修改Android原生SDK中的MediaController可以通过以下方法:
mView = LayoutInflater.from(getContext()).inflate(
R.layout.my_control, null);
try {
Field mRoot = android.widget.MediaController.class
.getDeclaredField("mRoot");
mRoot.setAccessible(true);
ViewGroup mRootVg = (ViewGroup) mRoot.get(this);
mRootVg.removeAllViews();
initControllerView();
mRootVg.addView(mView);
} catch (Exception e) {
e.printStackTrace();
}
}
private void initControllerView() throws Exception{
Field mPauseButtonField = android.widget.MediaController.class.getDeclaredField("mPauseButton");
if (mPauseButtonField != null) {
mPauseButtonField.setAccessible(true);
mPauseButtonField.set(this,mView.findViewById(R.id.pause));
}
Field mListenerField = android.widget.MediaController.class.getDeclaredField("mPauseListener");
if (mPauseButtonField != null && mListenerField != null) {
mListenerField.setAccessible(true);
((ImageButton)mPauseButtonField.get(this)).setOnClickListener(( View.OnClickListener)mListenerField.get(this));
}
Field mProgressFiled = android.widget.MediaController.class.getDeclaredField("mProgress");{
if (mProgressFiled != null) {
mProgressFiled.setAccessible(true);
mProgressFiled.set(this,mView.findViewById(R.id.mediacontroller_progress));
}
}
Field mSeekListenerField = android.widget.MediaController.class.getDeclaredField("mSeekListener");
if (mSeekListenerField != null && mProgressFiled != null) {
mSeekListenerField.setAccessible(true);
ProgressBar mProgress = (ProgressBar)mProgressFiled.get(this);
if (mProgress instanceof SeekBar) {
SeekBar seeker = (SeekBar) mProgress;
seeker.setOnSeekBarChangeListener((SeekBar.OnSeekBarChangeListener)mSeekListenerField.get(this));
}
mProgress.setMax(100);
}
Field mEndTimeFiled = android.widget.MediaController.class.getDeclaredField("mEndTime");
if (mEndTimeFiled != null) {
mEndTimeFiled.setAccessible(true);
mEndTimeFiled.set(this,mView.findViewById(R.id.time_end));
}
Field mCurrentTimeFiled = android.widget.MediaController.class.getDeclaredField("mCurrentTime");
if (mCurrentTimeFiled != null) {
mCurrentTimeFiled.setAccessible(true);
mCurrentTimeFiled.set(this,mView.findViewById(R.id.time_current));
}
}
由此可以看出,如果当我们要修改某些代码的时候但是又没有源码,可以用到反射机制,但是反射的运用不简简单单就是这样。
3.反射的高级运用
当我们有时候需要反射劫持到某个方法的时候,因为我们想修改该方法,但是仅仅是添加某些东西,而又不想修改它的基本逻辑,这时候可以用到劫持机制。
劫持,什么叫劫持呢,其实就是替换字段,有人说你直接反射拿到变量,直接反射替换就是了,这是一种方式,但是这并不是我想要的,比如我要劫持Student里的behavior变量,但是我不想修改它,我想保留它原来的逻辑,但是我又想加入新的东西,这个就有点类型面向切面编程了,比如日志的注入。怎么做?显然是动态代理啊。使用代理类去完成劫持操作,既可以保留原有操作,又可以增加新的逻辑。
可以通过这里下载MediaController的例子来查看反射的相关应用。