android中反射的应用

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

然而在android中Google很多的类的某些方法不让第三方应用去调用,通过java反射机制能把这些隐藏方法获取出来并调用,三方应用上我们就很方便的去用这些方法。

我们以android中获取StorageManager类中的getVolumePaths()方法为例:

            StorageManager sm = (StorageManager) getSystemService(STORAGE_SERVICE);
            Class c=StorageManager.class;
            Method method=c.getMethod("getVolumePaths",null);
            String[] paths= (String[]) method.invoke(sm, null);

一、Class类

首先向大家说明一点,Class本身就是一个类,Class是该类的名称。看下面这个类的定义:
  public class MyButton extends Button {…}
  注意到上面的class的首字母是小写,它表示的是一种类类型,但是我们的Class是一个类,相当于上面定义的MyButton类。所以,千万不要把这里的Class做为一个类类型来理解。

Class类是整个Java反射机制的源头,获取Class对象的方法有三种:

  1. Class.forName(“Employee”)
  2. Employee.class
  3. Employee e = new Employee(); e.getClass();

在平时的使用,要注意对这几种方法的灵活运用,尤其是对Class.forName()方法的使用。因为在很多开发中,会直接通过类的名称取得Class类的对象。
例子中使用的是 . Class:Class c=StorageManager.class;

二、获取类的相关信息

1.获取构造方法

Class类提供了四个public方法,用于获取某个类的构造方法。

1.Constructor getConstructor(Class[] params) 根据构造函数的参2.数,返回一个具体的具有public属性的构造函数

2.Constructor getConstructors() 返回所有具有public属性的构造函数数组

3.Constructor getDeclaredConstructor(Class[] params) 根据构造函数的参数,返回一个具体的构造函数(不分public和非public属性)

4.Constructor getDeclaredConstructors() 返回该类中所有的构造函数数组(不分public和非public属性)

例如: 获取指定类的公有构造方法
Constructor[] theConstructors = c.getDeclaredConstructors();

2.获取类的成员方法

与获取构造方法的方式相同,存在四种获取成员方法的方式。

1.Method getMethod(String name, Class[] params) 根据方法名和参数,返回一个具体的具有public属性的方法

2.Method[] getMethods() 返回所有具有public属性的方法数组

3.Method getDeclaredMethod(String name, Class[] params) 根据方法名和参数,返回一个具体的方法(不分public和非public属性)

4.Method[] getDeclaredMethods() 返回该类中的所有的方法数组(不分public和非public属性)

注意:方法getDeclaredMethods()只能获取到由当前类定义的所有方法,不能获取从父类继承的方法;方法getMethods() 不仅能获取到当前类定义的public方法,也能得到从父类继承和已经实现接口的public方法。

3.获取类的成员变量

存在四种获取成员属性的方法:

1.Field getField(String name) 根据变量名,返回一个具体的具有public属性的成员变量

2.Field[] getFields() 返回具有public属性的成员变量的数组

3.Field getDeclaredField(String name) 根据变量名,返回一个成员变量(不分public和非public属性)

4.Field[] getDelcaredField() 返回所有成员变量组成的数组(不分public和非public属性)

4.Method

Method类中包含着类的成员方法的信息。在Method类中有一个public成员函数:Object invoke(Object receiver, Object… args),参数receiver指明了调用对象,参数args指明了该方法所需要接收的参数。由于我们是在运行时动态的调用类的方法,无法提前知道该类的参数类型和返回值类型,所以传入的参数的类型是Object,返回的类型也是Object。(因为Object类是所有其他类的父类)
  如果某一个方法是Java类的静态方法,那么Object receiver参数可以传入null,因为静态方法从不属于对象。

Method中的receiver代表要调用方法所在类的实例,要调用一个类的方法,首先需要一个该类的实例(当然,如果该类是static,就不需要实例了,至于原因,你懂得!)。

在得到一个类的Class对象之后,我们可以利用类Constructor去实例化该对象。Constructor支持泛型,也就是它本身应该是Constructor。这个类有一个public成员函数:T newInstance(Object… args),其中args为对应的参数,我们通过Constructor的这个方法来创建类的对象实例。
  在代码LoadMethod.java和LoadMethodEx.java中,分别给出了两种实例化Class类的方法:一种是利用Constructor类调用newInstance()方法;另一种就是利用Class类本身的newInstance()方法创建一个实例。两种方法实现的效果是一样的。
// 利用newInstance()方法,获取构造方法的实例

// Class的newInstance方法,仅提供默认无参的实例化方法,类似于无参的构造方法

// Constructor的newInstance方法,提供了带参数的实例化方法,类似于含参的构造方法

Constructor ct = cls.getConstructor(null);

Object obj = ct.newInstance(null);

Object obj = cls.newInstance();

例子中的String[] paths= (String[]) method.invoke(sm, null);
sm就是StorageManager类的实例,我们通过StorageManager sm = (StorageManager) getSystemService(STORAGE_SERVICE);得到的。由于getVolumePaths()方法中没有参数,传入null就可以了;

