反射可以说是在android源码中的影子无处不在,为了更好地理解android源码,所以该篇整理出反射中经常看到的用法,反射的代码是在运行时期运行,因此在编译器不会提示代码的错误性,只有在运行期才能检索代码,下面通过几个简单的例子来看看如何使用:
调用类的构造方法
写了一个简单的测试类:
package com.single.layoutinflaterdemo;
public class Reflection {
private String params1;
private int params2;
private long params3;
public Reflection() {
System.out.println("调用了Reflection类的无参数构造器");
}
public Reflection(String params1) {
this.params1 = params1;
System.out.println("调用了Reflection类的params1参数构造器====>params1:" + params1);
}
public Reflection(int params2) {
this.params2 = params2;
System.out.println("调用了Reflection类的params2参数构造器====>params2:" + params2);
}
public Reflection(long params3) {
this.params3 = params3;
System.out.println("调用了Reflection类的params3参数构造器====>params3:" + params3);
}
}
事例代码如下:
public static void main(String[] args) {
try {
Class clz = Class.forName("com.single.layoutinflaterdemo.Reflection");
Constructor appleConstructor1 = clz.getConstructor();
Object appleObj1 = appleConstructor1.newInstance();
Constructor appleConstructor2 = clz.getConstructor(String.class);
Object appleObj2 = appleConstructor2.newInstance("123");
Constructor appleConstructor3 = clz.getConstructor(int.class);
Object appleObj3 = appleConstructor3.newInstance(11);
Constructor appleConstructor4 = clz.getConstructor(long.class);
Object appleObj4 = appleConstructor4.newInstance(11l);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}

Class clz = Class.forName("com.single.layoutinflaterdemo.Reflection");
Constructor appleConstructor1 = clz.getConstructor(float.class);
Object appleObj1 = appleConstructor1.newInstance(12.23);

Reflection类中,没有定义float类型的构造方法,因此会抛了一个NoSuchMethodException异常。下面再看一种构造器调用的方式,这个得追索到上篇LayoutInflater背后隐藏的一些东西说事,直接到调用view的构造器的代码出:


asSubclass的作用,修改如下:


Reflection类:

ClassCastException,能正常执行代码,因此可以看出asSubclass的作用,限制了类的类型,减小了类的范围。
再来看个例子,这里是初始化CoordinatorLayout.Behavior

classLoader。android中经常用到的activity也是通过该方式创建成功的,具体可以看activity启动流程。
调用类的方法
这里简单在类里面定义了几个方法:
public int handleMethod(int a, String b) throws NumberFormatException {
System.out.println("调用了两个参数的handleMethod方法");
int intB = Integer.parseInt(b);
return a + intB;
}
public void handleMethod() {
System.out.println("调用了无参的handleMethod方法");
}
public int handleMethod(int a) {
System.out.println("调用了一个参数的handleMethod方法");
return a;
}
public void handleMethod(String params1) {
this.params1 = params1;
System.out.println("调用了一个参数的handleMethod方法====>params1:" + params1);
}
private void privateMethod() {
System.out.println("调用了私有的privateMethod方法");
}
protected void protectedMethod() {
System.out.println("调用了私有的protectedMethod方法");
}
调用无参数的方法
Class clz = Class.forName("com.single.layoutinflaterdemo.Reflection");
clz.asSubclass(Common.class);
Constructor appleConstructor1 = clz.getConstructor(int.class);
Object target = appleConstructor1.newInstance(123);
//指定方法名
Method handleMethod = clz.getMethod("handleMethod");
//通过invoke方法执行handleMethod
handleMethod.invoke(target);

调用有参的方法
Class clz = Class.forName("com.single.layoutinflaterdemo.Reflection");
clz.asSubclass(Common.class);
Constructor appleConstructor1 = clz.getConstructor(int.class);
Object target = appleConstructor1.newInstance(123);
//指定要调用方法的参数类型
Class<?>[] params = new Class[]{int.class, String.class};
Method handleMethod = clz.getMethod("handleMethod", params);
//args传入我们的参数值
Object invoke = handleMethod.invoke(target, 10, "20");
System.out.println("返回值:" + invoke);



调用private或protected方法
Class clz = Class.forName("com.single.layoutinflaterdemo.Reflection");
clz.asSubclass(Common.class);
Constructor appleConstructor1 = clz.getConstructor(int.class);
appleConstructor1.setAccessible(true);
Object target = appleConstructor1.newInstance(123);
//指定要调用方法的参数类型
Class<?>[] params = new Class[]{int.class, String.class};
//通过getDeclaredMethod方法获取
Method handleMethod = clz.getDeclaredMethod("privateMethod");
//并且设置可访问的权限
handleMethod.setAccessible(true);
//args传入我们的参数值
Object invoke = handleMethod.invoke(target);

访问类的变量
//调用指定的参数名
Field params1 = clz.getDeclaredField("params1");
params1.setAccessible(true);
//获取参数的值
Object name = params1.get(target);
由于一般类的变量都是private,所以只列举private情况,也是通过getDeclaredField(参数名)、field.setAccessible(true)一起完成,最后通过field.get(target)来获取参数值 访问所有的变量
//获取所有的参数
Field[] allParams = clz.getDeclaredFields();
for (int i = 0; i < allParams.length; i++) {
Field param = allParams[i];
param.setAccessible(true);
String name = param.getName();
Object value = param.get(target);
System.out.println("name:" + name + ";value:" + value);
}

继承关系,调用父类的方法或变量
//写了个父类,用来测试的
public class Common {
private String baseParam1 = "123";
protected int baseParam2 = 11;
public int baseParam3 = 123;
public void baseHandleMethod() {
System.out.println("调用了父类的baseHandleMethod方法");
}
protected void baseHandleMethod(String baseParam1) {
this.baseParam1 = baseParam1;
System.out.println("调用了父类protected类型的baseHandleMethod方法");
}
}
调用父类的public类型的方法
//调用父类的public类型的方法
Method baseHandleMethod = clz.getMethod("baseHandleMethod");
baseHandleMethod.invoke(target);
调用public类型是一切没有问题的,下面尝试下调用父类的protected或者private方法:
//调用父类的protect或private方法
//错误的写法
Method protectedBaseHandleMethod = clz.getDeclaredMethod("baseHandleMethod", String.class);
protectedBaseHandleMethod.setAccessible(true);
protectedBaseHandleMethod.invoke(target, "124");
可以看下运行结果:

//调用父类的protect或private方法
//正确的写法,通过getSuperclass获取到父类的class对象
Method protectedBaseHandleMethod = clz.getSuperclass().getDeclaredMethod("baseHandleMethod", String.class);
protectedBaseHandleMethod.setAccessible(true);
protectedBaseHandleMethod.invoke(target, "124");
可以看到如果想获取父类的protected或private方法必须先通过getSuperclass方法获取到父类的class对象。
获取父类的变量跟方法是一样的,大家自己尝试就行,这里就不给大家演示了。
是不是感觉反射拿到了一个类的全类名,就可以为所欲为呢,确实很强大,绕过了类的编译期,在运行的时候直接运行你的class字节码文件。熟悉反射,对android相关源码还是有很大帮助,所以大家还是要亲自写demo才能体会它的强大。