您的位置:首页 > 移动开发 > Android开发

Android中的序列化机制——Parcel与Parcelable

2014-12-01 15:35 369 查看


当我们在调用远程方法时,需要在进程间传递参数以及返回结果。这种类似的处理方式,需要把数据与进程相关性去除,变成一种中间形式,然后按统一的接口进行读写操作。这样的机制,一般在高级编程语言里都被称为序列化。




在Android世界里处理数据的序列化操作的,使用了一种Parcel类,而能够处理数据序列能力,则是实现Parcelable接口来实现。于是,当我们需要在进程间传输一个对象,则实现这一对象的类必须实现Parcelable接口里定义的相应属性或方法,而在使用这一对象时,则可以使用一个Parcel引用来处理传输时的基本操作。


Parcel和Serialize很类似,只是它是在内存中完成的序列化和反序列化,利用的是连续的内存空间,因此会更加高效。

1. Parcelable接口

Interface for classes whose instances can be written to and restored from a Parcel。 Classes implementing the Parcelable interface must also have a static field called CREATOR, which is an object implementing the Parcelable.Creator
interface。

2.实现Parcelable就是为了进行序列化,那么,为什么要序列化?

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

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

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

3.实现序列化的方法

Android中实现序列化有两个选择:一是实现Serializable接口(是JavaSE本身就支持的),一是实现Parcelable接口(是Android特有功能,效率比实现Serializable接口高效,可用于Intent数据传递,也可以用于进程间通信(IPC))。实现Serializable接口非常简单,声明一下就可以了,而实现Parcelable接口稍微复杂一些,但效率更高,推荐用这种方法提高性能。

注:Android中Intent传递对象有两种方法:一是Bundle.putSerializable(Key,Object),另一种是Bundle.putParcelable(Key,Object)。当然这些Object是有一定的条件的,前者是实现了Serializable接口,而后者是实现了Parcelable接口。

4.选择序列化方法的原则

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

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

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

5.应用场景

需要在多个部件(Activity或Service)之间通过Intent传递一些数据,简单类型(如:数字、字符串)的可以直接放入Intent。复杂类型必须实现Parcelable接口。

6.Serializable实现与Parcelabel实现的区别

1)Serializable的实现,只需要implements Serializable 即可。这只是给对象打了一个标记,系统会自动将其序列化。

2)Parcelabel的实现,不仅需要implements Parcelabel,还需要在类中添加一个静态成员变量CREATOR,这个变量需要实现 Parcelable.Creator 接口。

1)创建Person类,实现Serializable:

public class Person implements Serializable
{
private static final long serialVersionUID = -7060210544600464481L;
private String name;
private int age;

public String getName()
{
return name;
}

public void setName(String name)
{
this.name = name;
}

public int getAge()
{
return age;
}

public void setAge(int age)
{
this.age = age;
}
}


2)创建Book类,实现Parcelable:

public class Book implements Parcelable
{
private String bookName;
private String author;
private int publishDate;

public Book()
{

}

public String getBookName()
{
return bookName;
}

public void setBookName(String bookName)
{
this.bookName = bookName;
}

public String getAuthor()
{
return author;
}

public void setAuthor(String author)
{
this.author = author;
}

public int getPublishDate()
{
return publishDate;
}

public void setPublishDate(int publishDate)
{
this.publishDate = publishDate;
}

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

@Override
public void writeToParcel(Parcel out, int flags)
{
out.writeString(bookName);
out.writeString(author);
out.writeInt(publishDate);
}

public static final Parcelable.Creator<Book> CREATOR = new Creator<Book>()
{
@Override
public Book[] newArray(int size)
{
return new Book[size];
}

@Override
public Book createFromParcel(Parcel in)
{
return new Book(in);
}
};

public Book(Parcel in)
{
bookName = in.readString();
author = in.readString();
publishDate = in.readInt();
}
}


1、describeContents(),Parcelabl所需要的接口方法之一,必须实现。这一方法作用很简单,就是通过返回的整形来描述这一Parcel是起什么作用的,通过这一整形每个bit来描述其类型,一般会返回0。

2、writeToParcel(),Parcelabl所需要的接口方法之二,必须实现。writeToParcel()方法的作用是发送,就是将类所需要传输的属性写到Parcel里,被用来提供发送功能的Parcel,会作为第一个参数传入,于是在这个方法里都是使用writeInt()、writeLong()写入到Parcel里。这一方法的第二参数是一个flag值,可以用来指定这样的发送是单向还是双向的,可以与aidl的in、out、inout三种限定符匹配。

3、CREATOR对象,Parcelable接口所需要的第三项,必须提供实现,但这是一个是接口对象。正如我们看到的,这一CREATOR对象,是使用模板类Parcelable.Creator,套用到TaskInfo来得到的,Parcelable.Creator<TaskInfo>。这个CREATOR对象在很大程度上是一个工厂(Factory)类,用于远程对象在接收端的创建。从某种意义上来说,writeToParcel()与CREATOR是一一对应的,发送端进程通过writeToParcel(),使用一个Parcel对象将中间结果保存起来,而接收端进程则会使用CREATOR对象把作为Parcel对象的中间对象再恢复出来,通过类的初始化方法以这个Parcel对象为基础来创建新对象。后续的4-6,则是完成这个CREATOR对象的实现。

4、createFromParcel(),这是ParcelableCreator<T>模板类所必须实现的接口方法,提供从Parcel转义出新的对象的能力。接收端来接收传输过来的Parcel对象时,便会以这一个接口方法来取得对象。我们这里直接调用基于Parcel 的类的初始化方法,然后将创建的对象返回。

