这篇文章是说什么的?
主要就是写了一个让反射更易用的工具类,不想看废话的可以直接拿这里的源码:
https://github.com/aesean/ActivityStack/blob/master/app/src/main/java/com/aesean/activitystack/utils/ReflectUtils.java
最终你可以像下面这样,只写一行代码即可反射到目标。
Application application = (Application)ReflectUtils.reflect(null,
"android.app.ActivityThread#currentActivityThread()");
ArrayMap map = (ArrayMap)ReflectUtils.reflect(application,
"mLoadedApk.mActivityThread.mActivities");
什么是反射?
反射简单说可以说成是用非常规方法调用和方法Java的方法和属性,而且可以突破很多Java语法对访问可见性的限制。反射其实已经算是比较基础的知识了,但说清楚还是很复杂的,这里不解释太多了。
Java的private等关键字的访问限制可以理解成是Java为了面向对象做的“君子协定”。所有属性和方法依然是保存在你自己的进程空间的堆栈区,你是完全具有访问权限的,只是Java语法限制你直接去访问。如果你非要访问一个private字段或者方法其实也是可以的,就是利用反射。通常来说,尽量少用反射。但是对于很多特殊需求,还是非常有必要用反射的。例如Android中的Activity创建,Activity是被谁创建的?是被AndroidSDK。那SDK本身是静态代码,不会随着你的业务代码不同而编译出不同的字节码。还记得你是如何打开一个Activity的吗?
Intent intent=new Intent();
intent.setClassName(packageName,"com.aesean.MainActivity");
startActivity(intent);
注意到了吗?整个过程你并不需要自己手动new 一个Activity实例。那最后Activity实例是怎么出现呢?就是反射。通过传入的"com.aesean.MainActivity"字符串,找到Activity的类类型Class,然后通过newInstance创建Activity实例。
public class Instrumentation {
......
public Activity newActivity(ClassLoader cl, String className,
Intent intent)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
return (Activity)cl.loadClass(className).newInstance();
}
......
}
不熟悉反射怎么轻松用反射的功能
这个才是本文重点。首先有下面一个类,所有属性方法全部private,当然缺省有一个public的构造方法。
public class SampleDemo {
private String name;
private String getName() {
return name;
}
private void setName(String name) {
this.name = name;
}
}
那我们要拿name属性的值,或者执行getName和setName方法怎么办?就是用反射。
public class SampleMain {
public static void main(String[] args) {
SampleDemo demo = new SampleDemo();
Class extends SampleDemo> clazz = demo.getClass();
try {
Field demoField = clazz.getDeclaredField("demo");
demoField.setAccessible(true);
Object name = demoField.get(clazz);
System.out.println(name);
} catch (Exception e) {
e.printStackTrace();
}
}
}
熟悉的反射的可能会写出类似上面的代码,最终输出SampleDemo的name。上面一大坨好复杂,我也觉得复杂,如果反射一些层次比较多的类代码会非常长,而且容易出错。那怎么破?
一行代码轻松用反射
public class SampleMain {
public static void main(String[] args) {
SampleDemo demo = new SampleDemo();
try {
Object name = ReflectUtils.reflect(demo, "name");
System.out.println(name);
} catch (ReflectUtils.ReflectException e) {
e.printStackTrace();
}
}
}
用了ReflectUtils这个工具类后反射就变成了上面的样子。而且不光如此,还支持像写脚本一样进行多层级反射。我们把SampleDemo类改成下面这样子。
public class SampleDemo {
private String name;
private String getName() {
return name;
}
private void setName(String name) {
this.name = name;
}
private SampleDemo createSampleDemo(String name) {
SampleDemo sampleDemo = new SampleDemo();
sampleDemo.name = name;
return sampleDemo;
}
}
然后main方法改成这样
public class SampleMain {
public static void main(String[] args) {
SampleDemo demo = new SampleDemo();
try {
Object name = ReflectUtils.reflect(demo, "createSampleDemo(%0).name", new Object[]{"newName"});
System.out.println(name);
} catch (ReflectUtils.ReflectException e) {
e.printStackTrace();
}
}
}
上面写法相当于调用了demo的createSample方法,这个方法有参数,所以用%0来代替参数,实际参数的值为后面Object[]数组的index=0的变量,这里的0就是用来指定参数的。当然有多个参数就,%1,%2,%3逗号分隔继续写就可以了。