Android 使用反射 反射获取activity

1. 背景

在andorid开发中,经常遇见在某些工具类中没有Context上下文对象时,一些系统服务的代理对象无法创建出来,举个例子:比如在源码(framework/base/graphics/java/android/graphics)路径下的Canvas.java Bitmap.java Picture.java Paint.java 类就没有上下文对象。 当时有需要调用AMS的API获取当前界面activity的名称作为判断条件去修改这些工具类中的参数,但是这几个工具类中又没有上下文对象,怎么办呢?

一般情况下,获取顶层activity名称的方法如下:

java 复制代码
//1. 获取 AMS 的代理对象 ActivityManager
ActivityManager am = (ActivityManager) context
                    .getSystemService(Context.ACTIVITY_SERVICE);

//2. 调用相关API 获取顶层activity的名称
String activityName = am.getRunningTasks(1).get(0).topActivity.getClassName();

上述代码是在有上下文对象context的情况下,直接获取。

而在上面提及到的 Picture.java等类中, 根本就没有上下文,怎么搞? 这个时候我们通过java反射来实现,接下来,本篇文章就来讲解一下java反射机制,并利用反射实现获取顶层activity。

2. java反射

2.1 什么是反射?

反射(Reflection)是程序的自我分析能力,通过反射可以确定类中有哪些方法、有哪些构造方法以及有哪些成员变量,在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

2.2 什么情况下要用反射?

首先java是面向对象编程的,万物皆对象, 当我们拿到这个类对象后,就可以访问类中的成员变量,调用类中的方法了,但是呢?对于private变量和方法,它的访问权限作用域只在本类中,在外部类或者继承类中,就算你创建了该类对象,也是无法直接调用的。来看下这个例子:

普通的一个User 类

java 复制代码
public class User {
    private String name = "墨子"; //私有变量
    public int age = 18;

    //私有方法
    private int test() {
        System.out.println("私有成员方法");
        return 1;
    }

    protected String test2() {
        System.out.println("protected成员方法");
        return "protected";
    }

    public static void main(String[] args) {
        //如果在本类中创建了对象,则所有的方法和变量都可以直接访问
        User user = new User();
        System.out.println(user.name); //访问私有变量
        System.out.println(user.test());//调用私有方法
    }
}

____________________________________________________________
打印结果如下:
墨子
私有成员方法
1

如果在外部内中创建该类对象,还能直接访问私有方法和变量吗? 测试一下:

java 复制代码
public class PrivateTest {
    public static void main(String[] args) {
        User user = new User();

        //外部内中创建的对象直接私有变量  报错
        //System.out.println(user.name);

        //外部内中创建的对象,直接调用private方法,也报错
        //System.out.println(user.test());

        //但是可以直接访问 protected public的方法和变量
        System.out.println(user.age);
        System.out.println(user.test2());
    }
}

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 报错的两行代码注释掉了,可以把代码复制过去验证一下。

当然了,在andorid开发中,还有其他场景会使用到反射, 很多类或方法中经常加上了"@hide"注释标记,这些API是不允许在第三方应用直接调用的 比如:SystemProperties类在android.os下,但这个类是隐藏的,没有系统权限(android:sharedUserId="android.uid.system")的app无法直接使用,只能通过java反射来调用。

还有 Andorid中动态代理模式 , Android插件化(Hook)这些都会用到反射,这里不一一说明,因为每个技术点都需要去深挖才能理解透彻。

2.3 反射的优缺点

优点:

  1. 可以调用私有方法、变量

  2. 可以调用隐藏api接口

  3. 能够运行时动态获取类的实例,提高代码灵活性

缺点:

  1. 安全问题:反射可以获取到类对应的所有方法和属性,如果存在外部调用的情况,可能出现超出预期的调用,从而导致安全风险, 而且也破坏java面向对象编程的封装这一特性。

  2. 性能问题:无论是通过字符串获取Class、Method还是Field,都需要JVM的动态链接机制动态的进行解析和匹配,势必造成性能开销。每一次的反射调用都会造成Java安全机制进行额外的安全性验证,造成性能开销。反射代码使得许多JVM的运行时优化无法进行。

3. Java反射机制API

Java反射机制API主要是 java.lang.Class类 和 java.lang.reflect包。

|-------------------------------|-----------------------------------|
| 类 | 说明 |
| java.lang.Class | 代表整个字节码。代表一个类型,代表整个类。 |
| java.lang.reflect.Constructor | 代表字节码中的构造方法字节码。代表类中的构造方法。 |
| java.lang.reflect.Method | 代表字节码中的方法字节码。代表类中的方法。 |
| java.lang.reflect.Field | 代表字节码中的属性字节码。代表类中的成员变量(静态变量+实例变量) |

3.1 获取Class对象

|-------------------------|--------|
| 方式 | 说明 |
| 对象.getClass(); | |
| 类名.class | |
| Class.forName("类的全路径"); | 推荐此种写法 |

java 复制代码
package com.example.javademo.reflect;

/**
 * author : me
 * date   : 22-10-21下午4:39
 * desc   : 获取Class对象的3种方式
 * version: 1.0
 */
