浅谈Android中的序列化

来源:互联网 发布:淘宝手机尺寸是多少 编辑:程序博客网 时间:2024/05/16 15:18

转载请附原文链接: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),又能准确的描述一个对象。


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










0 0
原创粉丝点击