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对象的方法有三种:
- Class.forName(“Employee”)
- Employee.class
- 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获取成员变量mMinDrawerMarginField 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
更多相关文章
- android中px,dp,sp的区别与使用方法
- Android核心分析 之一--------分析方法论探讨之设计意图
- android 设置颜色的三种方法
- android 获取设备支持的编解码器的方法
- Android Studio:Error:Could not find com.android.tools.build:g