public class ClassObject {

    public static void main(String[] args) throws ClassNotFoundException {
        //第一种: 对象.getClass();
        People people = new People();
        Class clazz1 = people.getClass();
        System.out.println(clazz1);

        //第二种: 类名.class
        Class clazz2 = People.class;
        System.out.println(clazz2);

        //第三种: Class.forName(包名.类名)
        Class clazz3 = Class.forName("com.example.javademo.reflect.People");
        System.out.println(clazz3);
    }
}

第一种:直接new出对象,然后通过对象获取class对象, 但是你想想类对象都可以直接new出来,在外部内中除了private 变量和方法不能直接访问外,都可以直接调用,我还反射干嘛?写那么多代码岂不是为难自己,没事找事,不推荐!

第二种:通过类名.class 获取字节码class对象,需要导入包,依赖性比较强,不然会编译报错

第三种:Class的静态方法,安全性强, 参数为:类包名+类名

运行结果如下:

java 复制代码
class com.example.javademo.reflect.People
class com.example.javademo.reflect.People
class com.example.javademo.reflect.People

三个都是同一个Class对象,在运行期间,一个类,只有一个Class对象产生

3.2 反射调用类构造方法

|--------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------|
| 方法名 | 解释说明 |
| public Constructor[] getConstructors() | 所有"公有的"构造方法 |
| public Constructor getConstructor(Class... parameterTypes) | 获取指定参数的共有构造方法 |
| public Constructor[] getDeclaredConstructors() | 获取所有的构造方法(包括私有、受保护、默认、公有) |
| public Constructor getDeclaredConstructor(Class... parameterTypes) | :获取"指定参数的构造方法"可以是私有的,或受保护、默认、公有; |
| newInstance(Object... initargs) | 使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。 它的返回值是T类型,所以newInstance是创建了一个构造方法的声名类的新实例对象,相当于People p = new People(); |

我们看下Demo

java 复制代码
package com.example.javademo.reflect;

/**
 * author : me
 * date   : 22-10-20??10:08
 * desc   : 供反射调用的构造方法类
 * version: 1.0
 */
public class People {

    private String name;

    private int age;

    public String sex;

    private long height;

    public People() {
        System.out.println("调用了public无参构造方法 执行完毕");
    }

    public People(String name) {
        this.name = name;
        System.out.println("调用public有参构造方法 String:name = " + name);
    }

    private People(int age, String sex) {
        this.age = age;
        this.sex = sex;
        System.out.println("调用private有参数构造方法 age :"+ age + "  sex:" + sex);
    }

    People(long height) {
        this.height = height;
        System.out.println("调用default有参数构造方法 height :"+ height );
    }

}

反射测试类:

java 复制代码
package com.example.javademo.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * author : me
 * date   : 22-10-20上午10:12
 * desc   : 反射调用构造方法的实现类
 * version: 1.0
 */
public class ReflectPeople {

