Android插件化开发指南
上QQ阅读APP看书,第一时间看更新

第3章 反射

本章介绍Java中最强大的技术:反射。

Java原生的反射语法艰涩难懂,于是我们一般将这些反射语法封装成Utils类,包括反射类、反射方法(构造函数)、反射字段。这其中,做得最好的莫过于jOOR这个开源反射封装库。但是它有个缺点,就是不适用于Android中定义为final的字段,就连作者也承认,jOOR只为了Java而设计,而没有考虑Android。

所有反射语法中最难的莫过于反射一个泛型类,而这又是我们在做插件化编程中不可避免的。

3.1 基本反射技术

反射包括以下技术:

□ 根据一个字符串得到一个类的对象。

□ 获取一个类的所有公用或私有、静态或实例的字段、方法、属性。

□ 对泛型类的反射。

相比于其他语言,Java反射的语法是非常艰涩难懂的,我们按照上面的三点依次介绍。

提示

本节的示例代码参见https://github.com/BaoBaoJianqiang/TestReflection

3.1.1 根据一个字符串得到一个类

1.getClass

通过一个对象,获取它的类型。类型用Class表示:

    String str = “abc”;
    Class c1 = str.getClass();

2.Class.forName

这个方法用得最多。

通过一个字符串获取一个类型。这个字符串由类的命名空间和类的名称组成。而通过getSuperclass方法,获取对象的父类类型:

    try {
        Class c2 = Class.forName("java.lang.String");
        Class c3 = Class.forName("android.widget.Button");

        //通过getSuperClass,每个Class都有这个函数
        Class c5 = c3.getSuperclass();  //得到TextView
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

3.class属性

每个类都有class属性,可以得到这个类的类型:

    Class c6 = String.class;
    Class c7 = java.lang.String.class;
    Class c8 = MainActivity.InnerClass.class;
    Class c9 = int.class;
    Class c10 = int[].class;

4.TYPE属性

基本类型,如BOOLEAN,都有TYPE属性,可以得到这个基本类型的类型:

    Class c11 = Boolean.TYPE;
    Class c12 = Byte.TYPE;
    Class c13 = Character.TYPE;
    Class c14 = Short.TYPE;
    Class c15 = Integer.TYPE;
    Class c16 = Long.TYPE;
    Class c17 = Float.TYPE;
    Class c18 = Double.TYPE;
    Class c19 = Void.TYPE;

看到这里,读者也许会问,为什么不厌其烦地想要得到类或对象的类型。在后面的章节,我们在使用Proxy.newProxyInstance()的时候,会大量用到这些类型值作为参数。

3.1.2 获取类的成员

1.获取类的构造函数

获取类的构造函数,包括private和public两种,也支持无参数和有参数这两种类型的构造函数。

比如TestClassCtor这个类,就有很多构造函数:

    public TestClassCtor() {
        name = "baobao";
    }

    public TestClassCtor(int a) {

    }

    public TestClassCtor(int a, String b) {
        name = b;
    }

    private TestClassCtor(int a, double c) {

    }

1)获取类的所有构造函数。

通过Class的getDeclaredConstructors方法,获取类的所有构造函数,包括public和private的构造函数,然后就可以通过for循环遍历每一个构造函数了:

    TestClass r = new TestClass();
        Class temp = r.getClass();
        String className = temp.getName();        // 获取指定类的类名

        Log.v("baobao", "获取类的所有ctor,不分public还是private---------------------");
        //获取类的所有ctor,不分public还是private
        try {
            Constructor[] theConstructors = temp.getDedaredConstructors();
                                              // 获取指定类的公有构造方法

            for (int i = 0; i < theConstructors.length; i++) {
                int mod = theConstructors[i].getModifiers();    // 输出修饰域和方法名称
                Log.v("baobao", Modifier.toString(mod) + " " + className + "(");

                Class[] parameterTypes = theConstructors[i].getParameterTypes();
                                              //获取指定构造方法参数的集合
                for (int j = 0; j < parameterTypes.length; j++) {
                                              // 输出打印参数列表
                    Log.v("baobao", parameterTypes[j].getName());
                    if (parameterTypes.length > j + 1) {
                        Log.v("baobao", ", ");
                    }
                }
                Log.v("baobao", ")");
            }
        } catch (Exception e) {
            e.printStackTrace();
            }

