广告位联系
返回顶部
分享到

Andorid开发中反射机制的介绍

Android 来源:互联网 作者:佚名 发布时间:2022-11-05 20:25:36 人浏览
摘要

在andorid开发中,经常遇见在某些工具类中没有Context上下文对象时,一些系统服务的代理对象无法创建出来,举个例子:比如在源码(framework/base/graphics/java/android/graphics)路径下的Canvas.j

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

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

1

2

3

4

5

//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 类

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

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

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

1

2

3

4

5

6

7

8

9

10

11

12

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(“类的全路径”); 推荐此种写法

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

package com.example.javademo.reflect;

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的静态方法,安全性强, 参数为:类包名+类名

运行结果如下:

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

package com.example.javademo.reflect;

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 );

    }

}

反射测试类:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

package com.example.javademo.reflect;

import java.lang.reflect.Constructor;

import java.lang.reflect.InvocationTargetException;

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:

**********************获取反射后类对象*********************************
反射类对象: 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) 在具有指定参数的 方法对象上调用此 方法对象表示的底层方法。

我们看看这个例子

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

package com.example.javademo.reflect;

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;

    }

}

反射测试类

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

package com.example.javademo.reflect;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

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:

**********************获取反射后类对象*********************************
反射类对象 :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) 方法参数的理解 :

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

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

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

3.4 反射类中的成员变量

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

我们通过Demo讲解:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

package com.example.javademo.reflect;

/**

 * 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 + '\'' +

                '}';

    }

反射测试类:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

package com.example.javademo.reflect;

import java.lang.reflect.Field;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

/**

 * 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:

************获取所有的字段(包括共有、私有、受保护、默认的)********************
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如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

package com.example.javademo.reflect;

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;

    }

}

反射测试类:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

package com.example.javademo.reflect;

import java.lang.reflect.Field;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

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();

        }

    }

打印结果:

**********************打印修改后的age值**************************
25

总结如下:

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

4. 反射在Android中的应用

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

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

/**

    * @description: 通过反射方式获取 顶层activity名称

    * @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 方法,我们也可以通过反射来实现, 这个工具类已经实现好,可以拿去直接用:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

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缩略图

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

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住反射。


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。

您可能感兴趣的文章 :

原文链接 : https://blog.csdn.net/u012514113/article/details/127443659
相关文章
  • Flow解决背压问题的方法介绍

    Flow解决背压问题的方法介绍
    随着时间的推移,越来越多的主流应用已经开始全面拥抱Kotlin,协程的引入,Flow的诞生,给予了开发很多便捷,作为协程与响应式编程结合
  • Andorid开发中反射机制的介绍
    在andorid开发中,经常遇见在某些工具类中没有Context上下文对象时,一些系统服务的代理对象无法创建出来,举个例子:比如在源码(framewo
  • Flutter加载图片的多样玩法汇总

    Flutter加载图片的多样玩法汇总
    加载本地图片 在项目目录下创建assets文件夹,再在其文件夹下创建images文件夹,后面将需要的图片复制到其中即可 在pubspec.yaml文件中添加引
  • Kotlin的Collection与Sequence操作异同点介绍

    Kotlin的Collection与Sequence操作异同点介绍
    在Android开发中,集合是我们必备的容器,Kotlin的标准库中提供了很多处理集合的方法,而且还提供了两种基于容器的工作方式:Collection 和
  • 实现一个Kotlin函数类型方法

    实现一个Kotlin函数类型方法
    接口与函数类型 业务开发中,经常会有实现一个函数式接口(即接口只有一个方法需要实现)的场景,大家应该都会不假思索的写出如下代
  • Android10 App启动Activity源码分析
    ActivityThread的main方法 让我们把目光聚焦到ActivityThread的main方法上。 ActivityThread的源码路径为/frameworks/base/core/java/android/app/ActivityThread。 1 2
  • Android10客户端事务管理ClientLifecycleManager源码解析

    Android10客户端事务管理ClientLifecycleManager源码解析
    在Android 10 App启动分析之Activity启动篇(二)一文中,简单地介绍了Activity的生命周期管理器是如何调度Activity进入onCreate生命周期的流程。这
  • Kotlin对象的懒加载方式by lazy与lateinit异同介绍

    Kotlin对象的懒加载方式by lazy与lateinit异同介绍
    属性或对象的延时加载是我们相当常用的,一般我们都是使用 lateinit 和 by lazy 来实现。 他们两者都是延时初始化,那么在使用时那么他们两
  • Android类加载流程分析

    Android类加载流程分析
    本文分析的代码基于Android8.1.0源码。 流程分析 从loadClass开始,我们来看下Android中类加载的流程 /libcore/ojluni/src/main/java/java/lang/ClassLoader.ja
  • Android实现读写USB串口数据的代码

    Android实现读写USB串口数据的代码
    最近在研究USB方面的内容;先后做了关于Android读写HID、串口设备的DEMO。本文比较简单,主要介绍的是Android实现读取串口数据的功能 废话不
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计