Android中Parcelable的原理和使用方法

Android中Parcelable的原理和使用方法Parcelable 的简单介绍介绍 Parcelable 不得不先提一下 Serializable 接口 Serializable 是 Java 为我们提供的一个标准化的序列化接口 那什么是序列化呢 进行 Android 开发的时候 无法将对象的引用传给 Activities 或者 Fragments 我们需要将这些对象放到一个 Intent 或者 Bundle 里面 然后再传递 简单来说就是将对象转换为可以传输的二进制流 二进制序列 的过程 这样我们就可以通过序列化 转化为可以在网络传输或者保存到本地的流 序列 从而进行传

Parcelable的简单介绍

介绍Parcelable不得不先提一下Serializable接口,Serializable是Java为我们提供的一个标准化的序列化接口,那什么是序列化呢?

进行Android开发的时候,无法将对象的引用传给Activities或者Fragments,我们需要将这些对象放到一个Intent或者Bundle里面,然后再传递。简单来说就是将对象转换为可以传输的二进制流(二进制序列)的过程,这样我们就可以通过序列化,转化为可以在网络传输或者保存到本地的流(序列),从而进行传输数据 ,那反序列化就是从二进制流(序列)转化为对象的过程.

Parcelable是Android为我们提供的序列化的接口,Parcelable相对于Serializable的使用相对复杂一些,但Parcelable的效率相对Serializable也高很多,这一直是Google工程师引以为傲的,有时间的可以看一下Parcelable和Serializable的效率对比 Parcelable vs Serializable 号称快10倍的效率

Android源码中的Parcelable

/ * Interface for classes whose instances can be written to * and restored from a {@link Parcel}. Classes implementing the Parcelable * interface must also have a non-null static field called CREATOR * of a type that implements the {@link Parcelable.Creator} interface. * * 

A typical implementation of Parcelable is:

* *
 * public class MyParcelable implements Parcelable { * private int mData; * * public int describeContents() { * return 0; * } * * public void writeToParcel(Parcel out, int flags) { * out.writeInt(mData); * } * * public static final Parcelable.Creator 
   
     CREATOR * = new Parcelable.Creator 
    
      () { * public MyParcelable createFromParcel(Parcel in) { * return new MyParcelable(in); * } * * public MyParcelable[] newArray(int size) { * return new MyParcelable[size]; * } * }; * * private MyParcelable(Parcel in) { * mData = in.readInt(); * } * } 
     
   

*/

通过源码中的介绍 可以知道,Parcelable接口的实现类是可以通过Parcel写入和恢复数据的,并且必须要有一个非空的静态变量 CREATOR,而且还给了一个例子,这样我们写起来就比较简单了,但是简单的使用并不是我们的最终目的,通过查看Android源码中Parcelable可以看出,Parcelable实现过程主要分为序列化,反序列化,描述三个过程,下面分别介绍下这三个过程。

Parcel的简介

在介绍之前我们需要先了解Parcel是什么?Parcel翻译过来是打包的意思,其实就是包装了我们需要传输的数据,然后在Binder中传输,也就是用于跨进程传输数据

简单来说,Parcel提供了一套机制,可以将序列化之后的数据写入到一个共享内存中,其他进程通过Parcel可以从这块共享内存中读出字节流,并反序列化成对象,下图是这个过程的模型。

Android中Parcelable的原理和使用方法

Parcelable中的三大过程介绍(序列化,反序列化,描述)

什么是序列化

序列化,表示将一个对象转换成可存储或可传输的状态。序列化后的对象可以在网络上进行传输,也可以存储到本地。

我先把代码贴出来

public class Album implements Parcelable { / * 负责反序列化 */ public static final Creator 
  
    CREATOR = new Creator 
   
