Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Android开发之漫漫长途 X——Android序列化

Android开发之漫漫长途 X——Android序列化

作者头像
LoveWFan
发布于 2018-08-07 07:50:08
发布于 2018-08-07 07:50:08
40200
代码可运行
举报
运行总次数:0
代码可运行

该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列。该系列引用了《Android开发艺术探索》以及《深入理解Android 卷Ⅰ,Ⅱ,Ⅲ》中的相关知识,另外也借鉴了其他的优质博客,在此向各位大神表示感谢,膜拜!!!另外,本系列文章知识可能需要有一定Android开发基础和项目经验的同学才能更好理解,也就是说该系列文章面向的是Android中高级开发工程师。


前言

上一篇中我们比较详尽的分析了ServiceManager。那么本篇我们来讲一下Android序列化的相关知识。为什么跨度那么大,因为“任性”?其实不是的,同志们还记得上两篇出现的Parcel吗,Parcel是一个容器,他可以包含数据或者是对象引用,并且能够用于Binder的传输。同时支持序列化以及跨进程之后进行反序列化,同时其提供了很多方法帮助开发者完成这些功能。从上面的描述可以看出Parcel是进程间通信的数据载体。我们常常需要持久化一些对象,除了数据库等持久化方案之外,把对象转换成字节数组并通过流的方式存储在本地也是一个不错的方法,另外当我们需要通过Intent和Binder传输数据是就需要使用序列化后的数据。

Java中的Serializable

Serializable 是Java所提供的一个序列化接口,它是一个空接口,为对象提供标准的序列化和反序列化操作。使用Serializable来实现序列化相当简单,只需要在需要序列化的类实现Serializable接口并在其中声明一个类似下面的标识即可自动实现默认的序列化过程。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Person extends PersonParent implements Serializable {
    private static final long serialVersionUID = 1L;

    //静态域
    public static int static_field;
    //transient域
    public transient int transient_field;
    //一个普通的域
    public String desc;

    public Person(String desc) {
        this.desc = desc;
    }

    static class PersonSerializableProxy implements Serializable{
        private String desc;

        private PersonSerializableProxy(Person s) {
            this.desc = s.desc;
        }

        /**
         * 与writeReplace相同,ObjectInputStream会通过反射调用 readResolve()这个方法,
         * 决定是否替换反序列化出来的对象。
         * @return
         */
        private Object readResolve() {
            return new Person(desc);
        }

    }

    /**
     *
     * 在序列化一个对象时,ObjectOutputStream会通过反射首先调用writeReplace这个方法,
     * 在这里我们可以替换真正送去序列的对象,
     * 如果我们没有重写,那序列化的对象就是最开始的对象。
     * @return
     */
    private Object writeReplace() {
         //序列化Person的时候我们并没有直接写入Person对象,而是写入了PersonSerializableProxy对象
        return new PersonSerializableProxy(this);
    }

    /**
     * 这里主要是为了防止攻击,任何以Person声明的对象字节流都是流氓!!
     * 因为我在writeReplace中已经把序列化的实例指向了SerializableProxy
     * @param stream
     * @throws InvalidObjectException
     */
    private void readObject(ObjectInputStream stream) throws InvalidObjectException {
        throw new InvalidObjectException("proxy requied!");
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Person person = new Person("desc");
        person.transient_field = 100;
        person.static_field = 10086;

        ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("cache.txt"));
        outputStream.writeObject(person);
        outputStream.flush();
        outputStream.close();

        person.static_field = 10087;


        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("cache.txt"));
        Person deserialObj = (Person) objectInputStream.readObject();
        System.out.println(deserialObj);
}


}
class PersonParent{
    private String name;
    //PersonParent类要么继承自Serializable,要么需要提供一个无参构造器。
    public PersonParent() {
    }

    public PersonParent(String name) {
        this.name = name;
    }
}

不过在使用中也需要注意以下几个问题:

  1. serialVersionUID用来标识当前序列化对象的类版本,建议每一个实现Serialization的类都指定该域。当然如果我们没有指定,JVM会根据类的信息自动生成一个UID。
  2. 被transient描述的域和类的静态变量是不会被序列化的,序列化是针对类实例。
  3. 需要进行序列化的对象所有的域都必须实现Serializable接口,不然会直接报错NotSerializableException。当然,有两个例外:域为空 或者域被transient描述是不会报错的。
  4. 如果一个实现了Serializable类的对象继承自另外一个类,那么这个类要么需要继承自Serializable,要么需要提供一个无参构造器。
  5. 反序列化产生的对象并不是通过构造器创建的,那么很多依赖于构造器保证的约束条件在对象反序列化时都无法保证。比如一个设计成单例的类如果能够被序列化就可以分分钟克隆出多个实例...

