java反射机制
java中的反射
什么是反射?
反射是java中非常强大的一个技术,甚至很多博客中称其为java中最强大的技术。
java的反射机制其实是java提供给开发者的一种动态获取信息,动态调用对象的机制,简单来说就是在运行状态中,对于任意一个类,都能知道这个类所有的属性和方法,对于任意一个对象,都能调用它任意一个方法和属性。
一个例子
上面这样说还是比较抽象,举一个简单的例子,当我们需要为某个需求定制接口供app调用时,比如说需要在android.os
路径下新增一个HapticPlay
类,这个类中实现了一个start
方法,那么当我们在framework中添加好接口,编译通过之后,AndroidStudio一定是调用不到的,因为AndroidStudio中只有谷歌官方的接口,但是system.img中确实有了我们新增的接口,这个时候,我们就可以用反射来调用。
具体用法
反射的基本用法很简单,仍然以HapticPlayer
类为例
- 要调用一个类中的方法,首先需要获取到这个类,使用反射机制时,我们只需要知道包路径和包名即 可,根据上面的描述,包路径为
android.os
,包名为HapticPlayer
,那么获取类只 需要一行代码
Class<?> clz = Class.forName("android.os.HapticPlayer");
Class类型大致可以按照C语言中的void *
理解,如果这行代码没有抛异常的话,此时clz就代表我们的HapticPlayer
类。
- 获取到类之后需要获取类中我们想调用的方法
Method startMethod = clz.getMethod("start",int.class);
入口参数中,start
代表要调用的方法名,int.class
代表方法的入参为int
类型,也就是说,我们要调用的方法为start(int a)
。
- 接着就需要实例化这个类,
Constructor<?> hapticPlayerCon = clz.getDeclaredConstructor());
getDeclaredConstructor
相当于调用HapticPlayer
的构造函数,当构造函数有参时,比如int
类型调用的方法就是getDeclaredConstructor(int.class)
,如果是参数类型是自定义数据结构,那么就是getDeclaredConstructor(Class.forName("xxx"))
,比如我这里的参数是android.os.DynamicEffect
(新增的类),我就需要写成getDeclaredConstructor(Class.forName("android.os.DynamicEffect"))
。此时,如果没有抛出异常,那么hapticPlayerCon
相当于HapticPlayer
类中入参为android.os.DynamicEffect
的构造函数。
- 这时,我们就可以利用构造函数实例化这个类,
Object hapticObj = hapticPlayerCon.newInstance();
此时,如果没有抛出异常,那么hapticObj
相当于HapticPlayer
类的一个实例,利用这个实例,我们就可以调用start
方法。
- 这时,我们调用
startMethod.invoke(hapticObj, var);
就可以正常调用到start方法,invoke
函数中,第一个参数是start
方法所在的对象,第二个参数是start
方法需要的参数,我这里是一个int
。
所以,整个函数是这样的
public static void start (int loop) throws IllegalArgumentException,
ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
@SuppressLint("PrivateApi")
Class<?> clz = Class.forName("android.os.HapticPlayer");
Method startMethod = clz.getMethod("start",int.class);
@SuppressLint("PrivateApi")
Constructor<?> hapticPlayerCon = clz.getDeclaredConstructor(Class.forName("android.os.DynamicEffect"));
@SuppressLint("PrivateApi")
Object hapticObj = hapticPlayerCon.newInstance();
startMethod.invoke(hapticObj,loop);
}
相比与之前的代码,这个函数中只是添加了throw异常和@SuppressLint("PrivateApi")
,throw异常是为了当找不到这个类时app不会崩溃,@SuppressLint("PrivateApi")
是为了屏蔽一些警告----Android10之后,反射不再是谷歌推荐的方法,Android11上,谷歌在framework限制了反射调用api,通过反射调用api时会抛出NoSuchMethod
异常,这个问题可以看另一篇文档《Android11 apk中调用hide api》。
结语
反射是非常好用非常强大的机制,上面的介绍只是一点点皮毛。想要学学app的同事一定要重点关注这个机制,在定制接口、添加服务等场景下,一定会用到这个机制。