    public static void main(String[] args) {


        try {
            //获取People class对象
            Class peopleclass = Class.forName("com.example.javademo.reflect.People");
            System.out.println("**********************获取反射后类对象*********************************");
            System.out.println("反射类对象: " + peopleclass);

            //获取所有构造方法
            System.out.println("**********************打印所有构造方法*********************************");
            Constructor[] conArray = peopleclass.getDeclaredConstructors();
            for(Constructor c : conArray){
                System.out.println(c);
            }

            System.out.println("**************第一种:通过反射调用默认无参构造方法并创建people类实例对象***************************");

            //返回一个 Constructor对象, 该对象反映Constructor对象表示的类的指定的公共类函数。
            Object constructorPublic = peopleclass.getConstructor();
            System.out.println("无参构造方法的对象:" + peopleclass.getConstructor());
            //使用此 Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例。
            //newInstance()方法内部实际上调用了无参数构造方法,必须保证无参构造存在才可以
            Object peopleobject1 = ((Constructor) constructorPublic).newInstance();
            System.out.println("创建people1对象新实例: " + (People)peopleobject1);


            System.out.println("**************第二种:通过反射调用public有参构造方法并创建People类实例对象***************************");
            Object peopleobject2 = peopleclass.getConstructor(String.class).newInstance("甲方");
            System.out.println("创建people2对象新实例: " + (People)peopleobject2);


            System.out.println("**************第三种:通过反射调用private有参构造方法并创建People类实例对象***************************");
            //注意,如果访问protect private相关的构造方法,需要用getDeclaredConstructor这个API
            //返回一个 Constructor对象,该对象反映 Constructor对象表示的类或接口的指定类函数。

            Constructor constructorPrivate = peopleclass.getDeclaredConstructor(int.class, String.class);
            //访问私有构造方法,这句话一定要写,不然就会报错java.lang.IllegalAccessException
            constructorPrivate.setAccessible(true);
            System.out.println("有参private私有构造方法的对象:" + peopleclass.getDeclaredConstructor(int.class, String.class));
            Object peopleobject3 = constructorPrivate.newInstance(20, "men");
            System.out.println("创建people3对象新实例: " + peopleobject3);


            System.out.println("**************第四种:通过反射调用default有参构造方法并创建People类实例对象***************************");
            Constructor constructorDefault = peopleclass.getDeclaredConstructor(long.class);
            //访问public protect default构造方法,这句话可以不用写
            //constructorDefault.setAccessible(true);
            System.out.println("有参default构造方法的对象:" + peopleclass.getDeclaredConstructor(long.class));
            Object peopleobject4 = constructorDefault.newInstance(185);
            System.out.println("创建people4对象新实例: " + peopleobject4);

        } catch (ClassNotFoundException | NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

打印log:

java 复制代码
**********************获取反射后类对象*********************************
反射类对象: class com.example.javademo.reflect.People


**********************打印所有构造方法*********************************
com.example.javademo.reflect.People(long)
private com.example.javademo.reflect.People(int,java.lang.String)
public com.example.javademo.reflect.People(java.lang.String)
public com.example.javademo.reflect.People()


**************第一种:通过反射调用默认无参构造方法并创建people类实例对象***************************
无参构造方法的对象:public com.example.javademo.reflect.People()
调用了public无参构造方法 执行完毕
创建people1对象新实例: com.example.javademo.reflect.People@2a139a55


**************第二种:通过反射调用public有参构造方法并创建People类实例对象***************************
调用public有参构造方法 String:name = 甲方
创建people2对象新实例: com.example.javademo.reflect.People@15db9742


**************第三种:通过反射调用private有参构造方法并创建People类实例对象***************************
有参private私有构造方法的对象:private com.example.javademo.reflect.People(int,java.lang.String)
调用private有参数构造方法 age :20  sex:men
创建people3对象新实例: com.example.javademo.reflect.People@6d06d69c


**************第四种:通过反射调用default有参构造方法并创建People类实例对象***************************
有参default构造方法的对象:com.example.javademo.reflect.People(long)
调用default有参数构造方法 height :185
创建people4对象新实例: com.example.javademo.reflect.People@7852e922

通过上面的例子,总结:

  1. 反射Class对象, 反射Constructor对象, 反射类实例对象 是3个不同的对象,如果刚开始不熟悉API,建议分开创建;

  2. getConstructor() 和 getDeclaredConstructor(类<?>... parameterTypes)方法的区别 前者是获取public类型构造方法的Constructor对象,后者是获取全类型(public protect default private)类型的构造方法的Constructor对象

  3. 反射私有构造方法的时候,一定要setAccessible(true),不然会报错java.lang.IllegalAccessException错误 * true表示:禁止java语言使用时安全检查,暴力访问

  4. 调用Constructor类中newInstance()这个方法时,注意newInstance没有带参数(不带参数可以不写,或写成null 都可以行) 此方法内部实际上调用了无参数构造方法,必须保证无参构造存在才可以,否则会抛出java.lang.InstantiationException异常。

3.3 反射调用类中方法

|---------------------------------------------------------------------------|------------------------------------|
| 方法名 | 说明 |
| public Method[] getMethods() | 获取所有"公有方法";(包含了父类的方法也包含Object类) |
| public Method getMethod(String name,Class<?>... parameterTypes) | 获取指定参数的公共成员方法 对象。 |
| public Method[] getDeclaredMethods() | 获取所有的成员方法,包括私有的(不包括继承的) |
| public Method getDeclaredMethod(String name,Class<?>... parameterTypes) | 获取指定参数的方法对象 |
| public Object invoke(Object obj,Object... args) | 在具有指定参数的 方法对象上调用此 方法对象表示的底层方法。 |

我们看看这个例子

java 复制代码
package com.example.javademo.reflect;

/**
 * author : me
 * date   : 22-10-20下午12:03
 * desc   : 供反射调用的方法类
 * version: 1.0
 */
public class MethodTest {

    public MethodTest() {
        System.out.println("调用  MethodTest默认的共有构造方法");
    }

    String fruit = "苹果";
    int milliliter = 0;
    char game = 'X';
    String mood = "开心";

    public void eat(String fruit) {
        this.fruit = fruit;
        System.out.println("调用public成员方法 eat  吃的水果为 :" + fruit);
    }

    int drink(int milliliter) {
        this.milliliter = milliliter;
        System.out.println("调用default成员方法 drink 喝水毫升量 :" + milliliter);
        return milliliter;
    }

    protected void play(char game) {
        this.game = game;
        System.out.println("调用protected成员方法 play 玩游戏 :" + game);
    }

    private String happy(String mood) {
        this.mood = mood;
        System.out.println("调用private成员方法 happy 今日心情:" + mood);
        return mood;
    }

}

反射测试类

java 复制代码
package com.example.javademo.reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * author : me
 * date   : 22-10-20下午12:03
 * desc   : 反射MethodTest类中的成员方法实现类
 * version: 1.0
 */
public class ReflectMethod {

    public static void main(String[] args) {

        try {
            System.out.println("**********************获取反射后类对象*********************************");
            //获取方法类的class对象
            Class methodClass = Class.forName("com.example.javademo.reflect.MethodTest");
            System.out.println("反射类对象 :" + methodClass);

            System.out.println("**********************获取反射类中所有的成员方法**************************");
            //返回一个"方法数组对象":反射类或接口中所有声明的方法,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。
            Method[] methods = methodClass.getDeclaredMethods();
            for (Method m : methods) {
                System.out.println(m);
            }

            System.out.println("***************第一种*******调用反射类中public成员方法**************************");
            //返回一个方法对象,它表示此表示的类或接口的指定声明的方法对象。
            //第一个参数为方法名,第二个可变参数 : 为该方法传入的参数
            Method methodPublic = methodClass.getDeclaredMethod("eat", String.class);
            //PUBLIC :1     PRIVATE:2    PROTECTED:4   PACKAGE(default):8
            System.out.println("打印该public方法的修饰符 :" + methodPublic.getModifiers());
            System.out.println("打印该public方法对象 表示方法的名称: "+methodPublic.getName());
            System.out.println("打印该public方法对象 可执行文件的形式参数的数量 :" + methodPublic.getParameterCount());

            //实例化一个MethodTest类对象,每个类有一个默认的无参构造方法,可以写或不写
            //但是如果类中有带参数的构造方法,那无参构造方法是必须要写出来的,不然new对象的时候,就会报错
            MethodTest objMethodTest = (MethodTest)methodClass.getDeclaredConstructor().newInstance();
            System.out.println("MethodTest类对象 :" + objMethodTest);
            //invoke方法 第一个参数: 从底层方法被调用的对象   如果底层方法是静态的,则第一个参数obj对象传递null。
            //第二个参数: 该方法调用的参数
            //如果方法正常完成,则返回的值将返回给调用者.
            Object objectreturn1 = methodPublic.invoke(objMethodTest, "菠萝");
            //eat方法返回值为 null
            System.out.println("eat方法 返回值为 :" + objectreturn1);


            System.out.println("***************第二种*******调用反射类中private成员方法**************************");
            Method methodPrivate = methodClass.getDeclaredMethod("happy", String.class);
            System.out.println("打印该private方法的修饰符 :" + methodPrivate.getModifiers());
            System.out.println("打印该private方法对象 表示方法的名称: "+methodPrivate.getName());
            System.out.println("打印该public方法对象 可执行文件的形式参数的数量 :" + methodPrivate.getParameterCount());

            //如果是访问私有方法,则要加上这句话,禁止java语言使用时安全检查
            methodPrivate.setAccessible(true);
            Object objectreturn2 = methodPrivate.invoke(objMethodTest, "超级nice");
            System.out.println("happy方法 返回值为:"+objectreturn2);


            System.out.println("***************第三种*******调用反射类中protected成员方法**************************");
            Method methodProtected = methodClass.getDeclaredMethod("play", char.class);
            System.out.println("打印该protected方法的修饰符 :" + methodProtected.getModifiers());
            System.out.println("打印该protected方法对象 表示方法的名称: "+methodProtected.getName());
            System.out.println("打印该protected方法对象 可执行文件的形式参数的数量 :" + methodProtected.getParameterCount());

            Object objectreturn3 = methodProtected.invoke(objMethodTest, 'Y');
            System.out.println("play方法 返回值为:"+objectreturn3);


            System.out.println("***************第四种*******调用反射类中default成员方法**************************");
            Method methodDefault = methodClass.getDeclaredMethod("drink", int.class);
            System.out.println("打印该protected方法的修饰符 :" + methodDefault.getModifiers());
            System.out.println("打印该protected方法对象 表示方法的名称: "+methodDefault.getName());
            System.out.println("打印该protected方法对象 可执行文件的形式参数的数量 :" + methodDefault.getParameterCount());

            Object objectreturn4 = methodDefault.invoke(objMethodTest, 180);
            System.out.println("drink方法 返回值为:"+objectreturn4);

        } catch (ClassNotFoundException | NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

打印log:

java 复制代码
**********************获取反射后类对象*********************************
反射类对象 :class com.example.javademo.reflect.MethodTest


**********************获取反射类中所有的成员方法**************************
public void com.example.javademo.reflect.MethodTest.eat(java.lang.String)
private java.lang.String com.example.javademo.reflect.MethodTest.happy(java.lang.String)
protected void com.example.javademo.reflect.MethodTest.play(char)
int com.example.javademo.reflect.MethodTest.drink(int)


***************第一种*******调用反射类中public成员方法**************************
打印该public方法的修饰符 :1
打印该public方法对象 表示方法的名称: eat
打印该public方法对象 可执行文件的形式参数的数量 :1
调用  MethodTest默认的共有构造方法
MethodTest类对象 :com.example.javademo.reflect.MethodTest@15db9742
调用public成员方法 eat  吃的水果为 :菠萝
eat方法 返回值为 :null


***************第二种*******调用反射类中private成员方法**************************
打印该private方法的修饰符 :2
打印该private方法对象 表示方法的名称: happy
打印该public方法对象 可执行文件的形式参数的数量 :1
调用private成员方法 happy 今日心情:超级nice
happy方法 返回值为:超级nice


***************第三种*******调用反射类中protected成员方法**************************
打印该protected方法的修饰符 :4
打印该protected方法对象 表示方法的名称: play
打印该protected方法对象 可执行文件的形式参数的数量 :1
调用protected成员方法 play 玩游戏 :Y
play方法 返回值为:null


***************第四种*******调用反射类中default成员方法**************************
打印该protected方法的修饰符 :0
打印该protected方法对象 表示方法的名称: drink
打印该protected方法对象 可执行文件的形式参数的数量 :1
调用default成员方法 drink 喝水毫升量 :180
drink方法 返回值为:180

代码中基本都做了注释,通过上面的例子,总结:

  1. 反射class对象, 反射Method对象 反射类实例对象 是3个不同的对象,要区分开

  2. 在反射调用private方法时,一定要禁止java语言使用时安全检查,要setAccessible(true),其他修饰符方法可以不用加这句话

  3. (反射Method对象).invoke(Object obj, Object ...args) 方法参数的理解 :

  4. 第一个参数为 通过用反射方法构造出来的 反射类实例对象

  5. 第二个可变参数为: 该方法需要传入的参数

  6. 如果方法正常完成,则将返回值返回

3.4 反射类中的成员变量

|-------------------------------------------------|-------------------------|
| 方法 | 说明 |
| Field[] getFields() | 获取所有的"公有字段" |
| public Field getField(String fieldName) | 获取指定参数的共有字段 |
| .Field[] getDeclaredFields() | 获取所有字段,包括:私有、受保护、默认、公有; |
| public Field getDeclaredField(String fieldName) | 获取某个字段(包括私有的) |
| set(Object obj, Object value) | 将指定Field对象 设置为指定的新值 |

我们通过Demo讲解:

java 复制代码
package com.example.javademo.reflect;

/**
 * author : me
 * date   : 22-10-20下午4:06
 * desc   : 供反射调用的FieldTest类
 * version: 1.0
 */
public class FieldTest {

    public FieldTest() {
        System.out.println("调用FieldTest 默认无参构造方法");
    }

    public int age = 18;
    private String name = "张三";
    String sex = "男";
    protected String phoneNum = "123456789";

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }

    public String getSex() {
        return sex;
    }

    public String getPhoneNum() {
        return phoneNum;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public void setPhoneNum(String phoneNum) {
        this.phoneNum = phoneNum;
    }

    @Override
    public String toString() {
        return "FieldTest{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", phoneNum='" + phoneNum + '\'' +
                '}';
    }

反射测试类:

java 复制代码
package com.example.javademo.reflect;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * author : me
 * date   : 22-10-20下午4:11
 * desc   : 反射FiledTest类中各字段(Field)的实现类
 * version: 1.0
 */
public class ReflectField {

    public static void main(String[] args) {

        //用正常的new对象的方式,给字段赋值,打印对象值, 测试用
       /* FieldTest objcetFieldTest = new FieldTest();
        objcetFieldTest.setAge(15);
        objcetFieldTest.setName("xiaomao");
        objcetFieldTest.setPhoneNum("12345678");
        objcetFieldTest.setSex("男");
        System.out.println(objcetFieldTest.toString());*/


        //通过反射的方式,修改字段值
        try {
            //通过反射获取Class对象
            Class FieldTestClass = Class.forName("com.example.javademo.reflect.FieldTest");

            //用反射的方式来修改字段(成员变量)的值
            System.out.println("************获取所有的字段(包括共有、私有、受保护、默认的)********************");
            Field[] fields = FieldTestClass.getDeclaredFields();
            for (Field f : fields) {
                System.out.println(f);
            }

            //用反射的方式来构造FieldTestl类对象
            FieldTest objFieldTest = (FieldTest) FieldTestClass.getDeclaredConstructor().newInstance();

            System.out.println("*************反射获取public字段并调用***********************************");
            //返回一个 Field对象,它表示的类或接口的指定已声明字段对象。
            Field fieldPublic =  FieldTestClass.getDeclaredField("age");
            System.out.println("该字段的修饰符 :" + fieldPublic.getModifiers());
            System.out.println("该字段的名称 :" + fieldPublic.getName());

            /*
            * 获取字段的值:有 getInt(Object obj) getLong(Object obj) get(Object obj)等方法
            *
            * obj :为通过反射方法 构造出来的类对象
            * */

            //获取int类型 实例字段的默认值
            System.out.println("通过反射方式获取age默认值 : " + fieldPublic.getInt(objFieldTest));


           /* 设置字段的值:
 		    Field  public void set(Object obj,Object value):
 			参数说明:
				1.obj: 通过反射方法 构造出来的类对象
 				2.value:要为字段设置的值;
 		   */
            fieldPublic.set(objFieldTest, 20);
            //通过反射的方式调用 getAge() 方法
            Method method = FieldTestClass.getDeclaredMethod("getAge");
            Object objectReturn =  method.invoke(objFieldTest);
            System.out.println("通过反射方式set新值之后, age的值 :" + objectReturn);



            System.out.println("*************反射获取private字段并调用***********************************");
            //在项目实际中,因为私有变量和方法,无法在外部类去调用它们,所以反射在这点上就派上用场了
            Field fieldPrivate = FieldTestClass.getDeclaredField("name");

            //private字段一定要调用setAccessible(true)  禁止java语言使用时安全检查
            fieldPrivate.setAccessible(true);
            System.out.println("通过反射方式获取age默认值 : " + fieldPrivate.get(objFieldTest));

            //修改private变量name的值
            fieldPrivate.set(objFieldTest, "李四");

            //通过反射的方式调用 getName() 方法
            Method objectMethod = FieldTestClass.getDeclaredMethod("getName");
            Object objectReturn1 =  objectMethod.invoke(objFieldTest);
            System.out.println("通过反射方式set新值之后, name的值 :" + objectReturn1);


        } catch (ClassNotFoundException | NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

打印log:

java 复制代码
************获取所有的字段(包括共有、私有、受保护、默认的)********************
public int com.example.javademo.reflect.FieldTest.age
private java.lang.String com.example.javademo.reflect.FieldTest.name
java.lang.String com.example.javademo.reflect.FieldTest.sex
protected java.lang.String com.example.javademo.reflect.FieldTest.phoneNum
调用FieldTest 默认无参构造方法


*************反射获取public字段并调用***********************************
该字段的修饰符 :1
该字段的名称 :age
通过反射方式获取age默认值 : 18
通过反射方式set新值之后, age的值 :20


*************反射获取private字段并调用***********************************
通过反射方式获取age默认值 : 张三
通过反射方式set新值之后, name的值 :李四

通过例子,总结如下:

  1. 反射class对象, 反射Field对象 反射类实例对象 是3个不同的对象,要区分开

  2. 在反射调用private字段时,一定要禁止java语言使用时安全检查,要setAccessible(true)

  3. Field public void set(Object obj,Object value):

参数说明: 1.obj: 通过反射方法 构造出来的类对象

2.value:要为字段设置的值;

3.5 反射类中静态方法和变量

怎么修改static修饰的变量呢?静态变量和方法是在类的实例化之前就进行了初始化(类的初始化阶段),静态变量和方法是从属于类本身的,跟new出来的具体对象无关,所以我们获取变量就不需要传入对象,直接传入null即可。

Demo如下:

java 复制代码
package com.example.javademo.reflect;

/**
 * author : me
 * date   : 22-10-24 上午10:33
 * desc   : 静态变量和方法 类
 * version: 1.0
 */
public class StaticFieldTest {

    public StaticFieldTest() {
        System.out.println("调用 StaticFieldTest 无参构造方法");
    }
    //静态变量
    public static int age = 15;

    public static void setAge(int age) {
        StaticFieldTest.age = age;
    }

    //静态方法
    public static int getAge() {
        return age;
    }
}

反射测试类:

java 复制代码
package com.example.javademo.reflect;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * author : me
 * date   : 22-10-24上午10:37
 * desc   : 反射类中的静态变量和方法
 * version: 1.0
 */
public class ReflectStatic {

    public static void main(String[] args) {

        try {
            //获取 StaticFieldTest  class对象
            Class staticClazz = Class.forName("com.example.javademo.reflect.StaticFieldTest");

            //获取静态Field对象
            Field staticField = staticClazz.getDeclaredField("age");
            //因为是pulic修饰符,这句话也可以不用写
            staticField.setAccessible(true);

            //通过set方法,修改值,  静态变量 是从属于类,可以不用传入类实例对象,直接传入null
            staticField.set(null, 25);

            //验证修改后的值
            Method agetet = staticClazz.getDeclaredMethod("getAge");
            // 静态方法 是从属于类, 可以不用传入类实例对象,直接传入null
            int agetest = (int) agetet.invoke(null);
            System.out.println("**********************打印修改后的age值**************************");
            System.out.println(agetest);

        } catch (ClassNotFoundException | NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

打印结果:

java 复制代码
**********************打印修改后的age值**************************
25

总结如下:

  1. 对于static 变量和方法,因为它是从属于类本身,在类的实例化之前就进行了初始化,当传入obj对象参数的时候,直接传入null即可。

4. 反射在Android中的应用

4.1 反射实现获取顶层activity的名称

如前言背景中的问题描述,有了上面的理论知识做为基础,实现代码如下:

java 复制代码
/**
     * @description: 通过反射方式获取 顶层activity名称
     * @date:  22-10-24 下午3:29
     * @author: me
     * @param: null
     * @return: boolean 如果是目标activity 则返回true
    */
    public boolean isTopTargetActivity() {
        try {
            //通过包名和类名反射获取 class 对象
            Class activityThreadClass = Class.forName("android.app.ActivityThread");

            //第二种: 反射创建 activiyThread对象  反射调用静态方法,第一个参数obj对象传递 null
            Object activityThreadObj = activityThreadClass.getMethod("currentActivityThread").invoke(null);

            Log.e("test", "====activityThreadObj: "+activityThreadObj);
            //获取 mActivities Field对象  final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
            Field activitiesField = activityThreadClass.getDeclaredField("mActivities");
            //禁止Java语言访问使用时进行检查
            activitiesField.setAccessible(true);
            //返回该所表示的字段的值 Field
            Map activities = (Map) activitiesField.get(activityThreadObj);
            for (Object activityClientRecord : activities.values()) {
                //获取 ActivityClientRecord class对象
                Class activityRecordClass = activityClientRecord.getClass();
                //获取 boolean paused 字段 对象
                Field pausedField = activityRecordClass.getDeclaredField("paused");
                //允许暴力访问,禁止java语言运行时安全检查
                pausedField.setAccessible(true);
                //Activity onResume的判断条件
                if (!pausedField.getBoolean(activityClientRecord)) {
                    //获取 Activity activity field对象
                    Field activityField = activityRecordClass.getDeclaredField("activity");
                    activityField.setAccessible(true);
                    //获取当前显示的activity的对象
                    Activity activity = (Activity) activityField.get(activityClientRecord);
                    //获取当前activity的名称
                    String className = activity.getClass().toString();
                    Log.e("test", "====通过反射获取的className===="+className);
                    if ("cn.com.test.activity.MainActivity".equals(className)) {
                        return true;
                    }
                }
            }
        } catch (ClassNotFoundException e) {
            Log.e("test", "======ClassNotFoundException===="+e.getMessage());
        } catch (InvocationTargetException e) {
            Log.e("test", "======InvocationTargetException===="+e.getMessage());
        } catch (NoSuchMethodException e) {
            Log.e("test", "======NoSuchMethodException===="+e.getMessage());
        } catch (NoSuchFieldException e) {
            Log.e("test", "======NoSuchFieldException===="+e.getMessage());
        } catch (IllegalAccessException e) {
            Log.e("test", "======IllegalAccessException===="+e.getMessage());
        }
        return false;
    }
4.2 反射调用SystemProperties中set get方法

再者,比如第三方应用是无法直接调用 SystemProperties中 get set 方法,我们也可以通过反射来实现, 这个工具类已经实现好,可以拿去直接用:

java 复制代码
public class SystemPropertiesUtils {

    private static final String TAG = "SystemPropertiesUtils";

    private static Class<?> mClassType = null;
    private static Method mGetMethod = null;
    private static Method mGetIntMethod = null;
    private static Method mGetBooleanMethod = null;


    private static Class<?> getSystemPropertiesClass() throws ClassNotFoundException {
        if (mClassType == null) {
            mClassType = Class.forName("android.os.SystemProperties");
        }
        return mClassType;
    }

    private static Method getMethod() throws Exception {
        if (mGetMethod == null) {
            Class clazz = getSystemPropertiesClass();
            mGetMethod = clazz.getDeclaredMethod("get", String.class);
        }
        return mGetMethod;
    }

    private static Method getIntMethod() throws Exception {
        if (mGetIntMethod == null) {
            Class clazz = getSystemPropertiesClass();
            mGetIntMethod = clazz.getDeclaredMethod("getInt", String.class, int.class);
        }
        return mGetIntMethod;
    }

    private static Method getBooleanMethod() throws Exception {
        if (mGetBooleanMethod == null) {
            Class clazz = getSystemPropertiesClass();
            mGetBooleanMethod = clazz.getDeclaredMethod("getBoolean", String.class, boolean.class);
        }
        return mGetBooleanMethod;
    }

    public static String get(String key, String def) {
        try {
            String value = (String) getMethod().invoke(null, key);
            if (!TextUtils.isEmpty(value)) {
                return value;
            }
        } catch (Exception e) {
            Log.d(TAG, "Unable to read system properties");
        }

        return def;
    }

    public static int getInt(String key, int def) {
        int value = def;
        try {
            value = (int) getIntMethod().invoke(null, key, def);
        } catch (Exception e) {
            Log.d(TAG, "Unable to read system properties");
        }
        return value;
    }

    public static boolean getBoolean(String key, boolean def) {
        boolean value = def;
        try {
            value = (Boolean) getBooleanMethod().invoke(null, key, def);
        } catch (Exception e) {
            Log.d(TAG, "Unable to read system properties");
        }
        return value;
    }
}
4.3 通过反射创建Bitmap缩略图
java 复制代码
public static Bitmap createVideoThumbnail(String filePath) {
        // MediaMetadataRetriever is available on API Level 8
        // but is hidden until API Level 10
        Class<?> clazz = null;
        Object instance = null;
        try {
            clazz = Class.forName("android.media.MediaMetadataRetriever");
            instance = clazz.newInstance();

            Method method = clazz.getMethod("setDataSource", String.class);
            method.invoke(instance, filePath);

            // The method name changes between API Level 9 and 10.
            if (Build.VERSION.SDK_INT <= 9) {
                return (Bitmap) clazz.getMethod("captureFrame").invoke(instance);
            } else {
                byte[] data = (byte[]) clazz.getMethod("getEmbeddedPicture").invoke(instance);
                if (data != null) {
                    Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
                    if (bitmap != null) return bitmap;
                }
                return (Bitmap) clazz.getMethod("getFrameAtTime").invoke(instance);
            }
        } catch (IllegalArgumentException ex) {
            // Assume this is a corrupt video file
        } catch (RuntimeException ex) {
            // Assume this is a corrupt video file.
        } catch (InstantiationException e) {
            Log.e(TAG, "createVideoThumbnail", e);
        } catch (InvocationTargetException e) {
            Log.e(TAG, "createVideoThumbnail", e);
        } catch (ClassNotFoundException e) {
            Log.e(TAG, "createVideoThumbnail", e);
        } catch (NoSuchMethodException e) {
            Log.e(TAG, "createVideoThumbnail", e);
        } catch (IllegalAccessException e) {
            Log.e(TAG, "createVideoThumbnail", e);
        } finally {
            try {
                if (instance != null) {
                    clazz.getMethod("release").invoke(instance);
                }
            } catch (Exception ignored) {
            }
        }
        return null;
    }

MediaMetadataRetriever.java 类中有个public无参构造方法, 可以通过反射方式clazz.getDeclaredConstructor().newInstance() 拿到这个 类对象

接下来,就是获取Method Field 对象 再通过invoke set 方法去修改方法 变量的值。

5. 总结

反射在Andorid中开发应用的比较多,Class对象, Constructor 对象,Method 对象 Field对象各自的常用API要理解并熟练运用, 结合源码多阅读多仿写,相信你也可以carry住反射。

6. 更新

在代码编译的时候,有Warnning警告:

java 复制代码
Method getVolumeList = clz.getMethod("getVolumeList", null);
                                                                  ^
对于 varargs 调用, 应使用 Class
对于非 varargs 调用, 应使用 Class[], 这样也可以抑制此警告



警告: 最后一个参数使用了不准确的变量类型的 varargs 方法的非 varargs 调用; 
StorageVolume[] result = (StorageVolume[]) getVolumeList.invoke(storageManager, null);
                                                                                            
对于 varargs 调用, 应使用 Object
对于非 varargs 调用, 应使用 Object[], 这样也可以抑制此警告

修改方法如下:

警告: 最后一个参数使用了不准确的变量类型的 varargs 方法的非 varargs 调用;

javac\] 对于 varargs 调用,应使用 Java.lang.Object \[javac\] 对于非 varargs 调用,应使用 java.lang.Object\[\],这样也可以抑制此警告程序是一样的,在jdk1.4下可以编译通过,但在1.5就不行。 ```java Method getVolumeList = clz.getMethod("getVolumeList", new Class[0]); StorageVolume[] result = (StorageVolume[]) getVolumeList.invoke(storageManager, new Object[]{}); ``` 关于StorageManager.java 文件中的反射方法 ```java /** * 通过反射 返回已挂载成功的外设的StorageVolume数组 */ public StorageVolume[] getVolumeList(StorageManager storageManager) { try { Class clz = Class.forName("android.os.storage.StorageManager"); Method getVolumeList = clz.getMethod("getVolumeList", new Class[0]); StorageVolume[] result = (StorageVolume[]) getVolumeList.invoke(storageManager, new Object[]{}); return result; } catch (Exception e) { e.printStackTrace(); } return null; } StorageVolume[] result = getVolumeList(manager); mVolume = manager.findVolumeByUuid(result[1].getUuid()); // get StorageManager StorageManager sm = null; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { sm = getSystemService(StorageManager.class); } if (sm != null) { List volumes = null; try { volumes = (List) sm.getClass().getMethod("getVolumes").invoke(sm); } catch (Exception e) { Log.e(TAG, "exception: " + e.getMessage()); } if (volumes != null) { for (VolumeInfo vol : volumes) { DiskInfo diskInfo = vol.getDisk(); if (diskInfo != null && diskInfo.isSd()) { // is SD Log.d(TAG, "sdcard disk id: " + diskInfo.getId()); try { // format sm.getClass().getMethod("partitionPublic", String.class).invoke(sm, diskInfo.getId()); } catch (Exception e) { Log.e(TAG, "partitionPublic exception: " + e.getMessage()); } } } } } ```

相关推荐
2501_9400940218 小时前
emu系列模拟器最新汉化版 安卓版 怀旧游戏模拟器全集附可运行游戏ROM
android·游戏·安卓·模拟器
下位子18 小时前
『OpenGL学习滤镜相机』- Day9: CameraX 基础集成
android·opengl
参宿四南河三20 小时前
Android Compose SideEffect(副作用)实例加倍详解
android·app
火柴就是我21 小时前
mmkv的 mmap 的理解
android
没有了遇见21 小时前
Android之直播宽高比和相机宽高比不支持后动态获取所支持的宽高比
android
shenshizhong21 小时前
揭开 kotlin 中协程的神秘面纱
android·kotlin
vivo高启强1 天前
如何简单 hack agp 执行过程中的某个类
android
沐怡旸1 天前
【底层机制】 Android ION内存分配器深度解析
android·面试
你听得到111 天前
肝了半个月,我用 Flutter 写了个功能强大的图片编辑器,告别image_cropper
android·前端·flutter
KevinWang_1 天前
Android 原生 app 和 WebView 如何交互?
android