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


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


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


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


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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值