Android夸进程通信机制二:Parcel 与 Parcelable


Android夸进程通信机制系列:
Android夸进程通信机制一:多进程简介
Android夸进程通信机制二:Parcel 与 Parcelable
Android夸进程通信机制三:Messenger与Message
Android夸进程通信机制四:使用 Bundle进行进程间通信
Android夸进程通信机制五:使用文件共享进行进程间通信
Android夸进程通信机制六:使用ContentProvider进行进程间通信
Android夸进程通信机制七:使用 Socket进行进程间通信
Android夸进程通信机制八:使用 AIDL进行进程间通信
Android夸进程通信机制九:AIDL深入了解
...


一、前言

在之前一节,我们了解到,进程间通信的数据,除了基本数据类型,其他的数据都要进行序列化,而Parcelable是Android为我们提供的序列化的接口。

二、Serializable

在了解Parcel 与 Parcelable前,我们不得不先了解一下JAVA的Serializable

Serializable是Java为我们提供的一个标准化的序列化接口

序列化和反序列化的概念

那什么是序列化呢,这几涉及到系列化和反序列化

把对象转换为字节序列的过程称为对象的序列化。
把字节序列恢复为对象的过程称为对象的反序列化。

对象的序列化主要用途:

  • 1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
  • 2) 在网络上传送对象的字节序列。
  • 2) 进程间传递对象。

在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。

当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。

三、Parcel 与 Parcelable

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

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

Parcelable接口

Android采用Parcel来表示封装消息数据。这个是通过IBinder通信的消息的载体。需要明确的是Parcel用来存放数据的是内存(RAM),而不是永久性介质(Nand等)。

Parcelable,定义了将数据写入Parcel,和从Parcel中读出的接口。如果需要将目标数据(实体类)封装到消息中去,就该实体类就必须实现这一接口,让该实体类可序列化和反序列化。

接口定义如下

public interface Parcelable {
    //内容描述接口,基本不用管
    public int describeContents();
    //写入接口函数,打包
    public void writeToParcel(Parcel dest, int flags);
     //读取接口,目的是要从Parcel中构造一个实现了Parcelable的类的实例处理。因为实现类在这里还是不可知的,所以需要用到模板的方式,继承类名通过模板参数传入。
    //为了能够实现模板参数的传入,这里定义Creator嵌入接口,内含两个接口函数分别返回单个和多个继承类实例。
    public interface Creator<T> {
           public T createFromParcel(Parcel source);
           public T[] newArray(int size);
    }
}

通过定义可以知道,Parcelable接口的实现类是可以通过Parcel写入和恢复数据的,并且必须要有一个非空的静态变量 CREATOR
Parcel可以包含原始数据类型(用各种对应的方法写入,比如writeInt(),writeFloat()等),可以包含Parcelable对象,它还包含了一个活动的IBinder对象的引用,这个引用导致另一端接收到一个指向这个IBinder的代理IBinder。

四、实现Parcelable

Parcelable实现过程主要分为序列化,反序列化,描述三个过程,下面通过一个音乐数据类MuiscData的例子,来讲解Parcelable的实现。

public class MuiscData {
    //专辑名称
    public String mAlbum;
    //封面地址
    public String mCover;
    //歌名
    public String mName;
    //演唱人
    public String mSinger;

    private MuiscData(Builder builder) {
        this.mAlbum = builder.mAlbum;
        this.mCover = builder.mCover;
        this.mName = builder.mName;
        this.mSinger = builder.mSinger;
    }

    public void setmAlbum(String mAlbum) {
        this.mAlbum = mAlbum;
    }

    public void setmCover(String mCover) {
        this.mCover = mCover;
    }

    public void setmName(String mName) {
        this.mName = mName;
    }

    public void setmSinger(String mSinger) {
        this.mSinger = mSinger;
    }

    public String getmAlbum() {
        return mAlbum;
    }

    public String getmCover() {
        return mCover;
    }

    public String getmName() {
        return mName;
    }

    public String getmSinger() {
        return mSinger;
    }

    public static class Builder {
        //专辑名称
        public String mAlbum;
        //封面地址
        public String mCover;
        //歌名
        public String mName;
        //演唱人
        public String mSinger;

        public MuiscData build() {
            return new MuiscData(this);
        }

        public Builder album(String album){
            this.mAlbum = album;
            return  this;
        }

        public Builder cover(String cover){
            this.mCover = cover;
            return  this;
        }

        public Builder name(String name){
            this.mName = name;
            return  this;
        }

        public Builder singer(String singer){
            this.mSinger = singer;
            return  this;
        }
    }
}

这就是我们要传输的数据的格式,要将其在进程间进行传输,就要对其实现序列化,也就是实现Parcelable接口,这个接口中需要我们实现的主要方法有

writeToParcel :完成序列化
describe :描述
ContentsParcelable.Creator<?> :对对象进行反序列化并且返回一个对象实例。

也就是Parcelable实现的三个过程

接下来,我们就把MuiscData类实现Parcelable接口

public class MuiscData implements Parcelable {
  ...
  ...
  ...
  protected MuiscData(Parcel in) {
        mAlbum = in.readString();
        mCover = in.readString();
        mName = in.readString();
        mSinger = in.readString();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(mAlbum);
        dest.writeString(mCover);
        dest.writeString(mName);
        dest.writeString(mSinger);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator<MuiscData> CREATOR = new Creator<MuiscData>() {
        @Override
        public MuiscData createFromParcel(Parcel in) {
            return new MuiscData(in);
        }

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

完成了这三个过程,就可以对对象进行序列化了,也就是说我们可以传递对象了。

最后,我们就可以在俩个不同进程(或相同进程)的activity传递数据了。

发送者MusicActivity

public class MusicActivity extends Activity {
  ...
  ...
  ...
    public void sentData() {
        String coverUri = "https://gss1.bdstatic.com/-vo3dSag_xI4khGkpoWK1HF6hhy/baike/c0%3Dbaike150%2C5%2C5%2C150%2C50/sign=1a322bc2c9fcc3cea0cdc161f32cbded/d01373f082025aaff92dd92bfaedab64034f1a36.jpg";
        MuiscData musicD = new MuiscData.Builder().album("七里香").cover(coverUri).name("七里香").singer("周杰伦").build();
        Intent intent = new Intent(this, OtherActivity.class);
        Bundle b = new Bundle();
        b.putParcelable("music", musicD);
        intent.putExtras(b);
        this.startActivity(intent);
    }
  ...
  ...
  ...
}

接受者OtherActivity

public class OtherActivity extends Activity {
  ...
  ...
  ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.myprocesses_activity_layout);

        if (getIntent().getExtras().containsKey("music")) {
            MuiscData musicD = getIntent().getExtras().getParcelable("music");
        }
    }
  ...
  ...
  ...
}

五、结语
Parcelable和Serializable都是实现序列化并且都可以用于Intent间传递数据。

Serializable是Java的实现方式,可能会频繁的IO操作,所以消耗比较大,但是实现方式简单 。
Parcelable是Android提供的方式,效率比较高,但是实现起来复杂一些 。

二者的选取规则是:内存序列化上选择Parcelable, 存储到设备或者网络传输上选择Serializable(当然Parcelable也可以但是稍显复杂)

Parcel 与 Parcelable就讲到这里,下次继续讲IPC。

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

推荐阅读更多精彩内容