ARouter 传自定义对象获取值为null的解析 及解决方法

Stella981
• 阅读 608

首先

我在使用 withObject 传自定义对象的时候,发现一个BUG,在传值的时候,会走SerializationService的object2Json方法,但是在获取值的时候,竟然不调用parseObject这个方法来转回对象。

我们来一步一步分析

使用 withObject传值

ARouter 传自定义对象获取值为null的解析 及解决方法

这里可以看到,ARouter 获取了 我们自定义的 SerializationService 并且调用了 object2Json 方法,获取到转换后的字符串,然后将字符串保存到mBundle里

取值

ARouter 传自定义对象获取值为null的解析 及解决方法

这里可以看到,ARouter 其实内部根据类名,生成了一个内部类,我们继续跟踪进去

再跟踪

ARouter 传自定义对象获取值为null的解析 及解决方法

这里可以看到,其实ARouter已经将我们的自定义的SerializationService 服务实例化,但是不知道怎么回事,却没有将取出来的字符串传进服务里调用 parseObject 这个函数转回对象


OK 以上是问题出现的经过,以及断点逐步分析问题所在,下面说说解决方案

问题排查,以及解决方案

按道理,这么明显的BUG,阿里的工程师不会犯的,所以我把ARouter的代码下载下来之后,导入到AndroidStudio里,首先就去找对应的 AutowiredProcessor 找到关键点

private String buildStatement(String originalValue, String statement, int type, boolean isActivity) {
        if (type == TypeKind.BOOLEAN.ordinal()) {
            statement += (isActivity ? ("getBooleanExtra($S, " + originalValue + ")") : ("getBoolean($S)"));
        } else if (type == TypeKind.BYTE.ordinal()) {
            statement += (isActivity ? ("getByteExtra($S, " + originalValue + ")") : ("getByte($S)"));
        } else if (type == TypeKind.SHORT.ordinal()) {
            statement += (isActivity ? ("getShortExtra($S, " + originalValue + ")") : ("getShort($S)"));
        } else if (type == TypeKind.INT.ordinal()) {
            statement += (isActivity ? ("getIntExtra($S, " + originalValue + ")") : ("getInt($S)"));
        } else if (type == TypeKind.LONG.ordinal()) {
            statement += (isActivity ? ("getLongExtra($S, " + originalValue + ")") : ("getLong($S)"));
        }else if(type == TypeKind.CHAR.ordinal()){
            statement += (isActivity ? ("getCharExtra($S, " + originalValue + ")") : ("getChar($S)"));
        } else if (type == TypeKind.FLOAT.ordinal()) {
            statement += (isActivity ? ("getFloatExtra($S, " + originalValue + ")") : ("getFloat($S)"));
        } else if (type == TypeKind.DOUBLE.ordinal()) {
            statement += (isActivity ? ("getDoubleExtra($S, " + originalValue + ")") : ("getDouble($S)"));
        } else if (type == TypeKind.STRING.ordinal()) {
            statement += (isActivity ? ("getStringExtra($S)") : ("getString($S)"));
        } else if (type == TypeKind.SERIALIZABLE.ordinal()) {
            statement += (isActivity ? ("getSerializableExtra($S)") : ("getSerializable($S)"));
        } else if (type == TypeKind.PARCELABLE.ordinal()) {
            statement += (isActivity ? ("getParcelableExtra($S)") : ("getParcelable($S)"));
        } else if (type == TypeKind.OBJECT.ordinal()) {
            statement = "serializationService.parseObject(substitute." + (isActivity ? "getIntent()." : "getArguments().") + (isActivity ? "getStringExtra($S)" : "getString($S)") + ", new com.alibaba.android.arouter.facade.model.TypeWrapper<$T>(){}.getType())";
        }

        return statement;
    }

我们看到最后一行,发现竟然有对 parseObject 这个函数进行调用,但是调用的条件是 type==TypeKind.OBJECT.ordinal()

追踪 TypeKind

那么,这个TypeKind是个什么东西呢?我们找到对应的类:

public enum TypeKind {
    // Base type
    BOOLEAN,
    BYTE,
    SHORT,
    INT,
    LONG,
    CHAR,
    FLOAT,
    DOUBLE,

    // Other type
    STRING,
    SERIALIZABLE,
    PARCELABLE,
    OBJECT;
}

其实就是个枚举,那么这个值是从哪里传过来的呢?

ARouter 传自定义对象获取值为null的解析 及解决方法

这里可以看到这个函数在这里被调用,传入的值是由 typeUtils 决定的,我们继续追踪

追踪 TypeUtils

关键部分代码:

/**
 * Diagnostics out the true java type
 *
 * @param element Raw type
 * @return Type class of java
 */
