从底层原理看Android的序列化是如何实现的_android kotlin数据类为什么能序列化(1)

总结

Android架构学习进阶是一条漫长而艰苦的道路,不能靠一时激情,更不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。所以:贵在坚持!

上面分享的字节跳动公司2020年的面试真题解析大全,笔者还把一线互联网企业主流面试技术要点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

就先写到这,码字不易,写的很片面不好之处敬请指出,如果觉得有参考价值的朋友也可以关注一下我

①「Android面试真题解析大全」PDF完整高清版+②「Android面试知识体系」学习思维导图压缩包阅读下载,最后觉得有帮助、有需要的朋友可以点个赞

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

data class User(
    @Transient
    val name: String,
    val age: Int,
) : Serializable {

    companion object {
        val serialVersionUID = 1
    }
}

反序列化后,我们就可以看到,因为name没有参与序列化,所以拿到的值为null

E/TAG: result==>User(name=null, age=25)

如果一个类中的类成员变量不支持序列化,会发生什么情况?

场景如下,UserInner没有实现序列化接口

data class UserInner(
    val a: Int,
    val b: String
)


data class User(
    @Transient
    val name: String,
    val age: Int,
    val inner: UserInner
) : Serializable {

    companion object {
        val serialVersionUID = 1
    }
}

那么当进行序列化操作的时候报错,就是因为UserInner不支持序列化。

Caused by: java.io.NotSerializableException: com.lay.learn.asm.binder.UserInner

那么这里我们需要了解,序列化其实是一次深拷贝的操作。对于浅拷贝(这里不考虑基本数据类型)只是将引用地址做一次拷贝;深拷贝则是需要重新创建一个对象,并把数据拷贝过去。

所以在序列化的时候,因为还需要做一次本地化存储,所以必定是需要拿到UserInner的数据并存储下来,所以就需要把UserInner内部的数据序列化,但是UserInner又不支持序列化,所以就会报错。

如果某个类可以序列化,但是其父类不可以序列化,那么这个类可以序列化吗?

父类:

open class Person(
    val des: String
)

子类:

data class User(
    @Transient
    val name: String,
    val age: Int,
) : Serializable, Person(name) {

    companion object {
        val serialVersionUID = 1
    }
}

此时进行序列化操作的时候,报错:没有可用的构造函数。

Caused by: java.io.InvalidClassException: com.lay.learn.asm.binder.User; no valid constructor

at com.lay.learn.asm.SerializeUtils.readObject(Unknown Source:22)

从堆栈信息中,我们看是在反序列化的时候报错了,说明在序列化存储的时候是没问题的,那么这里我们就需要看下readObject的源码了

private Object readOrdinaryObject(boolean unshared)
    throws IOException
{
    //......省略部分代码

    Class<?> cl = desc.forClass();
    if (cl == String.class || cl == Class.class
            || cl == ObjectStreamClass.class) {
        throw new InvalidClassException("invalid class descriptor");
    }

    Object obj;
    try {
        obj = desc.isInstantiable() ? desc.newInstance() : null;
    } catch (Exception ex) {
        throw (IOException) new InvalidClassException(
            desc.forClass().getName(),
            "unable to create instance").initCause(ex);
    }

    //......省略部分代码

    return obj;
}

这里我们先关注下核心代码,我们发现最终反序列化的时候,是通过反射创建一个Object对象,此时我们看直接调用了newInstance()方法。

所以这里我们这样想,在反序列化的时候类似于创建一个子类的过程,此时应该先创建父类,调用父类的构造方法,因为父类没有实现序列化接口,那么父类信息是缺失的,只能调用一个无参构造方法,那么此时父类没有空参构造方法,因此直接报错

open class Person(
    val des: String
){
    constructor() : this("") {

    }
}

反之,如果子类没实现序列化接口,而父类实现了,那么这种情况下是可以完成序列化的,因为继承关系,子类就能够获取父类的序列化能力。

1.2 Java处理序列化的过程

如果我们想要从源码角度去看序列化的过程,其实只需要关注两个类:ObjectInputStream和ObjectOutputStream。

ObjectOutputStream:用于将特定数据结构拆分成二进制数据,例如类的信息,并本地化存储,那么这个过程如何完成的?

ObjectInputStream:用于将二进制数据合并成想要的数据结构。

1.2.1 数据怎么拆

拆数据的核心方法就是ObjectOutputStream的writeObject方法,在这个中传入要拆解的对象,我们以User为例。