Android中的Parcelable

相对于Serializable而言,Parcelable的使用要复杂一些

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Book implements Parcelable {
    private String name;

    public Book(String name) {
        this.name = name;
    }

    protected Book(Parcel in) {
        name = in.readString();
    }
    
    //反序列化功能由CREATOR完成,在CREATOR的内部标明的如何创建序列对象和数组
    public static final Creator<Book> CREATOR = new Creator<Book>() {
        //从Parcel中反序列化对象
        @Override
        public Book createFromParcel(Parcel in) {
            //其内部调用Parcel的一系列readXXX方法实现反序列化过程
            return new Book(in);
        }
        //创建序列化数组
        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }
    //序列化过程:
    //重写writeToParcel方法,我们要在这里逐一对需要序列化的属性用Parcel的一系列writeXXX方法写入
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
    }
}

从上述代码注释可以看出,写一个实现Parcelable接口的类还是比较麻烦的,和Serailable相比,我们需要在writeToParcel中按序写入各个域到流中,同样,在createFromParcel中我们需要自己返回一个Book对象。

Parcelable在使用上也与Serializable稍有不同

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class TestActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test2);

        //获取一个Parcel容器
        Parcel parcel = Parcel.obtain();
        //需要序列化的对象
        Book book = new Book("c++");

        //把对象写入Parcel
        parcel.writeParcelable(book,0);
        //Parcel读写共用一个位置计数,这里一定要重置一下当前的位置
        parcel.setDataPosition(0);
        //读取Parcel
        Book book1 = parcel.readParcelable(Book.class.getClassLoader());
        Log.d("TestActivity",book1.toString());
    }
}

Parcelable的写

我们来看一下writeParcelable方法 [Parcel.java]

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public final void writeParcelable(Parcelable p, int parcelableFlags) {
    //判断p是否为空
    if (p == null) {
        writeString(null);
        return;
    }
    //① 先写入p的类名
    writeParcelableCreator(p);
    //② 调用我们重写的writeToParcel方法,按顺序写入域
    p.writeToParcel(this, parcelableFlags);
}

public final void writeParcelableCreator(Parcelable p) {
    //① 先写入p的类名
    String name = p.getClass().getName();
    writeString(name);
}

Parcelable的读

我们来看readParcelable方法 [Parcel.java]

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public final <T extends Parcelable> T readParcelable(ClassLoader loader) {
    //① 调用readParcelableCreator
    //这时获得就是我们自定义的CREATOR
    Parcelable.Creator<?> creator = readParcelableCreator(loader);

    if (creator == null) {
        return null;
    }
    // 判断当前creator是不是Parcelable.ClassLoaderCreator<?>的实例
    if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
      //如果是的话,,我们调用reateFromParcel(this, loader);
      Parcelable.ClassLoaderCreator<?> classLoaderCreator =
          (Parcelable.ClassLoaderCreator<?>) creator;
      return (T) classLoaderCreator.createFromParcel(this, loader);
    }
    //调用我们自定义的CREATOR中重写的createFromParcel方法
    return (T) creator.createFromParcel(this);
}

public final Parcelable.Creator<?> readParcelableCreator(ClassLoader loader) {
    //首先把类名读取出来
    String name = readString();

    Parcelable.Creator<?> creator;
    //mCreators做了一下缓存,如果之前某个classloader把一个parcelable的Creator获取过
    //那么就不需要通过反射去查找了
    
    synchronized (mCreators) {
        HashMap<String,Parcelable.Creator<?>> map = mCreators.get(loader);
        if (map == null) {
            map = new HashMap<>();
            mCreators.put(loader, map);
        }
        creator = map.get(name);
        if (creator == null) {
            try {
                ClassLoader parcelableClassLoader =
                        (loader == null ? getClass().getClassLoader() : loader);
                //加载我们自己实现Parcelable接口的类
                Class<?> parcelableClass = Class.forName(name, false,
                        parcelableClassLoader);
                
                Field f = parcelableClass.getField("CREATOR");
                Class<?> creatorType = f.getType();
                creator = (Parcelable.Creator<?>) f.get(null);
            }
            catch (Exception e) {
                    //catch exception
            }
            if (creator == null) {
                throw new BadParcelableException("Parcelable protocol requires a "
                        + "non-null Parcelable.Creator object called "
                        + "CREATOR on class " + name);
            }

            map.put(name, creator);
        }
    }

    return creator;
}