public int typeExchange(Element element) {
    TypeMirror typeMirror = element.asType();

    // Primitive
    if (typeMirror.getKind().isPrimitive()) {
        return element.asType().getKind().ordinal();
    }

    switch (typeMirror.toString()) {
        case BYTE:
            return TypeKind.BYTE.ordinal();
        case SHORT:
            return TypeKind.SHORT.ordinal();
        case INTEGER:
            return TypeKind.INT.ordinal();
        case LONG:
            return TypeKind.LONG.ordinal();
        case FLOAT:
            return TypeKind.FLOAT.ordinal();
        case DOUBEL:
            return TypeKind.DOUBLE.ordinal();
        case BOOLEAN:
            return TypeKind.BOOLEAN.ordinal();
        case CHAR:
            return TypeKind.CHAR.ordinal();
        case STRING:
            return TypeKind.STRING.ordinal();
        default:    // Other side, maybe the PARCELABLE or SERIALIZABLE or OBJECT.
            if (types.isSubtype(typeMirror, parcelableType)) {  // PARCELABLE
                return TypeKind.PARCELABLE.ordinal();
            } else if (types.isSubtype(typeMirror, serializableType)) {  // PARCELABLE
                return TypeKind.SERIALIZABLE.ordinal();
            } else {    // For others
                return TypeKind.OBJECT.ordinal();
            }
    }
}

原来是在类型判断的时候,如果自定义对象类型是serializable,那么会被当成serializable处理,如果是parcelable那么会被当成parcelable方式处理,只有在不是 parcelable 也不是 serializable 的时候,才会当成自定义对象处理。

所以只需要将自定义的类,不要去实现 Serializable, Parcelable 接口 这两个接口,那么就可以正常传值。

End OK 本次追踪到此结束,有意思。

点赞
收藏
评论区
推荐文章
kenx kenx
3年前
SpringBoot 默认json解析器详解和字段序列化自定义
前言在我们开发项目API接口的时候,一些没有数据的字段会默认返回NULL,数字类型也会是NULL,这个时候前端希望字符串能够统一返回空字符,数字默认返回0,那我们就需要自定义json序列化处理SpringBoot默认的json解析方案我们知道在springboot中有默认的json解析器,SpringBoot中默认使用的Json解析技术框架是ja
Wesley13 Wesley13
3年前
FastJson、Jackson、Gson进行Java对象转换Json的细节处理
Java对象转换Json的细节处理前言Java对象在转json的时候,如果对象里面有属性值为null的话,那么在json序列化的时候要不要序列出来呢?对比以下json转换方式一、fastJson1、fastJson在转换java对象为json的时候,默认是不序列化nu
Stella981 Stella981
3年前
Javascript中的方法链式调用
Javascript中的方法链式调用前言为链式调用创建一个对象为什么报错?解决方法使用闭包实现链式调用前言方法的链式调用这个概念,其实是在面向对象编程中比较常见的语法,它能让使用者在一个对象上连续的调用不同的方法。在不使用临时变量存储中间结果的情
Wesley13 Wesley13
3年前
Java多线程带返回值的Callable接口
Java多线程带返回值的Callable接口在面试的时候,有时候是不是会遇到面试会问你,Java中实现多线程的方式有几种?你知道吗?你知道Java中有可以返回值的线程吗?在具体的用法你知道吗?如果两个线程同时来调用同一个计算对象,计算对象的call方法会被调用几次你知道吗?如果这些你知道,那么凯哥(凯哥Java:kaigejava)恭喜你,本文你可以不用
Wesley13 Wesley13
3年前
ES6新语法(二)
1.解构在ES6中,可以使用解构从数组和对象提取值并赋值给独特的变量,即将数组或对象中的值,拆成一个一个变量。解构:自动解析数组或对象中的值,并赋值给指定的变量。、1.1数组解构vararr3,4,5;vara,b,carr;
Stella981 Stella981
3年前
Redis 对象系统
Redis为了更好的实现键值对数据库,创建了一个对象系统,以下为Redis对象系统的相关知识简介。redisObjectRedis使用对象来表示数据库中的键和值,每次在Redis数据库中创建一个键值对时,至少会创建两个对象。一个为键对象,一个为值对象。Redis对象的定义如下:typedef
Stella981 Stella981
3年前
Scala 提取器(Extractor)
提取器是从传递给它的对象中提取出构造该对象的参数。Scala标准库包含了一些预定义的提取器,我们会大致的了解一下它们。Scala提取器是一个带有unapply方法的对象。unapply方法算是apply方法的反向操作:unapply接受一个对象,然后从对象中提取值,提取的值通常是用来构造该对象的值。以下实例演示了邮件地址的提取器对象:
Wesley13 Wesley13
3年前
unity将 -u4E00 这种 编码 转汉字 方法
 unity中直接使用 JsonMapper.ToJson(对象),取到的字符串,里面汉字可能是\\u4E00类似这种其实也不用转,服务器会通过类似fastjson发序列化的方式,将json转对象,获取对象的值就是中文但是有时服务器要求将传参中字符串中类似\\u4E00这种转汉字,就需要下面 publ
Wesley13 Wesley13
3年前
Go语言方法的 值接受者 和 指针接受者 的区别
首先说下结论1\.无论方法的接受者是值接受者还是指针接受者,对象值调用该方法和对象指针调用该方法都是可行的。2\.当方法接受者为指针接受者时,对象的值调用该方法和指针调用该方法都会操作对象本身。3\.当方法接受者为值接受者时,对象的值调用该方法和指针调用该方法都会操作对象的副本,对对象本身无影响。pack
Wesley13 Wesley13
3年前
Java面向对象技术
问题及答案来源自《Java程序员面试笔试宝典》第四章Java基础知识4.2面向对象技术1、面向对象与面向过程有什么区别?看下面一个实例即可:面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候依次调用;面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而