5、newArray(),这是ParcelableCreator<T>模板类所必须实现的另一个接口方法,但这一方法用于创建多个这种实现了Parcelable接口的类。通过这一方法,CREATOR对象不光能创建单个对象,也能返回多个创建好的空对象,但多个对象不能以某个Parcel对象为基础创建,于是会使用默认的类创始化方法。

6、实现具体的以Parcel为参照的初始化方法,这并非必须,我们也可以在createFromParcel()里直接根据Parcel的值赋值到对象来实现,但这样实现则更清晰。这一方法,基本上与writeToParcel()是成对的,以什么顺序将对象属性写入Parcel,则在createFromParcel()会就会以同样的顺序对象属性从Parcel里读出来,使用Parcel的readInt()、readLong()等方法来完成。

7.应用实例

就应用程序而言,最常见使用Parcel类的场景就是在Activity间传递数据。没错,在Activity间使用Intent传递数据的时候,可以通过Parcelable机制传递复杂的对象。

在下面的程序中,MyColor用于保存一个颜色值,MainActivity在用户点击屏幕时将MyColor对象设成红色,传递到SubActivity中,此时SubActivity的TextView显示为红色的背景;当点击SubActivity时,将颜色值改为绿色,返回MainActivity,期望的是MainActivity的TextView显示绿色背景。

MyColor类的实现代码:



package com.test;

import android.graphics.Color;
import android.os.Parcel;
import android.os.Parcelable;

public class MyColor implements Parcelable {
private int color=Color.BLACK;

MyColor(){
color=Color.BLACK;
}

MyColor(Parcel in){
color=in.readInt();
}

public int getColor(){
return color;
}

public void setColor(int color){
this.color=color;
}

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

@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(color);
}

public static final Parcelable.Creator<MyColor> CREATOR
= new Parcelable.Creator<MyColor>() {
public MyColor createFromParcel(Parcel in) {
return new MyColor(in);
}

public MyColor[] newArray(int size) {
return new MyColor[size];
}
};
}


MainActivity的代码:

package com.test;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.view.MotionEvent;

public class MainActivity extends Activity {
private final int SUB_ACTIVITY=0;
private MyColor color=new MyColor();

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode==SUB_ACTIVITY){
if (resultCode==RESULT_OK){
if (data.hasExtra("MyColor")){
color=data.getParcelableExtra("MyColor");  //<span style="font-size: 14px; white-space: pre;">反序列化后是一个新的MyColor对象</span>
findViewById(R.id.text).setBackgroundColor(color.getColor());
}
}
}
}

@Override
public boolean onTouchEvent(MotionEvent event){
if (event.getAction()==MotionEvent.ACTION_UP){
Intent intent=new Intent();
intent.setClass(this, SubActivity.class);
color.setColor(Color.RED);
intent.putExtra("MyColor", color);
startActivityForResult(intent,SUB_ACTIVITY);
}
return super.onTouchEvent(event);
}

}


SubActivity的代码:

package com.test;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.view.MotionEvent;
import android.widget.TextView;

public class SubActivity extends Activity {
private MyColor color;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
((TextView)findViewById(R.id.text)).setText("SubActivity");
Intent intent=getIntent();
if (intent!=null){
if (intent.hasExtra("MyColor")){
color=intent.getParcelableExtra("MyColor");
findViewById(R.id.text).setBackgroundColor(color.getColor());
}
}
}

@Override
public boolean onTouchEvent(MotionEvent event){
if (event.getAction()==MotionEvent.ACTION_UP){
Intent intent=new Intent();
if (color!=null){
color.setColor(Color.GREEN);
intent.putExtra("MyColor", color);
}
setResult(RESULT_OK,intent);
finish();
}
return super.onTouchEvent(event);
}
}


在MainActivity的onActivityResult()中,有一句color=data.getParcelableExtra("MyColor"),这说明的是反序列化后是一个新的MyColor对象,因此要想使用这个对象,我们做了这个赋值语句。

如果数据本身是IBinder类型,那么反序列化的结果就是原对象,而不是新建的对象,很显然,如果是这样的话,在反序列化后在MainActivity中就不再需要color=data.getParcelableExtra("MyColor")这句了。因此,换一种MyColor的实现方法,令其中的int color成员变量使用IBinder类型的成员变量来表示。

新建一个BinderData类继承自Binder,代码如下:

package com.test;

import android.os.Binder;

public class BinderData extends Binder {
public int color;
}


修改MyColor的代码如下:

package com.test;

import android.graphics.Color;
import android.os.Parcel;
import android.os.Parcelable;

public class MyColor implements Parcelable {
private BinderData data=new BinderData();

MyColor(){
data.color=Color.BLACK;
}

MyColor(Parcel in){
data=(BinderData) in.readValue(BinderData.class.getClassLoader());
}

public int getColor(){
return data.color;
}

public void setColor(int color){
data.color=color;
}

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

@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeValue(data);
}

public static final Parcelable.Creator<MyColor> CREATOR
= new Parcelable.Creator<MyColor>() {
public MyColor createFromParcel(Parcel in) {
return new MyColor(in);
}

public MyColor[] newArray(int size) {
return new MyColor[size];
}
};
}


去掉MainActivity的onActivityResult()中的color=data.getParcelableExtra("MyColor")一句,变成:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode==SUB_ACTIVITY){
if (resultCode==RESULT_OK){
if (data.hasExtra("MyColor")){
findViewById(R.id.text).setBackgroundColor(color.getColor());
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: