关于对象序列化
来源:互联网 发布:2013知乎年度吐槽精选 编辑:程序博客网 时间:2024/05/23 00:10
在开发Android应用时,我们经常需要将数据进行持久化。对于少量的数据,Android提供了轻量的,以XML格式文件保存的SharedPreferences工具。对于大量的,且需要进行增删改查操作的数据,Android则提供了SQLite数据库。有时,我们希望对内存里的某些结构体数据(比如某个类的实例、ArrayList等)进行持久化,这时,使用SharedPreferences则过于繁琐,而使用数据库则太“大材小用”,其实我们可以直接将其序列化到磁盘上,等到要用时再反序列化。如下面的demo,我们将size为1000的ArrayList<Data>用ObjectOutputStream序列化到磁盘(作为demo,为了简单,直接将IO操作放到主线程,下同):
public class MainActivity extends Activity {public static class Data implements Serializable {private static final long serialVersionUID = 1L;public String str1 = "aa";public String str2 = "bb";public String str3 = "cc";public int int1 = 1;public int int2 = 2;public int int3 = 3;public long long1 = 1L;public long long2 = 2L;public long long3 = 3L;public InnerStruct is = new InnerStruct();}public static class InnerStruct implements Serializable {private static final long serialVersionUID = 1L;public float f = 1.0f;}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);FileOutputStream fos = null;ObjectOutputStream oos = null;try {fos = new FileOutputStream(getApplication().getFilesDir().getAbsolutePath() + "/test.bin");oos = new ObjectOutputStream(fos);ArrayList<Data> data = new ArrayList<Data>();for (int i = 0; i < 1000; i++) {data.add(new Data());}oos.writeObject(data);} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {if (oos != null) {try {oos.close();} catch (IOException e) {e.printStackTrace();}}if (fos != null) {try {fos.close();} catch (IOException e) {e.printStackTrace();}}}}}
运行程序,我们可以看到test.bin文件被正确地写到了/data/data/com.example.iotest/files目录下,大小为67313字节:
但是,这是新手的写法,稍微有点经验的程序员看到这些代码都会指出其IO性能问题:写文件次数过多!是吗?我们来看一下到底写了多少次文件:
这是用Traceview工具获取的数据,在解释这个数据之前,我们需要先从源码层面了解ObjectOutputStream在序列化对象时都干了哪些事情。ObjectOutputStream的主要功能是将复杂的对象进行拆解,真正写文件是由FileOutputStream的write(byte[] buffer, int byteOffset, int byteCount)方法进行,之后的调用路径如下图所示:
我们可以看到JAVA层的最后一个方法是libcore.io.Posix的writeBytes(FileDescriptor fd, Object buffer, int offset, int byteCount),之后就通过JNI调用了native的方法。libcore.io.Posix对应的C++文件是libcore_io_Posix.cpp(源码:http://androidxref.com/4.0.4/xref/libcore/luni/src/main/native/libcore_io_Posix.cpp#),writeBytes对应的C++方法是Posix_writeBytes(JNIEnv* env, jobject, jobject javaFd, jbyteArray javaBytes, jint byteOffset, jint byteCount),如下图所示:
在Posix_writeBytes方法里最后调用了GNU的libc库函数write (int filedes, const void *buffer, size_t size),这里是操作系统层面写文件的地方。
我们回过头来看看Traceview的数据,就可以发现写文件的次数达到了惊人的19052次!
那么我们应该怎样写才会减少写文件的次数呢?答案是用BufferedOutputStream做缓存,再批量写入文件,如下所示:
public class MainActivity extends Activity {public static class Data implements Serializable {private static final long serialVersionUID = 1L;public String str1 = "aa";public String str2 = "bb";public String str3 = "cc";public int int1 = 1;public int int2 = 2;public int int3 = 3;public long long1 = 1L;public long long2 = 2L;public long long3 = 3L;public InnerStruct is = new InnerStruct();}public static class InnerStruct implements Serializable {private static final long serialVersionUID = 1L;public float f = 1.0f;}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);FileOutputStream fos = null;BufferedOutputStream bos = null;ObjectOutputStream oos = null;try {fos = new FileOutputStream(getApplication().getFilesDir().getAbsolutePath() + "/test.bin");bos = new BufferedOutputStream(fos);oos = new ObjectOutputStream(bos);ArrayList<Data> data = new ArrayList<Data>();for (int i = 0; i < 1000; i++) {data.add(new Data());}oos.writeObject(data);oos.flush();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {if (oos != null) {try {oos.close();} catch (IOException e) {e.printStackTrace();}}if (bos != null) {try {bos.close();} catch (IOException e) {e.printStackTrace();}}if (fos != null) {try {fos.close();} catch (IOException e) {e.printStackTrace();}}}}}
这个写法写文件的次数是多少呢?我们用Traceview来看一下:
现在就只有9次了。
我们可以对比一下这两种方法写文件的耗时,无缓存的方法和有缓存的方法各执行10次(单位:ms):
无缓存600456466483473525456470531490有缓存9812611613310010512211779132无缓存方法执行的平均时间为:495ms,有缓存方法执行的平均时间为:112.8ms,时间减少77.2%。
BufferedOutputStream其实很简单,内部的数据结构就是一个byte数组,一个int的计数器:
其默认的buffer大小为8K:
当写文件时,如果buffer已经装不下了,就先将buffer里的数据写入文件,如果可以装下,则将数据先暂时放到buffer里:
- 关于对象序列化
- 关于对象序列化
- 关于 Java 对象序列化
- 关于 对象的 序列化问题
- 关于java对象序列化问题
- 关于java的对象序列化
- 关于Java对象序列化的应用~~~
- 关于对象序列化的问题
- 关于序列化和对象流
- 关于java的对象序列化
- 关于序列化对象.Serializable Object总结.
- 关于对象序列化的问题
- 关于JAVA的对象序列化----------为什么要序列化
- 关于序列化:把某个对象序列化成字节流
- 关于复杂对象的序列化和反序列化
- 关于对象的持久化(序列化)
- 关于android传递对象Parcelable序列化的问题
- 关于 Java 对象序列化的5件事
- 微信入口绑定,微信事件处理,微信API全部操作
- 段的综述
- 解决servlet在post/get传递中文乱码的问题
- 黑马程序员----面向对象之(三)----Exception
- 《Scala编程》学习笔记(11~14章)
- 关于对象序列化
- epoll 机制--epoll_create, epoll_ctl和epoll_wait
- RMQ算法分析
- 人生
- 菜鸟学习Struts2遇到的问题
- 命名空间、using声明和using指示【附送彩蛋】
- centos FTP服务器的架设和配置
- Java知识整理
- 文章标题