     () { / * 从序列化对象中,获取原始的对象 * @param source * @return */ @Override public Album createFromParcel(Parcel source) { return new Album(source); } / * 创建指定长度的原始对象数组 * @param size * @return */ @Override public Album[] newArray(int size) { return new Album[0]; } }; private final String mId; private final String mCoverPath; private final String mDisplayName; private final long mCount; Album(String id, String coverPath, String displayName, long count) { mId = id; mCoverPath = coverPath; mDisplayName = displayName; mCount = count; } Album(Parcel source) { mId = source.readString(); mCoverPath = source.readString(); mDisplayName = source.readString(); mCount = source.readLong(); } / * 描述 * 返回的是内容的描述信息 * 只针对一些特殊的需要描述信息的对象,需要返回1,其他情况返回0就可以 * * @return */ @Override public int describeContents() { return 0; } / * 序列化 * * @param dest * @param flags */ @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mId); dest.writeString(mCoverPath); dest.writeString(mDisplayName); dest.writeLong(mCount); } 
    
  

实现Parcelable的作用

1)永久性保存对象,保存对象的字节序列到本地文件中;

2)通过序列化对象在网络中传递对象;

3)通过序列化在进程间传递对象。

首先写一个类实现Parcelable接口,会让我们实现两个方法:

  • describeContents 描述
    其中describeContents就是负责文件描述.通过源码的描述可以看出,只针对一些特殊的需要描述信息的对象,需要返回1,其他情况返回0就可以

  • writeToParcel 序列化
    我们通过writeToParcel方法实现序列化,writeToParcel返回了Parcel,所以我们可以直接调用Parcel中的write方法,基本的write方法都有,对象和集合比较特殊下面单独讲,基本的数据类型除了boolean其他都有,Boolean可以使用int或byte存储

我们将上面的Album对象实现序列化,Album对象包含四个字段。

反序列化

反序列化需要定义一个CREATOR的变量,上面也说了具体的做法,这里可以直接复制Android给的例子中的,也可以自己定义一个(名字千万不能改),通过匿名内部类实现Parcelable中的Creator的接口

Parcelable的使用和实现

根据上面三个过程的介绍,Parcelable就写完了,就可以直接在Intent中传输了,可以自己写两个Activity传输一下数据试一下,其中一个putExtra另一个getParcelableExtra即可。

Parcelable中对象和集合的处理

