美文网首页
详细介绍Android中Parcelable的原理和使用方法

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

作者: 许栋挺 | 来源:发表于2018-05-12 07:49 被阅读0次

    今日推荐

    经常阅读博客是个好习惯 

    推荐技术小黑屋的Blog

    引言

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

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

    特别鸣谢: Android中Parcel的解读

    目录

    Parcelable的简单介绍 

    1.1 Parcelable的简介 

    1.2 Parcel的简介 

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

    Parcelable的使用和源码解读

    Parcelable中对象和集合的处理

    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 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();

    *    }

    * }

    */

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

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

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

    1.2 Parcel的简介

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

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

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

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

    可以看出包含了各种各样的read和write方法,最终都是通过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

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    1.3.3 反序列化

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

    /**

    * 负责反序列化

    */

    public static final Creator CREATOR = new Creator() {

        /**

        * 从序列化后的对象中创建原始对象

        */

        @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;

    }

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    2. Parcelable的实现和使用

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

    这里实现Parcelable也很简单

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

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

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

    如果包含对象Author和集合:

    public class Book implements Parcelable {

      public int id;

      public String name;

      public boolean isCell;

      public ArrayList tags;

      public Author author;

      // ***** 注意: 这里如果是集合 ,一定要初始化 *****

      public ArrayList authors = new ArrayList<>();

      public Book() {

      }

    }

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

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

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

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

    反序列化:

    //反序列化

    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];

        }

    };

    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();

    }

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    序列化:

    @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);

    }

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    写入和读取集合有两种方式, 

    一种是写入类的相关信息,然后通过类加载器去读取, –> writeList | readList 

    二是不用类相关信息,创建时传入相关类的CREATOR来创建 –> writeTypeList | readTypeList | createTypedArrayList 

    第二种效率高一些

    一定要注意如果有集合定义的时候一定要初始化 like this –>

    public ArrayList authors = new ArrayList<>();

    4. Parcelable和Serializable的区别和比较

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

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

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Justin_1107/article/details/72903006

    相关文章

      网友评论

          本文标题:详细介绍Android中Parcelable的原理和使用方法

          本文链接:https://www.haomeiwen.com/subject/pqvflftx.html