我们的测试例子读取Parcel

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Book book1 = parcel.readParcelable(Book.class.getClassLoader());

可以看到我们在使用 readParcelable的时候,传入的参数是Book类的类加载器,根据我们上面的代码,我们知道我们先会通过反射获取定义在Book类中的CREATOR属性,我们回想一下在Book类中是怎么定义CREATOR的

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public static final Creator<Book> CREATOR = new Creator<Book>() {
        //从Parcel中反序列化对象
        @Override
        public Book createFromParcel(Parcel in) {
            //其内部调用Parcel的一系列readXXX方法实现反序列化过程
            return new Book(in);
        }
        //创建序列化数组
        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

我们得到CREATOR属性后,调用它的createFromParcel方法,由多态可知调用的实际我们定义在CREATOR内的createFromParcel方法,在该方法内我们创建了Book对象(内部实现是通过Parcel的一系列readXXX方法)并返回。至此我们就得到了反序列化的对象


本篇总结

我们本篇详细分析了Android序列化相关知识,你可以使用Java中的Serializable也可以使用Parcelable。


下篇预告

下一篇文章是对前面所讲文章做一个小结。读者敬请期待哦。

此致,敬礼

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-01-10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Android 进阶6:两种序列化方式 Serializable 和 Parcelable
张拭心 shixinzhang
2018/01/05
1.6K0
Android 进阶6:两种序列化方式 Serializable 和 Parcelable
Android开发笔记(二十七)对象序列化
程序中存储和传递信息,需要有个合适的数据结构,最简单的是定义几个变量,变量多了之后再分门别类,便成了聚合若干变量的对象。代码在函数调用时可以直接传递对象,但更多的场合例如与文件交互、与网络交互、组件之间交互等等,就无法直接使用未经处理的对象。因此Java引入了序列化的概念,用于把一个对象转换为字节序列,然后再对这个字节序列做存储和传递操作。与之对应的是反序列化,反序列化是把一个字节序列恢复为Java对象的过程,而序列化是把Java对象转化为字节序列的过程。
aqi00
2019/01/18
6620
必读!Android序列化权威面试指南,资深面试者的秘诀
在Android开发中,Serializable与Parcelable是两种用于实现对象序列化的常见方式。在面试中,对于这两种技术的理解和应用能力常常被用来评估一个Android开发者的水平。本文将围绕Serializable与Parcelable展开一系列高级疑难的面试问题,并深入探讨它们的原理、优劣势以及实际应用中的技巧。
Rouse
2024/03/06
1670
必读!Android序列化权威面试指南,资深面试者的秘诀
Android序列化总结
公园里,一位仙风鹤骨的老者在打太极,一招一式都仙气十足,一个年轻人走过去:“大爷,太极这玩意儿花拳绣腿,你练它干啥?”老者淡淡一笑:“年轻人,你还没有领悟到太极的真谛,这样,你用最大力气打我试试。”于是年轻人用力打了老头一拳,被讹了八万六。
大公爵
2018/09/05
8460
Android序列化总结
Android 反序列化漏洞攻防史话
Java 在历史上出现过许多反序列化的漏洞,但大部分出自 J2EE 的组件。即便是 FastJSON 这种漏洞,似乎也很少看到在 Android 中被实际的触发和利用。本文即为对历史上曾出现过的 Android Java 反序列化漏洞的分析和研究记录。
evilpan
2023/03/27
1.9K0
Android 反序列化漏洞攻防史话
Android查缺补漏(IPC篇)-- 进程间通讯基础知识热身
本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8479282.html 在Android中进程间通信是比较难的一部分,同时又非常重要,针对进程间通信,博主会用四篇文章来介绍,本篇文章为IPC系列的开篇,主要介绍一些IPC中用到的一些概念、基础等,目的是让读者朋友们在学习IPC之前对一些必要的知识有一个大体的把握。在Android中进程间通讯的方式有很多种,在后续的三篇中会分别介绍每一种方式的实现过程已经各自的优缺点。 IPC是什么? I
codingblock
2018/03/30
6230
漫谈序列化—使用、原理、问题
说到对象,是一个比较宽泛的概念,简单的说,他就是类的一个实例,有状态和行为,存活在内存中,一旦JVM停止运行,对象的状态也会丢失。
码上积木
2021/02/08
9090
Android进程间通信(一):基础介绍
进程间通信 即 IPC机制,IPC 全称为 Inter-Process Communication。
103style
2022/12/19
3180
Serializable和Parcelable的再次回忆
自己开发Android也有些时间了,Serializable和Parcelable遇到过不止一次了。但是每次别人问起具体的内容自己偏偏记得不是很清晰。因为某些原因再次梳理一下,以文章的形式给自己存储下来。温故而知新!!
静默加载
2020/05/31
6100
Android十八章:多进程基础
这一章主要讲述多进程的作用,序列化和反序列化,Serializable和Parcelable
ppjun
2018/09/05
4730
【Android 应用开发】BluetoothClass详解
Java中的序列化方法 : 在Java中序列化有两种方法, 一种是实现Serializable接口, 一种是实现Parcelable接口, Serializable接口是J2SE固有支持的, Parcelable是Android支持的, 是Android中特有的, 效率比Serializable高;
韩曙亮
2023/03/27
2720
[084]Binder答疑解惑(二)
只需要序列化的时候将String和int写个某个内存区域,反序列化的时候读取这个内存区域,重新构造一个Person对象。
王小二
2023/09/11
2760
[084]Binder答疑解惑(二)
Android技能树 — 多进程相关小结
这次是讲Android存储路径及IO的基本操作。因为我们在开发的时候会经常这种方便的需求。这篇文章的内容我写的可能很少,都没有细写。别吐槽。o( ̄︶ ̄)o
青蛙要fly
2018/08/29
4570
Android技能树 — 多进程相关小结
彻底理解Serializable和Parcelable
这里有二个关键字,存储和传输,存储的场景比如对象的持久化,传输的场景比如将对象通过网络传输,然后在需要使用的时候,反序列化,重新创建对象。
三好码农
2019/03/15
1.2K0
Java数据的序列化总结
Android开发中经常需要用到序列化,系统提供了两个接口用来实现, · Parcelable · Serializable
PhoenixZheng
2018/08/07
2530
Android中的序列化:Parcelable和Serializable
概述 序列化:将一个对象转换成 可存储或 可传输的状态。 Parcelable和Serializable的区别 作用 Serializable的作用是为了保存对象的属性到本地文件、数据库、网络流、rmi以方便数据传输,当然这种传输可以是程序内的也可以是两个程序间的。 Parcelable的设计初衷是因为Serializable效率过慢,为了在程序内不同组件间以及不同Android程序间(AIDL)高效的传输数据而设计,这些数据仅在内存中存在,Parcelable是通过IBinder通信的消息的载体。 性能比
用户1205080
2018/12/21
7990
Java IO之对象的序列化、ObjectInputStream和ObjectOutputStream类
Java将数据从源(文件、内存、键盘、网络)读入到内存 中,形成了流,然后将这些流还可以写到另外的目的地(文件、内存、控制台、网络),之所以称为流,是因为这个数据序列在不同时刻所操作的是源的不同部分。按照不同的分类标准,IO流分为不同类型。主要有以下几种方式:按照数据流方向、数据处理的单位和功能。
用户7886150
2021/04/25
1.1K0
Kotlin 序列化Parcelable/Serializable
开发中,序列化是常见操作,在java中,我们一般会用到Serializable或者Parcelable,优缺点就不赘述了,今天来看看kotlin中的序列化。
yechaoa
2022/06/10
2K0
Android入门之数据传递
Intent数据传递 List 传递List传递List的方法 ArrayList<String> info = new ArrayList<String>(); info.add(name); info.add(website); info.add(weibo); Intent intent = new Intent(MainActivity.this, ResultActivity.class); intent.putStringArrayListExtra("infoList"
xiangzhihong
2018/02/06
8660
android之Parcelable介绍
Parcelable类的概述在SDK中是这样的:这些类的接口可以将实例写入或从中还原 Parcel。
李小白是一只喵
2020/12/31
6580
相关推荐
Android 进阶6:两种序列化方式 Serializable 和 Parcelable
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验