使用

上面说的基本都是获取方式,下面透过一个例子来实际使用他们:
首先创建一个用于反射的类并创建一些成员变量、方法等属性:

public class ReflectTest {

    public String testPublicFiled;
    private String testPrivateFiled;

    public ReflectTest() {
    }

    public ReflectTest(String testPublicFiled, String testPrivateFiled) {
        this.testPublicFiled = testPublicFiled;
        this.testPrivateFiled = testPrivateFiled;
    }

    public void testPublicMethod(String name) {

    }

    private String testPrivateMethod() {
        return null;
    }

}

然后在其他地方通过反射获取这个类的信息:

 public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        //获取类
        Class c = Class.forName("com.shenhesoft.lib.ReflectTest");

        //获取继承的父类
        String superClass = c.getSuperclass().getSimpleName();
        System.out.println(superClass);

        System.out.println("------------------------------");

        //获取实现的接口
        Class[] interfaces = c.getInterfaces();
        for (int i = 0; i < interfaces.length; i++) {
            System.out.println(interfaces[i].getSimpleName());
        }

        System.out.println("-----------------------------");

        //获取类中所有的成员变量
        Field[] fields = c.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            //参数修饰类型
            String modifyName = Modifier.toString(fields[i].getModifiers());
            //参数类型
            String argsTypeName = fields[i].getType().getSimpleName();
            //参数名
            String argsName = fields[i].getName();

            System.out.println(modifyName + " " + argsTypeName + " " + argsName);
        }
        //获取指定名称的变量
        Field field = c.getDeclaredField("testPublicFiled");
        //省略。。。。。。。。。

        System.out.println("-------------------------------");

        //获取类中所有的方法
        Method[] methods = c.getDeclaredMethods();
        for (int i = 0; i < methods.length; i++) {
            //方法修饰类型
            String modifyName = Modifier.toString(methods[i].getModifiers());
            //方法返回类型
            String returnType = methods[i].getReturnType().getSimpleName();
            //方法名
            String methodName = methods[i].getName();
            //方法里的参数类型
            Class[] parameterTypes = methods[i].getParameterTypes();
            String paramType = "";
            for (int i1 = 0; i1 < parameterTypes.length; i1++) {
                paramType = paramType + parameterTypes[i1].getSimpleName() + " ";
            }
            System.out.println(modifyName + " " + returnType + " " + methodName + " " + paramType);
        }
        //获取指定名称的方法
        Method method = c.getDeclaredMethod("testPublicMethod", String.class);
        //省略。。。。。。。。。

        System.out.println("---------------------------------");

        //获取类中所有的构造方法
        Constructor[] constructors = c.getDeclaredConstructors();
        for (int i = 0; i < constructors.length; i++) {
            //获取构造方法的修饰类型
            String modifyName = Modifier.toString(constructors[i].getModifiers());
            //获取构造函数的参数类型
            Class[] parameterTypes = constructors[i].getParameterTypes();
            String paramType = "";
            for (int i1 = 0; i1 < parameterTypes.length; i1++) {
                paramType = paramType + parameterTypes[i1].getSimpleName() + " ";
            }
            System.out.println(modifyName + " " + paramType);
        }
        //获取指定名称的方法
        Constructor constructor = c.getDeclaredConstructor(null);
        //省略。。。。。。。。。

    }

前面写的那么多方法在这里就用到了,我就不一一的讲了,对照着注释和前面的文字就能看懂,其实调用的方法就那么几个。

通过反射操作对象

想要操作对象首先要获取到这个对象,然后才能通过反射操作这个对象,有些人可能会问,我已经有这个对象了可以直接操作为什么还要通过反射操作,这个问题的答案就是反射的核心。你有了对象可以直接操作私有的成员变量和方法吗,可以修改final修饰的成员变量吗,显然是不行的,下面我们看一下反射怎么做到这些。
修改ReflectTest类如下:

public class ReflectTest {

    public String testPublicFiled;
    private String testPrivateFiled;

    public ReflectTest() {
    }

    public ReflectTest(String testPublicFiled, String testPrivateFiled) {
        this.testPublicFiled = testPublicFiled;
        this.testPrivateFiled = testPrivateFiled;
    }

    public void testPublicMethod(String name) {
        System.out.println("公共方法,参数:" + name);
    }

    private void testPrivateMethod() {
        System.out.println("私有方法");
    }

    public String getTestPublicFiled() {
        return testPublicFiled;
    }

    public String getTestPrivateFiled() {
        return testPrivateFiled;
    }
}

