浅谈Android中的序列化

转载请附原文链接:http://blog.csdn.net/a774057695/article/details/50440617


首先,的确是想写点东西安慰自己还在坚持写东西。

写这个,只是念头中有这个东西,前段时间面试几个刚接受过培训的新人的时候,问了一个看有没有灵性的问题,因为现在用xml的太少了,顺口问了一下json的解析库会不会使用,比如fastjson,(当然不会深入去问的,毕竟刚出来),然后就有目的的问了一个看有没有灵性,擅不擅长思考的问题咯:app内跨activity传值、传对象怎么做,倒是都在答parcelable接口的东西,追问有没有其他省力的方法时,没有人说用json的。好吧,这一篇就大概回顾一些相关的内容。

谈parcelable接口之前,我们先看看Java 中的序列化Serializable:


1、首先回顾下:什么是序列化和反序列化,纯粹定义咯,(定义往往很精练,值得反复推敲,总能获得新收获)

Serialization(序列化)是一种将对象以一连串的字节描述的过程;

deserialization(反序列化)是一种将一连串的字节重建为一个对象的过程。


也就是说,这里只会牵涉到: 对象、一连串字节。

2、那么,哪些情景需要进行序列化 ?
a)将一个对象以“一连串的字节”形式保存时,例如写到一个文件、数据库。
b)使用套接字在网络上传送对象时,而且我们都会想到流

c)通过序列化在进程间传输对象时 ——这个和我们问题是相关的


3、序列化怎么做?
java中,将需要序列化的类实现Serializable接口即可,需要注意:Serializable接口中没有定义任何方法。

4、序列化和反序列化demo


序列化一个对象,首先要创建某些OutputStream(例如FileOutputStream、ByteArrayOutputStream等),然后将这些OutputStream封装在一个ObjectOutputStream中。

然后调用writeObject()方法,就可以将对象序列化,并将其发送给OutputStream

注意:我们之前提到序列化只和 对象,字节相关,所以不能使用Reader、Writer等和字符相关的东西。

顺理成章的,反序列需要将一个InputStream(例如FileInputstream、ByteArrayInputStream等)封装在ObjectInputStream内,然后调用readObject(),就可以获得想要的对象。

以下是一个demo,对Test类实现序列化
package test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
 * @ClassName: Serialization
 * @Description: TODO
 * @date 2015年12月31日 上午11:56:46
 * 
 * @author leobert.lan
 * @version 1.0
 */
public class Serialization {

