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的同事一定要重点关注这个机制,在定制接口、添加服务等场景下,一定会用到这个机制。