详细介绍Android中Parcelable的原理和使用方法

今日推荐

经常阅读博客是个好习惯
推荐技术小黑屋的Blog

引言

本篇文章目的在于详细的理解Parcelable的使用,而不是死记代码

我的CSDN博客地址 (由于csdn的图片经常出现不显示问题,以后就在简书写博客了)

特别鸣谢: Android中Parcel的解读

目录

  1. Parcelable的简单介绍
    1.1 Parcelable的简介
    1.2 Parcel的简介
    1.3 Parcelable中的三大过程介绍(序列化,反序列化,描述)
  2. Parcelable的使用和源码解读
  3. Parcelable中对象和集合的处理
  4. Parcelable和Serializable的区别和比较

1.1 Parcelable的简单介绍

介绍Parcelable不得不先提一下Serializable接口,Serializable是Java为我们提供的一个标准化的序列化接口,那什么是序列化呢? ---- 简单来说就是将对象转换为可以传输的二进制流(二进制序列)的过程,这样我们就可以通过序列化,转化为可以在网络传输或者保存到本地的流(序列),从而进行传输数据 ,那反序列化就是从二进制流(序列)转化为对象的过程.

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 <code>CREATOR</code>
 * of a type that implements the {@link Parcelable.Creator} interface.
 * 
 * <p>A typical implementation of Parcelable is:</p>
 * 
 * <pre>
 * 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<MyParcelable> CREATOR
 *             = new Parcelable.Creator<MyParcelable>() {
 *         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();
 *     }
 * }</pre>
 */

通过源码中的介绍 可以知道,Parcelable接口的实现类是可以通过Parcel写入和恢复数据的,并且必须要有一个非空的静态变量 CREATOR, 而且还给了一个例子,这样我们写起来就比较简单了,但是简单的使用并不是我们的最终目的

通过查看Android源码中Parcelable可以看出,Parcelable实现过程主要分为序列化,反序列化,描述三个过程,下面分别介绍下这三个过程

1.2 Parcel的简介

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

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

Parcel模型

Parcel可以包含原始数据类型(用各种对应的方法写入,比如writeInt(),writeFloat()等),可以包含Parcelable对象,它还包含了一个活动的IBinder对象的引用,这个引用导致另一端接收到一个指向这个IBinder的代理IBinder。

Parcelable通过Parcel实现了read和write的方法,从而实现序列化和反序列化,我们在源码中看一下

Parcel中的Read方法
Parcel中的write方法

可以看出包含了各种各样的read和write方法,最终都是通过native方法实现

Parcel中的native方法

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

到这里,基本上关系都理清了,也明白简单的介绍和原理了,接下来在实现Parcelable之前,介绍下实现Parcelable的三大流程
首先写一个类实现Parcelable接口,会让我们实现两个方法

要实现的方法

1.3.1 描述

其中describeContents就是负责文件描述,首先看一下源码的解读

描述的源码

通过上面的描述可以看出,只针对一些特殊的需要描述信息的对象,需要返回1,其他情况返回0就可以

1.3.2 序列化

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

举个例子:我们将上面的User对象实现序列化,User对象包含三个字段 age,name,isMale

 /**
 * 该方法负责序列化
 * @param dest
 * @param flags
 */
@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeInt(age);
    dest.writeString(name);
    // boolean 可以使用int或byte方式进行存储,怎么存就怎么取
    dest.writeInt(isMale ? 1 : 0);
}

1.3.3 反序列化

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

/**
 * 负责反序列化
 */
public static final Creator<User> CREATOR = new Creator<User>() {
    /**
     * 从序列化后的对象中创建原始对象
     */
    @Override
    public User createFromParcel(Parcel source) {
        return new User(source);
    }

    /**
     * 创建指定长度的原始对象数组
     */
    @Override
    public User[] newArray(int size) {
        return new User[size];
    }
};

public User(Parcel parcel) {
    age = parcel.readInt();
    name = parcel.readString();
    isMale = parcel.readInt() == 1;
}

2. Parcelable的实现和使用

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

这里实现Parcelable也很简单

1.写一个类实现Parcelable然后alt+enter 添加Parcelable所需的代码块,AndroidStudio会自动帮我们实现(这里需要注意如果其中包含对象或集合需要把对象也实现Parcelable)

Paste_Image.png

3. Parcelable中对象和集合的处理

如果实现Parcelable接口的对象中包含对象或者集合,那么其中的对象也要实现Parcelable接口
如果包含对象Author和集合:

public class Book implements Parcelable {

  public int id;
  public String name;
  public boolean isCell;
  public ArrayList<String> tags;
  public Author author;
  // ***** 注意: 这里如果是集合 ,一定要初始化 *****
  public ArrayList<Author> authors = new ArrayList<>();

  public Book() {
  }
}

这里包含一个Author对象和Author集合

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

针对上面Book对象的序列化和反序列化的实现:

反序列化:

//反序列化
public static final Creator<Book> CREATOR = new Creator<Book>() {
    @Override
    public Book createFromParcel(Parcel in) {
        return new Book(in);
    }

    @Override
    public Book[] newArray(int size) {
        return new Book[size];
    }
};

protected Book(Parcel in) {
    id = in.readInt();
    name = in.readString();
    isCell = in.readByte() != 0;

    tags = in.createStringArrayList();

    // 读取对象需要提供一个类加载器去读取,因为写入的时候写入了类的相关信息
    author = in.readParcelable(Author.class.getClassLoader());

    //读取集合也分为两类,对应写入的两类
    //这一类需要用相应的类加载器去获取
    //in.readList(authors, Author.class.getClassLoader()); // 对应writeList

    //这一类需要使用类的CREATOR去获取
    in.readTypedList(authors, Author.CREATOR); //对应writeTypeList
    //authors = in.createTypedArrayList(Author.CREATOR);//对应writeTypeList

    //这里获取类加载器主要有几种方式
    getClass().getClassLoader();
    Thread.currentThread().getContextClassLoader();
    Author.class.getClassLoader();
}

序列化:

@Override
public void writeToParcel(Parcel dest, int flags) {
    //下面三种是基本类型就不多说
    dest.writeInt(id);
    dest.writeString(name);
    dest.writeByte((byte) (isCell ? 1 : 0));

    //序列化一个String的集合
    dest.writeStringList(tags);

    // 序列化对象的时候传入要序列化的对象和一个flag,
    // 这里的flag几乎都是0,除非标识当前对象需要作为返回值返回,不能立即释放资源
    dest.writeParcelable(author, flags);

    // 序列化一个对象的集合有两种方式,以下两种方式都可以

    //这些方法们把类的信息和数据都写入Parcel,以使将来能使用合适的类装载器重新构造类的实例.所以效率不高
    dest.writeList(authors);

    //这些方法不会写入类的信息,取而代之的是:读取时必须能知道数据属于哪个类并传入正确的Parcelable.Creator来创建对象
    // 而不是直接构造新对象。(更加高效的读写单个Parcelable对象的方法是:
    // 直接调用Parcelable.writeToParcel()和Parcelable.Creator.createFromParcel())
    dest.writeTypedList(authors);
}

写入和读取集合有两种方式,
一种是写入类的相关信息,然后通过类加载器去读取, --> writeList | readList
二是不用类相关信息,创建时传入相关类的CREATOR来创建 --> writeTypeList | readTypeList | createTypedArrayList
第二种效率高一些

一定要注意如果有集合定义的时候一定要初始化 like this -->
public ArrayList<Author> authors = new ArrayList<>();

4. Parcelable和Serializable的区别和比较

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

希望这篇文章可以帮助到需要的人,如果还有其他问题或者补充可以联系我~~~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容