吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 5744|回复: 12
收起左侧

[Android 原创] Xposed反射字段流程分析

[复制链接]
luoyesiqiu 发表于 2019-8-13 16:59

XposedBridge源码中,反射字段的方法封装在de.robv.android.xposed.XposedHelpers类里面.下面来看看Xposed是如何获取和设置字段的值的

获取字段的值

获取字段的值有许多个方法,有获取基本类型字段的值的方法(getIntField,getLongField,getDoubleField...),也有获取对象类型字段的值的方法.它们的实现大同小异.我们以获取对象类型字段的值为例,来分析Xposed是如何反射获取字段对象的值.这个功能在XposedHelpers类的getObjectField方法

public static Object getObjectField(Object obj, String fieldName) {
    try {
        return findField(obj.getClass(), fieldName).get(obj);
    } catch (IllegalAccessException e) {
        // should not happen
        XposedBridge.log(e);
        throw new IllegalAccessError(e.getMessage());
    } catch (IllegalArgumentException e) {
        throw e;
    }
}

getObjectField有两个参数:一个是要获取的字段的所属对象,另一个是字段的名称.
getObjectField主要调用findField方法和调用findField方法返回的对象的set方法,去看看findField方法:

public static Field findField(Class<?> clazz, String fieldName) {
    String fullFieldName = clazz.getName() + '#' + fieldName;

    if (fieldCache.containsKey(fullFieldName)) {
        Field field = fieldCache.get(fullFieldName);
        if (field == null)
            throw new NoSuchFieldError(fullFieldName);
        return field;
    }

    try {
        Field field = findFieldRecursiveImpl(clazz, fieldName);
        field.setAccessible(true);
        fieldCache.put(fullFieldName, field);
        return field;
    } catch (NoSuchFieldException e) {
        fieldCache.put(fullFieldName, null);
        throw new NoSuchFieldError(fullFieldName);
    }
}

findField方法的开始,先拼接一个完整的字段名称.接下来根据完整字段名判断缓存中是否存在这个字段,如果有就从缓存中取出这个字段,就不用在查找了,这样子做可以提高反射速率.如果从缓存取出的为空值,代表这个字段之前找过了,找不到,并抛出异常.

缓存名叫fieldCache,它是一个HashMap.

private static final HashMap<String, Field> fieldCache = new HashMap<>();

如果缓存中没有这个字段呢,调用findFieldRecursiveImpl查找字段:

private static Field findFieldRecursiveImpl(Class<?> clazz, String fieldName) throws NoSuchFieldException {
    try {
        return clazz.getDeclaredField(fieldName);
    } catch (NoSuchFieldException e) {
        while (true) {
            clazz = clazz.getSuperclass();
            if (clazz == null || clazz.equals(Object.class))
                break;

            try {
                return clazz.getDeclaredField(fieldName);
            } catch (NoSuchFieldException ignored) {}
        }
        throw e;
    }
}

findFieldRecursiveImpl方法首先在传进来的Class类中查找有没有这个字段,如果有返回Field对象.如果没有,就开一个死循环从它的父类中找,如果这个类没有父类或者一直找到Object类也找不到这个方法,说明这个类真的没有要找的字段,抛出一个异常.如果在它的父类中找到了这个字段,返回Field对象.

findFieldRecursiveImpl调用结束,将会回到findField的后半段:

public static Field findField(Class<?> clazz, String fieldName) {
    ......
    try {
        Field field = findFieldRecursiveImpl(clazz, fieldName);
        field.setAccessible(true);
        fieldCache.put(fullFieldName, field);
        return field;
    } catch (NoSuchFieldException e) {
        fieldCache.put(fullFieldName, null);
        throw new NoSuchFieldError(fullFieldName);
    }
}

如果findFieldRecursiveImpl没有抛出异常,那么将这个字段加入缓存,并将这个字段返回.如果如果findFieldRecursiveImpl抛出异常,也放入缓存,但是放入的是空值.

findField方法结束,回到最初的getObjectField方法,调用get方法,来返回字段存储的对象值

设置字段的值

和获取字段的值的方法一样,设置字段的方法也是有许多,它们的实现也是大同小异.我们来看看其中的setObjectField方法:

public static void setObjectField(Object obj, String fieldName, Object value) {
    try {
        findField(obj.getClass(), fieldName).set(obj, value);
    } catch (IllegalAccessException e) {
        // should not happen
        XposedBridge.log(e);
        throw new IllegalAccessError(e.getMessage());
    } catch (IllegalArgumentException e) {
        throw e;
    }
}

setObjectField方法和上面讲解的getObjectField几乎相同,也是通过调用findField方法得到Field类对象.不同的是,setObjectField得到Field对象后调用的是它的set方法,来对字段的值进行设置.

免费评分

参与人数 7吾爱币 +4 热心值 +5 收起 理由
houmiao + 1 我很赞同!
笙若 + 1 + 1 谢谢@Thanks!
zhao. + 1 热心回复!
少林大虾 + 1 热心回复!
fei8255 + 1 + 1 用心讨论,共获提升!
hayb + 1 我很赞同!
一剑断长生 + 1 我很赞同!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

whyerect 发表于 2019-8-13 18:54
好深奥啊!
好市民刘青云O 发表于 2019-8-13 20:47
一剑断长生 发表于 2019-8-14 00:22
ColoThor 发表于 2019-8-14 09:13
其实就是java的反射
佚丶名 发表于 2019-8-14 10:48
看不太懂 还是要支持
wycmxg 发表于 2019-8-14 11:50
看不太懂 支持支持
fhxylang 发表于 2019-8-14 13:50
技术贴,看得迷糊,大致意思懂了
BlueTears_ 发表于 2019-8-15 01:28
看不懂...
wowowowo 发表于 2019-8-15 09:20
大佬666,就是看不懂
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 20:46

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表