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

浅谈Android中的序列化

2015-12-31 11:32 387 查看
转载请附原文链接: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),又能准确的描述一个对象。

写这篇,只是想写点东西,而且再次加深自己的一个意识:返璞归真的去思考问题,能够获得更多。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: