zoukankan      html  css  js  c++  java
  • Android中Parcelable的原理和使用方法

    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 <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实现过程主要分为序列化,反序列化,描述三个过程,下面分别介绍下这三个过程。

    Parcel的简介

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

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

       

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

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

    什么是序列化

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

    到这里,基本上关系都理清了,也明白简单的介绍和原理了,接下来在实现Parcelable之前,介绍下实现Parcelable的三大流程 .
    [图片上传中...(image.png-73288d-1522235519935-0)]

    我先把代码贴出来

    public class Album implements Parcelable {
    
        /**
         * 负责反序列化
         */
        private static final Creator<Album> CREATOR = new Creator<Album>() {
            /**
             * 从序列化对象中,获取原始的对象
             * @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;
    
    /**
     * Created by fengxing on 2018/3/28.
     */
    
    public class ParcelDemo implements Parcelable {
    
        private int count;
        private String name;
        private ArrayList<String> tags;
        private Book book;
        // ***** 注意: 这里如果是集合 ,一定要初始化 *****
        private ArrayList<Book> 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<ParcelDemo> CREATOR = new Creator<ParcelDemo>() {
            @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;
    
    /**
     * Created by fengxing on 2018/3/28.
     */
    
    public class Book implements Parcelable {
    
        protected Book(Parcel in) {
        }
    
        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];
            }
        };
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
        }
    }

    写入和读取集合有两种方式,
    一种是写入类的相关信息,然后通过类加载器去读取, –> writeList | readList
    二是不用类相关信息,创建时传入相关类的CREATOR来创建 –> writeTypeList | readTypeList | createTypedArrayList
    第二种效率高一些
    一定要注意如果有集合定义的时候一定要初始化 like this –>

    public ArrayList<T> demo = new ArrayList<>();
    

    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 。

  • 相关阅读:
    centos在设置时区
    Qt那点事儿(三) 论父对象与子对象的关系
    Qt多线程应用--QRunnable
    Qt学习之如何启动和终止一个线程
    Qt 互斥量 QMutex
    Qt网络获取本机网络信息
    QT程序拷贝 转移 改变运行环境
    Qt调用dll中的功能函数
    用Qt生成dll类库及调用方法
    Qt DLL总结【二】-创建及调用QT的 DLL
  • 原文地址:https://www.cnblogs.com/JasonLGJnote/p/11876156.html
Copyright © 2011-2022 走看看