public final void writeObject(Object obj) throws IOException {
    if (enableOverride) {
        writeObjectOverride(obj);
        return;
    }
    try {
        writeObject0(obj, false);
    } catch (IOException ex) {
        if (depth == 0) {
            // BEGIN Android-changed: Ignore secondary exceptions during writeObject().
            // writeFatalException(ex);
            try {
                writeFatalException(ex);

            } catch (IOException ex2) {
                // If writing the exception to the output stream causes another exception there
                // is no need to propagate the second exception or generate a third exception,
                // both of which might obscure details of the root cause.
            }
            // END Android-changed: Ignore secondary exceptions during writeObject().
        }
        throw ex;
    }
}

我们看writeObject0这个方法干了什么?

private void writeObject0(Object obj, boolean unshared)
        throws IOException
    {
        boolean oldMode = bout.setBlockDataMode(false);
        depth++;
        try {
          
            // ......省略部分代码
            
            // check for replacement object
            Object orig = obj;
            Class<?> cl = obj.getClass();
            ObjectStreamClass desc;

            Class repCl;
            // 核心代码 1
            desc = ObjectStreamClass.lookup(cl, true);
            if (desc.hasWriteReplaceMethod() &&
                (obj = desc.invokeWriteReplace(obj)) != null &&
                (repCl = obj.getClass()) != cl)
            {
                cl = repCl;
                desc = ObjectStreamClass.lookup(cl, true);
            }
            // END Android-changed: Make only one call to writeReplace.

            if (enableReplace) {
                Object rep = replaceObject(obj);
                if (rep != obj && rep != null) {
                    cl = rep.getClass();
                    desc = ObjectStreamClass.lookup(cl, true);
                }
                obj = rep;
            }

            // if object replaced, run through original checks a second time
            if (obj != orig) {
                subs.assign(orig, obj);
                if (obj == null) {
                    writeNull();
                    return;
                } else if (!unshared && (h = handles.lookup(obj)) != -1) {
                    writeHandle(h);
                    return;
                }
            }

            // remaining cases
            // BEGIN Android-changed: Make Class and ObjectStreamClass replaceable.
            if (obj instanceof Class) {
                writeClass((Class) obj, unshared);
            } else if (obj instanceof ObjectStreamClass) {
                writeClassDesc((ObjectStreamClass) obj, unshared);
            // END Android-changed: Make Class and ObjectStreamClass replaceable.
            } else if (obj instanceof String) {
                writeString((String) obj, unshared);
            } else if (cl.isArray()) {
                writeArray(obj, desc, unshared);
            } else if (obj instanceof Enum) {
                writeEnum((Enum<?>) obj, desc, unshared);
            } else if (obj instanceof Serializable) {
                // 核心代码 2
                writeOrdinaryObject(obj, desc, unshared);
            } else {
                if (extendedDebugInfo) {
                    throw new NotSerializableException(
                        cl.getName() + "\n" + debugInfoStack.toString());
                } else {
                    throw new NotSerializableException(cl.getName());
                }
            }
        } finally {
            depth--;
            bout.setBlockDataMode(oldMode);
        }
    }

核心代码1

在这里,我们看到拿到User类的class对象,然后创建了一个ObjectStreamClass类,我们看下这个类是干啥的。

private ObjectStreamClass(final Class<?> cl) {
    this.cl = cl;
    // 获取类名
    name = cl.getName();
    isProxy = Proxy.isProxyClass(cl);
    isEnum = Enum.class.isAssignableFrom(cl);
    // 是否实现了serializable接口
    serializable = Serializable.class.isAssignableFrom(cl);
    externalizable = Externalizable.class.isAssignableFrom(cl);

    Class<?> superCl = cl.getSuperclass();
    superDesc = (superCl != null) ? lookup(superCl, false) : null;
    localDesc = this;

    if (serializable) {
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
                
                // 获取serialVersionUID
                suid = getDeclaredSUID(cl);
                try {
                    // 获取全部的字段信息
                    fields = getSerialFields(cl);
                    computeFieldOffsets();
                } catch (InvalidClassException e) {
                    serializeEx = deserializeEx =
                        new ExceptionInfo(e.classname, e.getMessage());
                    fields = NO_FIELDS;
                }

                if (externalizable) {
                    cons = getExternalizableConstructor(cl);
                } else {
                    cons = getSerializableConstructor(cl);
                    // 看是否有 writeObject readObject readObjectNoData方法
                    writeObjectMethod = getPrivateMethod(cl, "writeObject",
                        new Class<?>[] { ObjectOutputStream.class },
                        Void.TYPE);
                    readObjectMethod = getPrivateMethod(cl, "readObject",
                        new Class<?>[] { ObjectInputStream.class },
                        Void.TYPE);
                    readObjectNoDataMethod = getPrivateMethod(
                        cl, "readObjectNoData", null, Void.TYPE);
                    hasWriteObjectData = (writeObjectMethod != null);
                }
                writeReplaceMethod = getInheritableMethod(
                    cl, "writeReplace", null, Object.class);
                readResolveMethod = getInheritableMethod(
                    cl, "readResolve", null, Object.class);
                return null;
            }
        });
    } else {
        suid = Long.valueOf(0);
        fields = NO_FIELDS;
    }

    try {
        fieldRefl = getReflector(fields, this);
    } catch (InvalidClassException ex) {
        // field mismatches impossible when matching local fields vs. self
        throw new InternalError(ex);
    }

    if (deserializeEx == null) {
        if (isEnum) {
            deserializeEx = new ExceptionInfo(name, "enum type");
        } else if (cons == null) {
            deserializeEx = new ExceptionInfo(name, "no valid constructor");
        }
    }
    for (int i = 0; i < fields.length; i++) {
        if (fields[i].getField() == null) {
            defaultSerializeEx = new ExceptionInfo(
                name, "unmatched serializable field(s) declared");
        }
    }
    initialized = true;
}