	public static void main(String[] args) {
		try {
			ObjectOutputStream oos = new ObjectOutputStream(
					new FileOutputStream("my.out"));
			Test mTest = new Test();
			mTest.str1 = "111";
			mTest.str2 = "222";
			oos.writeObject(mTest);
			oos.flush();
			oos.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		deserialization();
	}

	/** 
	 * @Title: deserialization 
	 * @Description: 反序列化
	 * @author: leobert.lan    
	 */
	public static void deserialization() {
		ObjectInputStream oin = null;
		try {
			oin = new ObjectInputStream(new FileInputStream("my.out"));
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		Test test = null;
		try {
			test = (Test) oin.readObject();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println("str1 :" + test.str1);
		System.out.println("str2 :" + test.str2);

	}
}

class Test implements Serializable {
	/**
	 * serialVersionUID:TODO(用一句话描述这个变量表示什么).
	 */
	private static final long serialVersionUID = 1L;
	String str1;
	String str2;
}

5、序列化ID,serialVersionUID

刚才的代码中出现了一个静态常量:serialVersionUID

它的作用,我也说不出一朵花来,简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。

序列化 ID 在 Eclipse 下提供了两种生成策略,一个是固定的 1L,一个是随机生成一个不重复的 long 类型数据(有人说实际上是使用 JDK 工具生成,还有人进一步说是根据当前类结构哈希出来的)。


6.我们可能比较关心序列化前和序列化后的对象是一致的吗?

进一步说,关心的是:是同一个对象,还是两个内容一致的对象


当然是两个对象,只是内容一致,内存地址是不一致的,也就是说,你操作其中一个,另一个是不受影响的。

引用一句话吧,“通过序列化操作,我们可以实现对任何可Serializable对象的“深度复制(deep copy)”——这意味着我们复制的是整个对象网,而不仅仅是基本对象及其引用。对于同一流的对象,他们的地址是相同,说明他们是同一个对象,但是与其他流的对象地址却不相同。也就说,只要将对象序列化到单一流中,就可以恢复出与我们写出时一样的对象网,而且只要在同一流中,对象都是同一个。”

需要注意的是:

1类静态常量是类层面的,不是对象层面的,所以serialVersionUID是不会被序列化的(请不要形而上的理解);

2当一个父类实现序列化,子类已然实现了序列化。因为序列化是通过实现Serializable接口;
3当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化;

4transient修饰的变量不能被序列化


上面的内容说的是java中的序列化,我们看看Android中的序列化


Android当然可以像java一样使用Serializable接口实现序列化,但是并不推荐,还有就是实现Parcelable接口,这是Android特有的,效率比实现Serializable接口高,可结合Intent传递数据,也可以用于进程间通信(IPC))。实现Parcelable接口相对复杂一些。

Android中Intent传递对象有两种方法:

1:Bundle.putSerializable(Key,Object);需要实现Serializable接口

2::Bundle.putParcelable(Key,Object);需要实现Parcelable接口


1.了解一下parcelable接口内容

public interface Parcelable {
    /**
     * Flag for use with {@link #writeToParcel}: the object being written
     * is a return value, that is the result of a function such as
     * "<code>Parcelable someFunction()</code>",
     * "<code>void someFunction(out Parcelable)</code>", or
     * "<code>void someFunction(inout Parcelable)</code>".  Some implementations
     * may want to release resources at this point.
     */
    public static final int PARCELABLE_WRITE_RETURN_VALUE = 0x0001;
    
    /**
     * Bit masks for use with {@link #describeContents}: each bit represents a
     * kind of object considered to have potential special significance when
     * marshalled.
     */
    public static final int CONTENTS_FILE_DESCRIPTOR = 0x0001;
    
    /**
     * Describe the kinds of special objects contained in this Parcelable's
     * marshalled representation.
     *  
     * @return a bitmask indicating the set of special object types marshalled
     * by the Parcelable.
     */
    public int describeContents();
    
    /**
     * Flatten this object in to a Parcel.
     * 
     * @param dest The Parcel in which the object should be written.
     * @param flags Additional flags about how the object should be written.
     * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
     */
    public void writeToParcel(Parcel dest, int flags);

    /**
     * Interface that must be implemented and provided as a public CREATOR
     * field that generates instances of your Parcelable class from a Parcel.
     */
    public interface Creator<T> {
        /**
         * Create a new instance of the Parcelable class, instantiating it
         * from the given Parcel whose data had previously been written by
         * {@link Parcelable#writeToParcel Parcelable.writeToParcel()}.
         * 
         * @param source The Parcel to read the object's data from.
         * @return Returns a new instance of the Parcelable class.
         */
        public T createFromParcel(Parcel source);
        
        /**
         * Create a new array of the Parcelable class.
         * 
         * @param size Size of the array.
         * @return Returns an array of the Parcelable class, with every entry
         * initialized to null.
         */
        public T[] newArray(int size);
    }

    /**
     * Specialization of {@link Creator} that allows you to receive the
     * ClassLoader the object is being created in.
     */
    public interface ClassLoaderCreator<T> extends Creator<T> {
        /**
         * Create a new instance of the Parcelable class, instantiating it
         * from the given Parcel whose data had previously been written by
         * {@link Parcelable#writeToParcel Parcelable.writeToParcel()} and
         * using the given ClassLoader.
         *
         * @param source The Parcel to read the object's data from.
         * @param loader The ClassLoader that this object is being created in.
         * @return Returns a new instance of the Parcelable class.
         */
        public T createFromParcel(Parcel source, ClassLoader loader);
    }
}
直接copy了android中的源码
那么实现了接口之后要做哪些工作呢?

1.重写describeContents(),默认返回0就OK了。

2.重写writeToParcel(),该方法将对象序列化为一个Parcel对象,即:将类的数据写入外部提供的Parcel中,打包需要传递的数据到Parcel容器保存,以便从 Parcel容器获取数据
3.实例化静态内部对象CREATOR实现接口Parcelable.Creator

给一个例子:

package com.example.androidtest;


import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;


public class MainActivity extends Activity {
<span style="white-space:pre">	</span>
<span style="white-space:pre">	</span>@Override
<span style="white-space:pre">	</span>protected void onCreate(Bundle savedInstanceState) {
<span style="white-space:pre">		</span>super.onCreate(savedInstanceState);
<span style="white-space:pre">		</span>setContentView(R.layout.activity_main);
<span style="white-space:pre">		</span>Test t = new Test();
<span style="white-space:pre">		</span>t.setmIntData(1);
<span style="white-space:pre">		</span>t.setmStringData("str");
<span style="white-space:pre">		</span>Intent i = new Intent(MainActivity.this,SecondActivity.class);
<span style="white-space:pre">		</span>Bundle b = new Bundle();
<span style="white-space:pre">		</span>b.putParcelable("key", t);
<span style="white-space:pre">		</span>i.putExtras(b);
<span style="white-space:pre">		</span>startActivity(i);
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>
<span style="white-space:pre">	</span>
<span style="white-space:pre">	</span>
}


class Test implements Parcelable {
<span style="white-space:pre">	</span>private int mIntData;
<span style="white-space:pre">	</span>
<span style="white-space:pre">	</span>private String mStringData;
<span style="white-space:pre">	</span>
<span style="white-space:pre">	</span>public int getmIntData() {
<span style="white-space:pre">		</span>return mIntData;
<span style="white-space:pre">	</span>}


<span style="white-space:pre">	</span>public void setmIntData(int mIntData) {
<span style="white-space:pre">		</span>this.mIntData = mIntData;
<span style="white-space:pre">	</span>}


<span style="white-space:pre">	</span>public String getmStringData() {
<span style="white-space:pre">		</span>return mStringData;
<span style="white-space:pre">	</span>}


<span style="white-space:pre">	</span>public void setmStringData(String mStringData) {
<span style="white-space:pre">		</span>this.mStringData = mStringData;
<span style="white-space:pre">	</span>}


<span style="white-space:pre">	</span>public int describeContents() {
<span style="white-space:pre">		</span>return 0;
<span style="white-space:pre">	</span>}


<span style="white-space:pre">	</span>public void writeToParcel(Parcel out, int flags) {
<span style="white-space:pre">		</span>out.writeInt(mIntData);
<span style="white-space:pre">		</span>out.writeString(mStringData);
<span style="white-space:pre">	</span>}


<span style="white-space:pre">	</span>public static final Parcelable.Creator<Test> CREATOR = new Parcelable.Creator<Test>() {
<span style="white-space:pre">		</span>public Test createFromParcel(Parcel in) {
<span style="white-space:pre">			</span>return new Test(in);
<span style="white-space:pre">		</span>}


<span style="white-space:pre">		</span>public Test[] newArray(int size) {
<span style="white-space:pre">			</span>return new Test[size];
<span style="white-space:pre">		</span>}
<span style="white-space:pre">	</span>};


<span style="white-space:pre">	</span>private Test(Parcel in) {
<span style="white-space:pre">		</span>mIntData = in.readInt();
<span style="white-space:pre">		</span>mStringData = in.readString();
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>
<span style="white-space:pre">	</span>public Test() {}
}

package com.example.androidtest;


import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;




public class SecondActivity extends Activity {
<span style="white-space:pre">	</span>private final String tag = "SecondActivity";
<span style="white-space:pre">	</span>@Override
<span style="white-space:pre">	</span>protected void onCreate(Bundle savedInstanceState) {
<span style="white-space:pre">		</span>super.onCreate(savedInstanceState);
<span style="white-space:pre">		</span>setContentView(R.layout.activity_second);
<span style="white-space:pre">		</span>
<span style="white-space:pre">		</span>Intent i = getIntent();
<span style="white-space:pre">		</span>Test t = i.getParcelableExtra("key");
<span style="white-space:pre">		</span>Log.d(tag, "int data:"+t.getmIntData()+"\r\nString data:"+t.getmStringData());
<span style="white-space:pre">	</span>}


}

总之,用起来的时候比较烦,要写很多东西,而且非常重要的一点就是,
public void writeToParcel(Parcel out, int flags) {
<span>		</span>out.writeInt(mIntData);
<span>		</span>out.writeString(mStringData);
<span>	</span>}

<span>	</span>private Test(Parcel in) {
<span>		</span>mIntData = in.readInt();
<span>		</span>mStringData = in.readString();
<span>	</span>}

这两个方法写和读的顺序要一致。


像我这么爱(就)动(是)脑(懒)的人,是不喜欢这么做的。虽然提出用json来传的人是啥时候想的我是不知道的。

将对象转json(String格式的),因为string不是复杂格式,直接putStringExtra就行了(注意,只能传一条String数据),想要多条,我就ArrayList<String>了。再反过来想想,json是一种非常好的格式,既是一串字节(我们传递的String),又能准确的描述一个对象。


写这篇,只是想写点东西,而且再次加深自己的一个意识:返璞归真的去思考问题,能够获得更多。










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值