下面我们直接通过反射修改ReflectTest的私有成员变量testPrivateFiled:

    public static void main(String[] args) throws Exception{
        //获取类
        Class c = Class.forName("com.shenhesoft.lib.ReflectTest");
        //创建一个对象
        ReflectTest reflectTest = new ReflectTest("公共参数默认值", "私有参数默认值");
        //打印私有参数的值
        System.out.println(reflectTest.getTestPrivateFiled());
        //获取私有的成员变量
        Field field = c.getDeclaredField("testPrivateFiled");
        //设置可以访问私有的成员变量,重要
        field.setAccessible(true);
        //获取私有成员变量的值
        String s = (String) field.get(reflectTest);
        System.out.println(s);
        //修改私有成员变量的值
        field.set(reflectTest, "修改私有方法");
        //打印私有参数的值
        System.out.println(reflectTest.getTestPrivateFiled());
    }

运行结果:

私有参数默认值
私有参数默认值
修改私有方法

进程完成,退出码 0

可以看到我们已经成功通过反射修改了私有成员变量的值,关于通过反射修改公共成员变量我就不写了。

接下来我们看一下通过反射调用私有的方法testPrivateMethod():

    public static void main(String[] args) throws Exception {
        //获取类
        Class c = Class.forName("com.shenhesoft.lib.ReflectTest");
        //创建一个对象
        ReflectTest reflectTest = new ReflectTest("公共参数默认值", "私有参数默认值");
        //获取私有的方法
        Method method = c.getDeclaredMethod("testPrivateMethod", null);
        //设置可以访问私有的成员方法,重要
        method.setAccessible(true);
        //调用私有方法
        method.invoke(reflectTest, null);
    }

运行结果:

私有方法

进程完成,退出码 0

结果就不用多说了,接下来我们再看一下带有参数的公共方法怎么调用:

   public static void main(String[] args) throws Exception {
        //获取类
        Class c = Class.forName("com.shenhesoft.lib.ReflectTest");
        //创建一个对象
        ReflectTest reflectTest = new ReflectTest("公共参数默认值", "私有参数默认值");
        //获取公共的方法
        Method method = c.getMethod("testPublicMethod", String.class);
        //调用公共方法
        method.invoke(reflectTest, "反射");
    }

运行结果:

公共方法,参数:反射

进程完成,退出码 0

然后还有重要的一个操作,反射获取私有的静态成员变量和调用私有的静态方法,我们都知道静态的属性是类加载器启动的时候就已经加载了,并且全局只有一个,那我们获取的时候还需要对象吗,显然是不需要的,下面是具体的代码:
先在ReflectTest类中添加静态成员变量和方法

public class ReflectTest {

    public String testPublicFiled;
    private String testPrivateFiled;
    private static String testStaticFiled;

    public ReflectTest() {
    }

    public ReflectTest(String testPublicFiled, String testPrivateFiled) {
        this.testPublicFiled = testPublicFiled;
        this.testPrivateFiled = testPrivateFiled;
    }

    public void testPublicMethod(String name) {
        System.out.println("公共方法,参数:" + name);
    }

    private void testPrivateMethod() {
        System.out.println("私有方法");
    }

    public String getTestPublicFiled() {
        return testPublicFiled;
    }

    public String getTestPrivateFiled() {
        return testPrivateFiled;
    }

    public static String getTestStaticFiled() {
        return testStaticFiled;
    }

    private static void testStatic() {
        System.out.println("调用私有static方法");
    }
}
    public static void main(String[] args) throws Exception {
        //获取类
        Class c = Class.forName("com.shenhesoft.lib.ReflectTest");
        Method method = c.getDeclaredMethod("testStatic", null);
        method.setAccessible(true);
        method.invoke(null, null);
    }

运行结果:

调用私有static方法

进程完成,退出码 0

获取并修改静态成员变量的值

    public static void main(String[] args) throws Exception {
        //获取类
        Class c = Class.forName("com.shenhesoft.lib.ReflectTest");
        Field field = c.getDeclaredField("testStaticFiled");
        field.setAccessible(true);
        field.set(null, "修改静态的成员变量");
        System.out.println(ReflectTest.getTestStaticFiled());
    }
修改静态的成员变量

进程完成,退出码 0

最后在补充一个反射有参数方法的例子:

Class mClass=m.getClass();

Method method=mClass.getDeclaredMethod("setDrawerViewOffset",new Class[]{View.class,float.class});

method.invoke(m,m,0.9);

方法名为:setDrawerViewOffset;
参数类型为:VIew,float;
调用该方法时传入实例m,以及参数m(View类型),0.9(float类型);

反射私有成员变量

Class mClass=m.getClass();
//通过name获取成员变量mMinDrawerMargin
Field field=mClass.getDeclaredField("mMinDrawerMargin");
//设置私有变量可以修改
field.setAccessible(true);
//获取成员变量的值
Object value= field.get(m);
//mMinDrawerMargin原来的值
Log.i("info",value.toString());
//修改成员变量的值           
field.set(m,300);
//获取修改之后成员变量的值
value=field.get(m);
//mMinDrawerMargin修改之后的值
Log.i("info",value.toString());

top:访问所有private类型的变量、方法都要设置setAccessible(true);

参考自:https://blog.csdn.net/sinat_38259539/article/details/71799078

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值