文章参考自任玉刚大神的书籍《Android开发艺术探索》,强烈推荐这本书。java
在进行Android开发的时候咱们有时候须要用到数据的持久化存储,或者在进程之间传递数据。其中就可能须要用到对象的序列化,通过序列化的对象以后能够经过Intent或者Boundle来传输了。接下来仍是想些介绍下吧。android
序列化: 将数据结构或对象转换成二进制串的过程。
反序列化:将在序列化过程当中所生成的二进制串转换成数据结构或者对象的过程。网络
简单来讲,序列化就是将咱们生成的对象进行存储起来(好比磁盘上),以用来未来使用或者在网络上进行传输,而反序列化呢,就是由咱们的以前序列化生成的二进制串从新生成对象的过程。注意,这里咱们反复说的序列化啦,反序列化啦,都是针对的对象,而非类。由于咱们是针对对象进行存取与传输的,而非类,当咱们须要从新获取以前的对象的时候,是直接读取出来的(从文件或网络中),而非根据类new出一个对象,这点是须要注意的。数据结构
序列话的方式有两种,一种是实现Serializable接口,一种是实现Parceable接口,下面会具体介绍这两种方式。ide
这种序列化方式是Java提供的,它的优势是简单,其实Serializable接口是个空接口,于是咱们并不须要实现什么抽象方法,可是咱们却每每须要在类中声明一个静态变量标识(serialVersionUID),但这不是必须的,咱们不声明,依然能够实现序列化,可是这样的话会对反序列化产生必定的影响,可能会在咱们对类作了修改以后而形成对象的反序列化失败。声明方式以下:this
private static final long serialVersionUID = 8711368828010083044L;
注意,这里的值能够是任意值。spa
下面咱们来具体实现下。code
package com.qc.admin.myserializableparceabledemo; import java.io.Serializable; /** * Created by admin on 2016/12/1. */ public class User implements Serializable { private static final long serialVersionUID = 519067123721295773L; public int userId; public String userName; public boolean isMale; public User(int userId, String userName, boolean isMale) { this.userId = userId; this.userName = userName; this.isMale = isMale; } @Override public String toString() { return "User{ " + "userId = " + userId + ", userName = " + userName + ", isMale = " + isMale + " }"; } }
下面是序列化与反序列化过程:xml
private void beginSerizable() throws IOException, ClassNotFoundException { // 序列化 User user = new User(2016, "qian", true); ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(new File(getFilesDir(), "myfile.txt"))); out.writeObject(user); out.close(); // 反序列化 // 注意,这里后面的“/myfile.txt”前面有个斜杠“/”,不然会报“FileNotFoundException”异常 ObjectInputStream in = new ObjectInputStream(new FileInputStream(getFilesDir() + "/myfile.txt")); User mUser = (User) in.readObject(); textView.setText(mUser.toString()); in.close(); Log.i("test",mUser.toString()); }
运行结果截图:对象
注意:若是是在Android项目中调用以上方法,别忘了在Manifest.xml文件中配置以下权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
这种方式是Android提供的方式,相比较前面那种方式来说,这种方式稍微有点复杂,咱们须要本身尽享序列化与反序列化的操做,可是它却更加高效,并不须要执行大量的I/O操做。并且这种方式也是Android推荐的序列化方式,所以咱们应该首选Parceable。只要实现了这个接口,一个类的对象就能够实现序列化并能够经过Intent和Binder进行传递了。下面请看实例:
public class Book implements Parcelable { public String bookTitle; public int bookId; protected Book(Parcel in) { bookTitle = in.readString(); bookId = in.readInt(); } public static final Creator<Book> CREATOR = new Creator<Book>() { @Override public Book createFromParcel(Parcel in) { return new Book(in); } @Override public Book[] newArray(int size) { return new Book[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel parcel, int i) { parcel.writeString(bookTitle); parcel.writeInt(bookId); } }
这里将Book这个类就实现了Parcelable接口,其实在Android Studio IDE中,上述过程很简单,咱们只须要定义一个类,实现Parcelable接口,而后在里面定义咱们的属性或者说是字段,根据提示的错误,按照它提示的方法覆盖相应的方法,以后的一切其实均可以自动生成(不过若是须要构造方法的话,那就须要自动生成了,toString()方法也是本身实现的),因此不用担忧在Android开发中经过实现Parceable接口会比较麻烦,由于AS都会为你自动生成。
上面咱们已经完整的将Book类实现了Parceable接口,那接下来如何序列化和反序列化呢?若是你说,刚才不是已经说过了吗,采用文件读取的方式不久能够了啦...当你那样作的时候,你会发现会报以下的错误:
Why???...什么状况?提示咱们Book类没有实现序列化:
/System.err: java.io.NotSerializableException: com.qc.admin.myserializableparceabledemo.Book
好啦,之因此出现这种问题,并非咱们的实现过程有问题,而是使用该类的方式行不通。到这里咱们就明白了Serializable和Parceable两种方式实现序列化仍是有区别的,刚才咱们也讲了,Parceable更加高效,不会像Serializable那样有大量的I/O操做,这句话的具体含义就道出了Serializable与Parcelable区别:虽然二者都是用于支持序列化、反序列化话操做,可是二者最大的区别在于存储媒介的不一样,Serializable是将序列化后的对象存储在硬盘上,使用I/O读写的方式,而Parcelable是将其存储在内存中,是针对内存的读写,熟悉计算机组成原理的朋友都知道,内存的读写速度显然要远远大于I/O的读写速度,这也是为何Android中推荐使用Parcelable这种方式来实现对象的序列化。
那咱们应该怎么使用经过实现Parcelable接口实现序列化的对象呢?答案是:经过Intent方式传递,除了基本类型外,Intent只能传输序列化以后的对象,对应这两种序列化方式,也有两种相应的方法:
mIntent.getSerializableExtra(string name );
mIntent.getParcelableExtra(String name );
固然,放入的操做就没有这种区分了,都是方法:
mIntent.putExtra();
咱们能够在第一个Activity中将序列化对象放入Intent,在另外一个Activity中取出,好比:
在另外一端获取对象,例如:
Bundle mBundle = getIntent().getExtras(); Book mBook = mBundle.getParcelable("book1");
下面再看类User实现Parceable接口的过程,它内部包含了一个可序列化的类Book,具体细节跟上面的有点不一样:
package com.qc.admin.myserializableparceabledemo; import android.os.Parcel; import android.os.Parcelable; /** * Created by admin on 2016/12/1. */ public class User implements Parcelable { public int userId; public String userName; public boolean isMale; public Book book; public User(int userId, String userName, boolean isMale, Book book) { this.userId = userId; this.userName = userName; this.isMale = isMale; this.book = book; } protected User(Parcel in) { userId = in.readInt(); userName = in.readString(); isMale = in.readByte() != 0; // 此为不一样之处1 // 也能够经过这种方式:book = in.readParcelable(Thread.currentThread().getContextClassLoader()); book = in.readParcelable(Book.class.getClassLoader()); } public static final Creator<User> CREATOR = new Creator<User>() { @Override public User createFromParcel(Parcel in) { return new User(in); } @Override public User[] newArray(int size) { return new User[size]; } }; // 几乎在全部的状况下都应该返回0,只有在当前对象中存在文件描述的时候,此方法返回CONTENTS_FILE_DESCRIPTOR(常量值为1) @Override public int describeContents() { return 0; } // 将对象写入序列化结构中,其中i标识有两种值,0或者1(PARCELABLE_WRITE_RETURN_VALUE) // 为1时表示当前对象须要做为返回值返回,不能当即释放资源,几乎全部状况都为0 @Override public void writeToParcel(Parcel parcel, int i) { parcel.writeInt(userId); parcel.writeString(userName); // 注意这里,并非直接写入boolean值,而是写入整数值 parcel.writeByte((byte) (isMale ? 1 : 0)); // 此为不一样之处2 parcel.writeParcelable(book, i); } @Override public String toString() { return "User{ " + "userId = " + userId + ", userName = " + userName + ", isMale = " + isMale + "book = " + book.toString() + " }"; } }
能够看出,结果已经正确的打印了出来了:
注意:在 Parcelable 中,咱们没法直接写入 boolean 值,而是将其转化为整数值进行保存,这里为 Byte,固然,你也可使用 Int 等。