在这个类当中,我们看到其实就是拿到User类的class对象之后,获取类中的全部信息,像Class类名、是否实现了serialize接口、serialVersionUID、全部字段信息、是否存在writeObject readObject readObjectNoData方法,总之就是将类的全部信息,封装成了ObjectStreamClass类。

核心代码 2

回到前面的代码中,我们看会做一系列的判断,判断User是什么类型的对象,因为User实现了Serializable接口,所以会走到writeOrdinaryObject方法中,同时将ObjectStreamClass也传了进来。

private void writeOrdinaryObject(Object obj,
                                 ObjectStreamClass desc,
                                 boolean unshared)
    throws IOException
{
    if (extendedDebugInfo) {
        debugInfoStack.push(
            (depth == 1 ? "root " : "") + "object (class "" +
            obj.getClass().getName() + "", " + obj.toString() + ")");
    }
    try {
        desc.checkSerialize();

        bout.writeByte(TC_OBJECT);
        writeClassDesc(desc, false);
        handles.assign(unshared ? null : obj);
        if (desc.isExternalizable() && !desc.isProxy()) {
            writeExternalData((Externalizable) obj);
        } else {
            writeSerialData(obj, desc);
        }
    } finally {
        if (extendedDebugInfo) {
            debugInfoStack.pop();
        }
    }
}

首先调用了writeClassDesc方法,在这个方法中,做的主要事情就是把之前获取的类信息全部全部转换为二进制数据,从writeNonProxyDesc方法中就可以看到,太深入的我就不介绍了,有精力的伙伴可以深入看一下。

private void writeClassDesc(ObjectStreamClass desc, boolean unshared)
        throws IOException
    {
        int handle;
        if (desc == null) {
            writeNull();
        } else if (!unshared && (handle = handles.lookup(desc)) != -1) {


## 要如何成为Android架构师?

搭建自己的知识框架,全面提升自己的技术体系,并且往底层源码方向深入钻研。
大多数技术人喜欢用思维脑图来构建自己的知识体系,一目了然。这里给大家分享一份大厂主流的Android架构师技术体系,可以用来搭建自己的知识框架,或者查漏补缺;
![](https://img-blog.csdnimg.cn/img_convert/c59f9f753382e8a24aad38b369e70045.webp?x-oss-process=image/format,png)

> 对应这份技术大纲,我也整理了一套Android高级架构师完整系列的视频教程,主要针对3-5年Android开发经验以上,需要往高级架构师层次学习提升的同学,希望能帮你突破瓶颈,跳槽进大厂;
>

**最后我必须强调几点:**

1.搭建知识框架可不是说你整理好要学习的知识顺序,然后看一遍理解了能复制粘贴就够了,大多都是需要你自己读懂源码和原理,能自己手写出来的。
2.学习的时候你一定要多看多练几遍,把知识才吃透,还要记笔记,这些很重要! 最后你达到什么水平取决你消化了多少知识
3.最终你的知识框架应该是一个完善的,兼顾广度和深度的技术体系。然后经过多次项目实战积累经验,你才能达到高级架构师的层次。

你只需要按照在这个大的框架去填充自己,年薪40W一定不是终点,技术无止境




**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**


**最后我必须强调几点:**

1.搭建知识框架可不是说你整理好要学习的知识顺序,然后看一遍理解了能复制粘贴就够了,大多都是需要你自己读懂源码和原理,能自己手写出来的。
2.学习的时候你一定要多看多练几遍,把知识才吃透,还要记笔记,这些很重要! 最后你达到什么水平取决你消化了多少知识
3.最终你的知识框架应该是一个完善的,兼顾广度和深度的技术体系。然后经过多次项目实战积累经验,你才能达到高级架构师的层次。

你只需要按照在这个大的框架去填充自己,年薪40W一定不是终点,技术无止境




**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 27
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值