浅谈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类实现序列化
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接口内容
那么实现了接口之后要做哪些工作呢?
1.重写describeContents(),默认返回0就OK了。
2.重写writeToParcel(),该方法将对象序列化为一个Parcel对象,即:将类的数据写入外部提供的Parcel中,打包需要传递的数据到Parcel容器保存,以便从 Parcel容器获取数据
3.实例化静态内部对象CREATOR实现接口Parcelable.Creator
给一个例子:
总之,用起来的时候比较烦,要写很多东西,而且非常重要的一点就是,
这两个方法写和读的顺序要一致。
像我这么爱(就)动(是)脑(懒)的人,是不喜欢这么做的。虽然提出用json来传的人是啥时候想的我是不知道的。
将对象转json(String格式的),因为string不是复杂格式,直接putStringExtra就行了(注意,只能传一条String数据),想要多条,我就ArrayList<String>了。再反过来想想,json是一种非常好的格式,既是一串字节(我们传递的String),又能准确的描述一个对象。
写这篇,只是想写点东西,而且再次加深自己的一个意识:返璞归真的去思考问题,能够获得更多。
首先,的确是想写点东西安慰自己还在坚持写东西。
写这个,只是念头中有这个东西,前段时间面试几个刚接受过培训的新人的时候,问了一个看有没有灵性的问题,因为现在用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),又能准确的描述一个对象。
写这篇,只是想写点东西,而且再次加深自己的一个意识:返璞归真的去思考问题,能够获得更多。
相关文章推荐
- 欢迎使用CSDN-markdown编辑器
- Android资源管理框架(Asset Manager)简要介绍和学习计划
- Android屏幕适配
- Android Gradle打渠道包
- Android开发过程中遇到的关于so文件的问题
- [Android Studio] Android studio 多渠道打包(超简洁版)
- Android之屏幕测试
- 关于Android Studio 和 Gradle的那些事儿
- Plugin is too old,please update to a more recent version,or set ANDROID_DAILY_OVERRIDE environment v
- android 数据存储----文件方式 总结
- Android高德地图开发--读取解析KML文件并显示在地图上
- 利用 LeakCanary 来检查 Android 内存泄漏
- Android小技巧:json转map
- Android调用系统相机与图库
- 自己收集了几个android 开源框架连接
- Android中使用SQLite3 命令行查看内嵌数据库的方法
- Android Native中Android.mk编译链接时命名冲突的问题
- Android.mk报No rule to make target问题的一种解决方法
- Android 序列化 Parcelable接口用法
- android include标签的使用,在RelativeLayout中使用include标签需注意!!!!!