如果只想获取类的所有public构造函数,就不能再使用Class的getConstructors方法了,而要使用getDeclaredConstructors方法。

2)获取类的某个构造函数。

获取无参数的构造函数:

    Constructor c1 = temp.getDeclaredConstructor();

获取有一个参数的构造函数,参数类型int:

    Class[] p2 = {int.class};
    Constructor c2 = temp.getDeclaredConstructor(p2);

获取有两个参数的构造函数,参数类型依次是int和String:

    Class[] p3 = {int.class, String.class};
    Constructor c3 = temp.getDeclaredConstructor(p3);

反射到类的构造函数很重要,这是下述流程中至关重要的一步:通过字符串反射出一个类,然后通过反射获取到类的构造函数,执行构造函数就得到了类的实例。有了实例,就可以通过反射进一步得到实例的所有字段和方法。

3)调用构造函数。

接下来通过反射调用构造函数,得到类的实例,这要借助于Constructor的newInstance方法:

    Class r = Class.forName("jianqiang.com.testreflection.TestClassCtor");

    //含参
    Class[] p3 = {int.class, String.class};
    Constructor ctor = r.getDeclaredConstructor(p3);
    Object obj = ctor.newInstance(1, "bjq");

    //无参
    Constructor ctor2 = r.getDeclaredConstructor();
    Object obj2 = ctor2.newInstance();

如果构造函数是无参数的,那么可以直接使用Class的newInstance方法:

    Class r = Class.forName("jianqiang.com.testreflection.TestClassCtor");
    Object obj4 = r.newInstance();

2.获取类的私有实例方法并调用它

在TestClassCtor中,有一个私有方法doSOmething:

    private String doSOmething(String d) {
        Log.v("baobao", "TestClassCtor, doSOmething");

        return "abcd";
    }

想获取这个私有方法并执行它,要写如下代码:

    Class r = Class.forName("jianqiang.com.testreflection.TestClassCtor");
    Class[] p3 = {int.class, String.class};
    Constructor ctor = r.getDeclaredConstructor(p3);
    Object obj = ctor.newInstance(1, "bjq");

    //以下4句话,调用一个private方法
    Class[] p4 = {String.class};
    Method method = r.getDeclaredMethod("doSOmething", p4); //在指定类中获取指定的方法
    method.setAccessible(true);

    Object argList[] = {"jianqiang"};   //这里写死,下面有个通用的函数getMethodParamObject
    Object result = method.invoke(obj, argList);

3.获取类的静态的私有方法并调用它

在TestClassCtor中,有一个静态的私有方法work:

    private static void work() {
        Log.v("baobao", "TestClassCtor, work");
    }

想获取这个静态的私有方法并执行它,要写如下代码:

    Class r = Class.forName("jianqiang.com.testreflection.TestClassCtor");
    //以下3句话,调用一个private静态方法
    Method method = r.getDeclaredMethod("work"); //在指定类中获取指定的方法
    method.setAccessible(true);
    method.invoke(null);

4.获取类的私有实例字段并修改它

在TestClassCtor中,有一个私有的实例字段name:

    public class TestClassCtor {
        private String name;

        public String getName() {
            return name;
        }
    }

想获取这个私有实例字段并修改它的值,要写如下代码:

    //以下4句话,创建一个对象
    Class r = Class.forName("jianqiang.com.testreflection.TestClassCtor");
    Class[] p3 = {int.class, String.class};
    Constructor ctor = r.getDeclaredConstructor(p3);
    Object obj = ctor.newInstance(1, "bjq");

    //获取name字段,private
    Field field = r.getDeclaredField("name");
    field.setAccessible(true);
    Object fieldObject = field.get(obj);

    //只对obj有效
    field.set(obj, "jianqiang1982");

值得注意的是,这次修改仅对当前这个对象有效,如果接下来我们再次创建一个TestClassCtor对象,它的name字段的值为空而不是jianqiang1982:

    TestClassCtor testClassCtor = new TestClassCtor(100);
    testClassCtor.getName(); //仍然返回null,并没有修改

5.获取类的私有静态字段并修改它

在TestClassCtor中,有一个静态的私有字段address,想获取这个私有的静态字段并修改它的值,要写如下代码:

    //以下4句话,创建一个对象
    Class r = Class.forName("jianqiang.com.testreflection.TestClassCtor");

    //获取address静态字段,private
    Field field = r.getDeclaredField("address");
    field.setAccessible(true);

    Object fieldObject = field.get(null);

    field.set(fieldObject, "ABCD");

    //静态变量,一次修改,终生受用
    TestClassCtor.printAddress();

与前面介绍的实例字段不同,静态字段的值被修改了,下次再使用,这个字段的值是修改后的值。所谓“一次修改,终生受用”。

3.1.3 对泛型类的反射

Android系统源码中存在大量泛型,所以插件化技术离不开对泛型进行反射,比如单例模式(Singleton),下述代码是从Android源码中找出来的:

    public abstract class Singleton<T> {
        private T mInstance;

        protected abstract T create();

        public final T get() {
            synchronized (this) {
                if (mInstance == null) {
                    mInstance = create();
                }
                return mInstance;
            }
        }
    }

Singleton是一个泛型类,我们可以通过以下三行代码,取出Singleton中的mInstance字段:

    Class<? > singleton = Class.forName("jianqiang.com.testreflection.Singleton");
    Field mInstanceField = singleton.getDeclaredField("mInstance");
    mInstanceField.setAccessible(true);

同时,Singleton也是一个抽象类,在实例化Singleton的时候,一定要实现create这个抽象方法。

接下来我们看ActivityManagerNative(AMN)这个类,其中和Singleton有关的是下面几行代码:

    public class AMN {
        private  static  final  Singleton<ClassB2Interface>  gDefault  =  new  Singleton
            <ClassB2Interface>() {
            protected ClassB2Interface create() {
                ClassB2 b2 = new ClassB2();
                b2.id = 2;
                return b2;
            }
        };

        static public ClassB2Interface getDefault() {
            return gDefault.get();
        }
    }

上面的代码中gDefault是AMN的静态私有变量,它是Singleton类型的,所以要实现create方法,返回一个ClassB2类型的对象。

在Android的源码中,可通过AMN.getDefault()来获取create方法创建的ClassB2对象。

我们可以通过以下几行代码来获取AMN的gDefault静态私有字段,进一步得到生成的ClassB2类型对象rawB2Object:

    Class<? > activityManagerNativeClass = Class.forName("jianqiang.com.testreflection.
        AMN");
    Field gDefaultField = activityManagerNativeClass.getDeclaredField("gDefault");
    gDefaultField.setAccessible(true);
    Object gDefault = gDefaultField.get(null);
    // AMNgDefault对象里面原始的 B2对象
    Object rawB2Object = mInstanceField.get(gDefault);

后来我们可能发现,rawB2Object不是我们所需要的,我们希望把它换成ClassB2Mock类型的对象proxy。

ClassB2Mock是对rawB2Object的动态代理,这里使用了Proxy.newProxyInstance方法,额外打印了一行日志:

    // 创建一个这个对象的代理对象ClassB2Mock, 然后替换这个字段, 让我们的代理对象帮忙干活
    Class<? > classB2Interface = Class.forName("jianqiang.com.testreflection.ClassB2Interface");
    Object proxy = Proxy.newProxyInstance(
        Thread.currentThread().getContextClassLoader(),
        new Class<? >[] { classB2Interface },
        new ClassB2Mock(rawB2Object));
    mInstanceField.set(gDefault, proxy);

最后一行代码就是把AMN中的gDefault字段的mInstance字段,设置为代理对象proxy。

经过Hook, AMN.getDefault().doSomething()将执行ClassB2Mock里面的逻辑。

Android源码中AMN的思路和我的这些代码在思路上是一致的,只是这里用ClassB2和ClassB2Mock来模拟,比较简单。

3.2 jOOR

上面例子的语法都是基于原始的Java语法,有没有觉得这些语法很艰涩?我们希望用一种自然的、简单的、面向对象的语法,来取代上面这些艰涩的语言,于是便有了jOOR这个开源库。开源地址:https://github.com/jOOQ/jOOR

jOOR库就两个类,Reflect.java和ReflectException.java,所以我一般不依赖于gradle,而是直接把这两个类拖到项目中来。

其中,Reflect.java最为重要,包括6个核心方法:

□ on:包裹一个类或者对象,表示在这个类或对象上进行反射,类的值可以是Class,也可以是完整的类名(包含包名信息)。

□ create:用来调用之前的类的构造方法,有两种重载,一种有参数,一种无参数。

□ call:方法调用,传入方法名和参数,如有返回值还需要调用get。

□ get:获取(field和method返回)值相关,会进行类型转换,常与call组合使用。

□ set:设置属性值。

我们使用jOOR把上一节的代码重构一下。

提示

本节的示例代码参见https://github.com/BaoBaoJianqiang/TestReflection2

3.2.1 根据一个字符串得到一个类

1.getClass

通过一个字符串,获取它的类型。类型用Class表示:

    String str = "abc";
    Class c1 = str.getClass();

这些代码还是传统的语法,并没有改变。

2.根据字符串获取一个类

对于jOOR,我们一般import它的Reflect.on方法,这样我们就可以直接在代码中使用on了,让编码更加简单。代码如下:

    import static jianqiang.com.testreflection.joor.Reflect.on;

        //以下3个语法等效
        Reflect r1 = on(Object.class);
        Reflect r2 = on("java.lang.Object");
        Reflect r3 = on("java.lang.Object", ClassLoader.getSystemClassLoader());

        //以下2个语法等效,实例化一个Object变量,得到Object.class
        Object o1 = on(Object.class).<Object>get();
        Object o2 = on("java.lang.Object").get();

        String j2 = on((Object)"abc").get();
        int j3 = on(1).get();

        //等价于Class.forName()
        try {
            Class j4 = on("android.widget.Button").type();
        }
        catch (ReflectException e) {
            e.printStackTrace();
        }

3.2.2 获取类的成员

1.调用类的构造函数

调用类的构造函数,包括private和public两种,也支持无参数和有参数这两种类型的构造函数。

jOOR认为构造函数就是用来调用的,所以没有给出获取构造函数的方法,而是直接给出了调用构造函数的方法create:

    TestClassCtor r = new TestClassCtor();
    Class temp = r.getClass();
    String className = temp.getName();        // 获取指定类的类名

    //public构造函数
    Object obj = on(temp).create().get();    //无参
    Object obj2 = on(temp).create(1, "abc").get();  //有参

    //private构造函数
    TestClassCtor obj3 = on(TestClassCtor.class).create(1, 1.1).get();
    String a = obj3.getName();

2.获取类的私有实例方法

获取类的私有实例方法并调用它:

    //以下4句话,创建一个对象
        TestClassCtor r = new TestClassCtor();
        Class temp = r.getClass();
        Reflect reflect = on(temp).create();

        //调用一个实例方法
        String a1 = reflect.call("doSOmething", "param1").get();

3.获取类的私有静态方法

获取类的私有静态方法并调用它:

    //以下4句话,创建一个对象
    TestClassCtor r = new TestClassCtor();
    Class temp = r.getClass();
    Reflect reflect = on(temp).create();

    //调用一个静态方法
    on(TestClassCtor.class).call("work").get();

4.获取类的私有实例字段

获取类的私有实例字段并修改它:

    Reflect obj = on("jianqiang.com.testreflection.TestClassCtor").create(1, 1.1);
    obj.set("name", "jianqiang");
    Object obj1 = obj.get("name");

5.获取类的私有静态字段

获取类的私有静态字段并修改它:

    on("jianqiang.com.testreflection.TestClassCtor").set("address", "avcccc");
    Object obj2 = on("jianqiang.com.testreflection.TestClassCtor").get("address");

3.2.3 对泛型类的反射

这个例子用jOOR写起来会更简单:

    //获取AMNgDefault单例gDefault, gDefault是静态的
    Object gDefault = on("jianqiang.com.testreflection.AMN").get("gDefault");

    // gDefault是一个 android.util.Singleton对象我们取出这个单例里面的mInstance字段
    // mInstance就是原始的ClassB2Interface对象
    Object mInstance = on(gDefault).get("mInstance");

    // 创建一个这个对象的代理对象ClassB2Mock, 然后替换这个字段, 让我们的代理对象帮忙干活
    Class<? > classB2Interface = on("jianqiang.com.testreflection.ClassB2Interface").type();
    Object proxy = Proxy.newProxyInstance(
        Thread.currentThread().getContextClassLoader(),
        new Class<? >[] { classB2Interface },
        new ClassB2Mock(mInstance));

    on(gDefault).set("mInstance", proxy);

外界对jOOR的评价很高,我看了相关的文章,大多是一些浮于表面的介绍,然后就被媒体无限放大,殊不知jOOR在Android领域有个很大的缺陷,那就是不支持反射final类型的字段。

看一个例子,User类有两个final字段,其中userId是静态字段,name是实例字段:

    public class User {
        private final static int userId = 3;
        private final String name = "baobao";
    }

在使用jOOR反射时的语法如下:

    //实例字段
    Reflect obj = on("jianqiang.com.testreflection.User").create();
    obj.set("name", "jianqiang");
    Object newObj = obj.get("name");

    //静态字段
    Reflect obj2 = on("jianqiang.com.testreflection.User");
    obj2.set("userId", "123");
    Object newObj2 = obj2.get("userId");

上面这段代码在执行set语法时必然报错,抛出一个NoSuchFieldException异常。究其原因,是jOOR的Reflect的set方法会在遇到final时,尝试反射出Field类的modifiers字段,在Java环境是有这个字段的,但是Android版本的Field类并没有这个字段,于是就报错了。

3.3 对基本反射语法的封装

考虑到jOOR的局限性,我们不得不另辟蹊径。对基本的Java反射语法进行封装,以得到简单的语法。

考察前面介绍的种种语法,无论是反射出一个类,还是反射出一个构造函数并调用它,都是为了进一步读写类的方法和字段,所以我们只要封装以下几个方法即可:

□ 反射出一个构造函数并调用它。

□ 调用静态方法。

□ 调用实例方法。

□ 获取和设置一个字段的值。

□ 对泛型的处理。

提示

本节的示例代码参见https://github.com/BaoBaoJianqiang/TestReflection3

3.3.1 反射出一个构造函数

在RefInvoke类定义方法如下:

    public static Object createObject(String className, Class[] pareTyples, Object[]
        pareVaules) {
        try {
            Class r = Class.forName(className);
            Constructor ctor = r.getDeclaredConstructor(pareTyples);
            ctor.setAccessible(true);
            return ctor.newInstance(pareVaules);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

以下是对这个封装函数的调用:

    Class r = Class.forName(className);

    //含参
    Class[] p3 = {int.class, String.class};
    Object[] v3 = {1, "bjq"};
    Object obj = RefInvoke.createObject(className, p3, v3);

    //无参
    Object obj2 = RefInvoke.createObject(className, null, null);

3.3.2 调用实例方法

在RefInvoke类定义方法如下:

    public static Object invokeInstanceMethod(Object obj, String methodName, Class[]
        pareTyples, Object[] pareVaules) {
        if(obj == null)
            return null;

        try {
            //调用一个private方法
            Method method = obj.getClass().getDeclaredMethod(methodName, pareTyples);
                //在指定类中获取指定的方法
            method.setAccessible(true);
            return method.invoke(obj, pareVaules);

        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

以下是调用这个封装函数:

    Class[] p3 = {};
    Object[] v3 = {};
    RefInvoke.invokeStaticMethod(className, "work", p3, v3);

3.3.3 调用静态方法

在RefInvoke类定义方法如下:

    public  static  Object  invokeStaticMethod(String  className,  String  method_name,
        Class[] pareTyples, Object[] pareVaules) {
        try {
                Class obj_class = Class.forName(className);
            Method method = obj_class.getDeclaredMethod(method_name, pareTyples);
            method.setAccessible(true);
            return method.invoke(null, pareVaules);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

以下是调用这个封装函数:

    Class[] p4 = {String.class};
    Object[] v4 = {"jianqiang"};
    Object result = RefInvoke.invokeInstanceMethod(obj, "doSOmething", p4, v4);

3.3.4 获取并设置一个字段的值

在RefInvoke类定义方法如下:

    public static Object getFieldObject(String className, Object obj, String filedName) {
        try {
                Class obj_class = Class.forName(className);
            Field field = obj_class.getDeclaredField(filedName);
            field.setAccessible(true);
            return field.get(obj);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    public static void setFieldObject(String classname, Object obj, String filedName,
        Object filedVaule) {
        try {
            Class obj_class = Class.forName(classname);
            Field field = obj_class.getDeclaredField(filedName);
            field.setAccessible(true);
            field.set(obj, filedVaule);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

以下是调用这两个封装函数:

    //获取实例字段
    Object fieldObject = RefInvoke.getFieldObject(className, obj, "name");
    RefInvoke.setFieldObject(className, obj, "name", "jianqiang1982");

    //获取静态字段
    Object fieldObject = RefInvoke.getFieldObject(className, null, "address");
    RefInvoke.setFieldObject(className, null, "address", "ABCD");

3.3.5 对泛型类的处理

借助于前面封装的5个方法,我们重写对泛型的反射调用:

    //获取AMNgDefault单例gDefault, gDefault是静态的
    Object  gDefault  =  RefInvoke.getFieldObject("jianqiang.com.testreflection.AMN",
        null, "gDefault");

    // gDefault是一个 android.util.Singleton对象我们取出这个单例里面的mInstance字段
    Object rawB2Object = RefInvoke.getFieldObject(
        "jianqiang.com.testreflection.Singleton",
        gDefault, "mInstance");

    // 创建一个这个对象的代理对象ClassB2Mock, 然后替换这个字段, 让我们的代理对象帮忙干活
    Class<? > classB2Interface = Class.forName("jianqiang.com.testreflection.
        ClassB2Interface");
    Object proxy = Proxy.newProxyInstance(
        Thread.currentThread().getContextClassLoader(),
        new Class<? >[] { classB2Interface },
        new ClassB2Mock(rawB2Object));

    //SingletonmInstance替换为proxy
    RefInvoke.setFieldObject("jianqiang.com.testreflection.Singleton",  gDefault,
        "mInstance", proxy);

虽然不是面向对象的语法,但总要简单得多。

3.4 对反射的进一步封装

我们在3.3节对反射语法进行了简单的封装,但在实际的使用中,我们发现,有的时候,有很多不方便的地方。在本节中会对此进行优化。

1.对于无参数和只有一个参数的处理

通过反射调用方法或者构造函数的时候,它们有时只需要一个参数,有时根本不需要参数,但我们每次都要按照多个参数的方式来编写代码,如下所示:

    Class r = Class.forName(className);

    //含参
    Class[] p3 = {int.class, String.class};
    Object[] v3 = {1, "bjq"};
    Object obj = RefInvoke.createObject(className, p3, v3);

    //无参
    Object obj2 = RefInvoke.createObject(className, null, null);

我们希望把代码写的更简单一些,比如说这样:

    //无参
    public static Object createObject(String className) {
        Class[] pareTyples = new Class[]{};
        Object[] pareVaules = new Object[]{};

        try {
            Class r = Class.forName(className);
            return createObject(r, pareTyples, pareVaules);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        return null;
    }

    //一个参数
    public static Object createObject(String className, Class pareTyple, Object pareVaule) {
        Class[] pareTyples = new Class[]{ pareTyple };
        Object[] pareVaules = new Object[]{ pareVaule };

        try {
            Class r = Class.forName(className);
            return createObject(r, pareTyples, pareVaules);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        return null;
    }

    //多个参数
    public static Object createObject(String className, Class[] pareTyples, Object[]
        pareVaules) {
        try {
            Class r = Class.forName(className);
            return createObject(r, pareTyples, pareVaules);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        return null;
    }

    //多个参数
    public  static  Object  createObject(Class  clazz,  Class[]  pareTyples,  Object[]
        pareVaules) {
        try {
            Constructor ctor = clazz.getDeclaredConstructor(pareTyples);
            ctor.setAccessible(true);
            return ctor.newInstance(pareVaules);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

以此类推,构造函数就是方法,我们封装的invokeStaticMethod和invokeInstanceMethod方法也可以有这许多种重载方式。

2.字符串可以替换为Class

截止到现在,我们加载类的方式,都是通过字符串的方式,比如createObject的实现:

    public static Object createObject(String className, Class[] pareTyples, Object[]
        pareVaules) {
        try {
            Class r = Class.forName(className);
            Constructor ctor = r.getConstructor(pareTyples);
            return ctor.newInstance(pareVaules);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

但有时候,我们直接就拥有这个类的Class类型,而不再用Class.forName(className)的方式再去生成,于是就可以有字符串和Class这两种形式的方法重载,如下所示:

    //多个参数
    public static Object createObject(String className, Class[] pareTyples, Object[]
        pareVaules) {
        try {
            Class r = Class.forName(className);
            return createObject(r, pareTyples, pareVaules);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        return null;
    }

    //多个参数
    public  static  Object  createObject(Class  clazz,  Class[]  pareTyples,  Object[]
        pareVaules) {
        try {
            Constructor ctor = clazz.getConstructor(pareTyples);
            return ctor.newInstance(pareVaules);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

RefInvoke中的所有方法,大都拥有String和Class这两种方法的重载,限于篇幅,这里就不一一介绍了,具体情况可以参考源码例子。

3.区分静态字段和实例字段

在反射字段的时候,我们发现,对静态字段和实例字段的处理,区别就在一个地方,由于静态字段的反射不需要obj参数,所以设置为null,如下所示:

    //获取实例字段
    Object fieldObject = RefInvoke.getFieldObject(className, obj, "name");
    RefInvoke.setFieldObject(className, obj, "name", "jianqiang1982");

    //获取静态字段
    Object fieldObject = RefInvoke.getFieldObject(className, null, "address");
    RefInvoke.setFieldObject(className, null, "address", "ABCD");

为了区分静态字段和实例字段这两种场景,我们把静态字段的读写方法改名为getStaticFieldObject和setStaticFieldObject,它们间接的调用了实例字段的getFieldObject和setFieldObject方法,但是省略了obj参数,如下所示,

    public static Object getStaticFieldObject(String className, String filedName) {
        return getFieldObject(className, null, filedName);
    }

    public static void setStaticFieldObject(String classname, String filedName, Object
        filedVaule) {
        setFieldObject(classname, null, filedName, filedVaule);
    }

那么在反射静态字段的时候,就可以优雅的写出下列代码了:

    Object fieldObject = RefInvoke.getFieldObject(className, null, "address");
    RefInvoke.setStaticFieldObject(className, "address", "ABCD");

4.对反射读写字段的优化

继续观察对实例字段的封装方法,以getFieldObject为例:

    public static Object getFieldObject(Class clazz, Object obj, String filedName) {
        try {
            Field field = clazz.getDeclaredField(filedName);
            field.setAccessible(true);
            return field.get(obj);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

我们发现,大多数情况下,obj的类型就是clazz,所以可以省略clazz参数,于是编写两个简易版的重载方法,如下所示:

    public static Object getFieldObject(Object obj, String filedName) {
        return getFieldObject(obj.getClass(), obj, filedName);
    }

    public static void setFieldObject(Object obj, String filedName, Object filedVaule) {
        setFieldObject(obj.getClass(), obj, filedName, filedVaule);
    }

然后就可以优雅的编写代码了:

    Object fieldObject = RefInvoke.getFieldObject(className, "address");
    RefInvoke.setStaticFieldObject(className, "address", "ABCD");

但是也有例外,如果obj的类型不是clazz(obj有可能是clazz的孙子,甚至辈分更低),那么就只能老老实实的使用原先封装的getFieldObject和setFieldObject方法。

3.5 本章小结

本章给出了反射语法的三种编写方式:

1)基本反射语法。

2)jOOR语法。

3)对基本反射语法的封装。

由于jOOR对Android的支持不是很好,所以业内开源框架一般采用的是方式1和3。

在本书后面的章节中,使用方式3来编写插件化的反射代码。