import android.os.Parcel; import android.os.Parcelable; import java.util.ArrayList; public class ParcelDemo implements Parcelable { private int count; private String name; private ArrayList 
  
    tags; private Book book; // * 注意: 这里如果是集合 ,一定要初始化 * private ArrayList 
   
     books = new ArrayList<>(); / * 反序列化 * * @param in */ protected ParcelDemo(Parcel in) { count = in.readInt(); name = in.readString(); tags = in.createStringArrayList(); // 读取对象需要提供一个类加载器去读取,因为写入的时候写入了类的相关信息 book = in.readParcelable(Book.class.getClassLoader()); //读取集合也分为两类,对应写入的两类 //这一类需要用相应的类加载器去获取 in.readList(books, Book.class.getClassLoader());// 对应writeList //这一类需要使用类的CREATOR去获取 in.readTypedList(books, Book.CREATOR); //对应writeTypeList //books = in.createTypedArrayList(Book.CREATOR); //对应writeTypeList //这里获取类加载器主要有几种方式 getClass().getClassLoader(); Thread.currentThread().getContextClassLoader(); Book.class.getClassLoader(); } public static final Creator 
    
      CREATOR = new Creator 
     
       () { @Override public ParcelDemo createFromParcel(Parcel in) { return new ParcelDemo(in); } @Override public ParcelDemo[] newArray(int size) { return new ParcelDemo[size]; } }; / * 描述 * * @return */ @Override public int describeContents() { return 0; } / * 序列化 * * @param dest * @param flags */ @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(count); dest.writeString(name); //序列化一个String的集合 dest.writeStringList(tags); // 序列化对象的时候传入要序列化的对象和一个flag, // 这里的flag几乎都是0,除非标识当前对象需要作为返回值返回,不能立即释放资源 dest.writeParcelable(book, 0); // 序列化一个对象的集合有两种方式,以下两种方式都可以 //这些方法们把类的信息和数据都写入Parcel,以使将来能使用合适的类装载器重新构造类的实例.所以效率不高 dest.writeList(books); //这些方法不会写入类的信息,取而代之的是:读取时必须能知道数据属于哪个类并传入正确的Parcelable.Creator来创建对象 // 而不是直接构造新对象。(更加高效的读写单个Parcelable对象的方法是: // 直接调用Parcelable.writeToParcel()和Parcelable.Creator.createFromParcel()) dest.writeTypedList(books); } } 
      
     
    
  

Book类,需要先实现Parcelable,实现步骤就不贴出来了,和普通的对象一样,实现三个过程。

import android.os.Parcel; import android.os.Parcelable; public class Book implements Parcelable { protected Book(Parcel in) { } public static final Creator 
  
    CREATOR = new Creator 
   
     () { @Override public Book createFromParcel(Parcel in) { return new Book(in); } @Override public Book[] newArray(int size) { return new Book[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { } } 
    
  

举个详细的例子:

class MessageFilter() : Parcelable{ var sender: List 
  
    ? = null var messageType: FilterMessageType? = null var time: FilterTime? = null //读数据进行恢复 constructor(parcel: Parcel) : this() { this.sender = mutableListOf() parcel.readList(sender, FilterSender.javaClass.classLoader) messageType = parcel.readParcelable(FilterMessageType.javaClass.classLoader) time = parcel.readParcelable(FilterTime.javaClass.classLoader) } //写数据进行保存 override fun writeToParcel(dest: Parcel?, flags: Int) { dest?.writeList(sender) dest?.writeParcelable(messageType, Parcelable.PARCELABLE_WRITE_RETURN_VALUE) dest?.writeParcelable(time, Parcelable.PARCELABLE_WRITE_RETURN_VALUE) } override fun describeContents(): Int { return 0 } companion object CREATOR : Parcelable.Creator 
   
     { override fun createFromParcel(parcel: Parcel): MessageFilter { return MessageFilter(parcel) } override fun newArray(size: Int): Array 
    
      { return arrayOfNulls(size) } } } //涉及到的相关类都需要实现Parcelable接口 data class FilterSender(val type: String, val id: String): Parcelable { constructor(parcel: Parcel) : this( parcel.readString(), parcel.readString()) override fun writeToParcel(dest: Parcel?, flags: Int) { dest?.writeString(type) dest?.writeString(id) } override fun describeContents(): Int { return 0 } companion object CREATOR : Parcelable.Creator 
     
       { override fun createFromParcel(parcel: Parcel): FilterSender { return FilterSender(parcel) } override fun newArray(size: Int): Array 
      
        { return arrayOfNulls(size) } } } 
       
      
     
    
  

Parcelable和Serializable的区别和比较

Parcelable和Serializable都是实现序列化并且都可以用于Intent间传递数据,Serializable是Java的实现方式,可能会频繁的IO操作,所以消耗比较大,但是实现方式简单 Parcelable是Android提供的方式,效率比较高,但是实现起来复杂一些 , 二者的选取规则是:内存序列化上选择Parcelable, 存储到设备或者网络传输上选择Serializable(当然Parcelable也可以但是稍显复杂)

选择序列化方法的原则

1)在使用内存的时候,Parcelable比Serializable性能高,所以推荐使用Parcelable。

2)Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC。

3)Parcelable不能使用在要将数据存储在磁盘上的情况,因为Parcelable不能很好的保证数据的持续性在外界有变化的情况下。尽管Serializable效率低点,但此时还是建议使用Serializable 。

延伸:

下面介绍下ParcelableSerializable的作用、效率、区别及选择。

1、作用

Serializable的作用是为了保存对象的属性到本地文件、数据库、网络流、rmi以方便数据传输,当然这种传输可以是程序内的也可以是两个程序间的。而Android的Parcelable的设计初衷是因为Serializable效率过慢,为了在程序内不同组件间以及不同Android程序间(AIDL)高效的传输数据而设计,这些数据仅在内存中存在,Parcelable是通过IBinder通信的消息的载体。

从上面的设计上我们就可以看出优劣了。

2、效率及选择

Parcelable的性能比Serializable好,在内存开销方面较小,所以在内存间数据传输时推荐使用Parcelable,如activity间传输数据,而Serializable可将数据持久化方便保存,所以在需要保存或网络传输数据时选择Serializable,因为android不同版本Parcelable可能不同,所以不推荐使用Parcelable进行数据持久化。

3、编程实现

对于Serializable,类只需要实现Serializable接口,并提供一个序列化版本id(serialVersionUID)即可。Parcelable则需要实现writeToParceldescribeContents函数以及静态的CREATOR变量,实际上就是将如何打包和解包的工作自己来定义,而序列化的这些操作完全由底层实现。

4、高级功能上

Serializable序列化不保存静态变量,可以使用Transient关键字对部分字段不进行序列化,也可以覆盖writeObjectreadObject方法以实现序列化过程自定义。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/206402.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 下午3:47
下一篇 2026年3月19日 下午3:47


相关推荐

  • nutch使用入门

    nutch使用入门基本概念 nbsp nbsp crawldb nutch 所处理的所有 url 信息 包括是否被 fetch 过 以及 url 被 fetch 时间 nbsp nbsp linkdb urllinks 包括源 url 和链接文本信息 nbsp nbsp segments 集合 每一个 segment 是一组 fetched 的 urls 单元 segments 包括以下子目录 nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp 1 crawl generate 将要被抓取 urls

    2026年3月17日
    2
  • 蓝牙协议栈初识(ceva蓝牙协议栈)

    在学习的过程中一直有疑问,为什么蓝牙技术突然就产生了呢?蓝牙技术的目的是什么呢?蓝牙技术相对于它所替代的技术存在什么样的优势和劣势呢?蓝牙技术都做了些什么呢?随着我们周围电子产品的增多电子产品之间的信息交互也越来越频繁,但是信息交互方式在无线连接出现之前只能使用有线连接,比如计算机接入键盘,鼠标,主机,扫描仪,打印机,摄像头等等,如果都是用有线连接那就会让你的工作台充满电缆,而且既然是有线的那么…

    2022年4月10日
    137
  • webstorm 格式化代码及常用快捷键

    webstorm 格式化代码及常用快捷键webstorm格式化代码  快捷键 centOS下Ctrl+Shift+l windows下 Ctrl+Alt+lmac下Option+Command+l查找/代替快捷键 说明ctrl+shift+N 通过文件名快速查找工程内的文件(必记)ctrl+shift+alt+N 通过一个字符快速查找位置(必记)ctrl+F 在文件内快速查找代码F3 查找下一个shift+F3 查找上一…

    2022年6月12日
    72
  • c语言bzero函数_hikaricp源码分析

    c语言bzero函数_hikaricp源码分析看《网络编程》,对bzero等函数的实现颇有兴趣,于是便翻看了其源代码的实现。bzero、bcopy、bcmp等函数源码如下:bzero源码:voidbzero(s1,n) void*s1; unsignedn;{ registerchar*t=s1; while(n!=0){ *t++=0; n–; }}bcopy源码如下:

    2022年10月13日
    6
  • 用户态和内核态区别是什么_进程运行在内核态还是用户态

    用户态和内核态区别是什么_进程运行在内核态还是用户态1、linux进程有4GB地址空间,如图所示:3G-4G大部分是共享的,是内核态的地址空间。这里存放整个内核的代码和所有的内核模块以及内核所维护的数据。2、特权级的概念:对于任何操作系统来说,创建一个进程是核心功能。创建进程要做很多工作,会消耗很多物理资源。比如分配物理内存,父子进程拷贝信息,拷贝设置页目录页表等等,这些工作得由特定的进程去做,所以就有了特权级别的概念。最关键的工作必须交给特权级最高的进程去执行,这样可以做到集中管理,减少有限资源的访问和使用冲突。interx86架构的cpu一共有

    2026年1月15日
    4
  • linux sort命令 排序,Linux sort排序方法[通俗易懂]

    linux sort命令 排序,Linux sort排序方法[通俗易懂]在文件的操作过程中,因为文件过多,往往需要进行一下排序,排序方法也就是从小到大排序或者从大到小排序。比如我们从nginx日志中需要找到访问量最长的url,那就需要对请求时间进行一个排序,根据请求时间长短排序后在打印后面的url就能清楚的知道那个url有问题了,废话先不说,看方法:文件排序我们先说一下linux的sort命令,sort命令可以根据我们的需求完成从大到小或者从小到大的排序。注意:sor…

    2022年8月12日
    10

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号