该文章是一个系列文章, 是本人在 Android 开发的漫漫长途上的一点感想和记录, 我会尽量按照先易后难的顺序进行编写该系列该系列引用了 Android 开发艺术探索以及深入理解 Android 卷,, 中的相关知识, 另外也借鉴了其他的优质博客, 在此向各位大神表示感谢, 膜拜!!! 另外, 本系列文章知识可能需要有一定 Android 开发基础和项目经验的同学才能更好理解, 也就是说该系列文章面向的是 Android 中高级开发工程师
前言
上一篇中我们比较详尽的分析了 ServiceManager 那么本篇我们来讲一下 Android 序列化的相关知识为什么跨度那么大, 因为任性? 其实不是的, 同志们还记得上两篇出现的 Parcel 吗, Parcel 是一个容器, 他可以包含数据或者是对象引用, 并且能够用于 Binder 的传输同时支持序列化以及跨进程之后进行反序列化, 同时其提供了很多方法帮助开发者完成这些功能从上面的描述可以看出 Parcel 是进程间通信的数据载体我们常常需要持久化一些对象, 除了数据库等持久化方案之外, 把对象转换成字节数组并通过流的方式存储在本地也是一个不错的方法, 另外当我们需要通过 Intent 和 Binder 传输数据是就需要使用序列化后的数据
Java 中的 Serializable
Serializable 是 Java 所提供的一个序列化接口, 它是一个空接口, 为对象提供标准的序列化和反序列化操作使用 Serializable 来实现序列化相当简单, 只需要在需要序列化的类实现 Serializable 接口并在其中声明一个类似下面的标识即可自动实现默认的序列化过程
- public class Person extends PersonParent implements Serializable {
- private static final long serialVersionUID = 1L;
- // 静态域
- public static int static_field;
- //transient 域
- public transient int transient_field;
- // 一个普通的域
- public String desc;
- public Person(String desc) {
- this.desc = desc;
- }
- static class PersonSerializableProxy implements Serializable{
- private String desc;
- private PersonSerializableProxy(Person s) {
- this.desc = s.desc;
- }
- /**
- * 与 writeReplace 相同, ObjectInputStream 会通过反射调用 readResolve()这个方法,
- * 决定是否替换反序列化出来的对象
- * @return
- */
- private Object readResolve() {
- return new Person(desc);
- }
- }
- /**
- *
- * 在序列化一个对象时, ObjectOutputStream 会通过反射首先调用 writeReplace 这个方法,
- * 在这里我们可以替换真正送去序列的对象,
- * 如果我们没有重写, 那序列化的对象就是最开始的对象
- * @return
- */
- private Object writeReplace() {
- // 序列化 Person 的时候我们并没有直接写入 Person 对象, 而是写入了 PersonSerializableProxy 对象
- return new PersonSerializableProxy(this);
- }
- /**
- * 这里主要是为了防止攻击, 任何以 Person 声明的对象字节流都是流氓!!
- * 因为我在 writeReplace 中已经把序列化的实例指向了 SerializableProxy
- * @param stream
- * @throws InvalidObjectException
- */
- private void readObject(ObjectInputStream stream) throws InvalidObjectException {
- throw new InvalidObjectException("proxy requied!");
- }
- public static void main(String[] args) throws IOException, ClassNotFoundException {
- Person person = new Person("desc");
- person.transient_field = 100;
- person.static_field = 10086;
- ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("cache.txt"));
- outputStream.writeObject(person);
- outputStream.flush();
- outputStream.close();
- person.static_field = 10087;
- ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("cache.txt"));
- Person deserialObj = (Person) objectInputStream.readObject();
- System.out.println(deserialObj);
- }
- }
- class PersonParent{
- private String name;
- //PersonParent 类要么继承自 Serializable, 要么需要提供一个无参构造器
- public PersonParent() {
- }
- public PersonParent(String name) {
- this.name = name;
- }
- }
不过在使用中也需要注意以下几个问题:
serialVersionUID 用来标识当前序列化对象的类版本, 建议每一个实现 Serialization 的类都指定该域当然如果我们没有指定, JVM 会根据类的信息自动生成一个 UID
被 transient 描述的域和类的静态变量是不会被序列化的, 序列化是针对类实例
需要进行序列化的对象所有的域都必须实现 Serializable 接口, 不然会直接报错 NotSerializableException 当然, 有两个例外: 域为空 或者域被 transient 描述是不会报错的
如果一个实现了 Serializable 类的对象继承自另外一个类, 那么这个类要么需要继承自 Serializable, 要么需要提供一个无参构造器
反序列化产生的对象并不是通过构造器创建的, 那么很多依赖于构造器保证的约束条件在对象反序列化时都无法保证比如一个设计成单例的类如果能够被序列化就可以分分钟克隆出多个实例
Android 中的 Parcelable
相对于 Serializable 而言, Parcelable 的使用要复杂一些
- public class Book implements Parcelable {
- private String name;
- public Book(String name) {
- this.name = name;
- }
- protected Book(Parcel in) {
- name = in.readString();
- }
- // 反序列化功能由 CREATOR 完成, 在 CREATOR 的内部标明的如何创建序列对象和数组
- public static final Creator<Book> CREATOR = new Creator<Book>() {
- // 从 Parcel 中反序列化对象
- @Override
- public Book createFromParcel(Parcel in) {
- // 其内部调用 Parcel 的一系列 readXXX 方法实现反序列化过程
- return new Book(in);
- }
- // 创建序列化数组
- @Override
- public Book[] newArray(int size) {
- return new Book[size];
- }
- };
- @Override
- public int describeContents() {
- return 0;
- }
- // 序列化过程:
- // 重写 writeToParcel 方法, 我们要在这里逐一对需要序列化的属性用 Parcel 的一系列 writeXXX 方法写入
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(name);
- }
- }
从上述代码注释可以看出, 写一个实现 Parcelable 接口的类还是比较麻烦的, 和 Serailable 相比, 我们需要在 writeToParcel 中按序写入各个域到流中, 同样, 在 createFromParcel 中我们需要自己返回一个 Book 对象
Parcelable 在使用上也与 Serializable 稍有不同
- public class TestActivity extends AppCompatActivity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_test2);
- // 获取一个 Parcel 容器
- Parcel parcel = Parcel.obtain();
- // 需要序列化的对象
- Book book = new Book("c++");
- // 把对象写入 Parcel
- parcel.writeParcelable(book,0);
- //Parcel 读写共用一个位置计数, 这里一定要重置一下当前的位置
- parcel.setDataPosition(0);
- // 读取 Parcel
- Book book1 = parcel.readParcelable(Book.class.getClassLoader());
- Log.d("TestActivity",book1.toString());
- }
- }
Parcelable 的写
我们来看一下 writeParcelable 方法
- [Parcel.java]
- public final void writeParcelable(Parcelable p, int parcelableFlags) {
- // 判断 p 是否为空
- if (p == null) {
- writeString(null);
- return;
- }
- // 先写入 p 的类名
- writeParcelableCreator(p);
- // 调用我们重写的 writeToParcel 方法, 按顺序写入域
- p.writeToParcel(this, parcelableFlags);
- }
- public final void writeParcelableCreator(Parcelable p) {
- // 先写入 p 的类名
- String name = p.getClass().getName();
- writeString(name);
- }
Parcelable 的读
我们来看 readParcelable 方法
- [Parcel.java]
- public final <T extends Parcelable> T readParcelable(ClassLoader loader) {
- // 调用 readParcelableCreator
- // 这时获得就是我们自定义的 CREATOR
- Parcelable.Creator<?> creator = readParcelableCreator(loader);
- if (creator == null) {
- return null;
- }
- // 判断当前 creator 是不是 Parcelable.ClassLoaderCreator<?>的实例
- if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
- // 如果是的话,, 我们调用 reateFromParcel(this, loader);
- Parcelable.ClassLoaderCreator<?> classLoaderCreator =
- (Parcelable.ClassLoaderCreator<?>) creator;
- return (T) classLoaderCreator.createFromParcel(this, loader);
- }
- // 调用我们自定义的 CREATOR 中重写的 createFromParcel 方法
- return (T) creator.createFromParcel(this);
- }
- public final Parcelable.Creator<?> readParcelableCreator(ClassLoader loader) {
- // 首先把类名读取出来
- String name = readString();
- Parcelable.Creator<?> creator;
- //mCreators 做了一下缓存, 如果之前某个 classloader 把一个 parcelable 的 Creator 获取过
- // 那么就不需要通过反射去查找了
- synchronized (mCreators) {
- HashMap<String,Parcelable.Creator<?>> map = mCreators.get(loader);
- if (map == null) {
- map = new HashMap<>();
- mCreators.put(loader, map);
- }
- creator = map.get(name);
- if (creator == null) {
- try {
- ClassLoader parcelableClassLoader =
- (loader == null ? getClass().getClassLoader() : loader);
- // 加载我们自己实现 Parcelable 接口的类
- Class<?> parcelableClass = Class.forName(name, false,
- parcelableClassLoader);
- Field f = parcelableClass.getField("CREATOR");
- Class<?> creatorType = f.getType();
- creator = (Parcelable.Creator<?>) f.get(null);
- }
- catch (Exception e) {
- //catch exception
- }
- if (creator == null) {
- throw new BadParcelableException("Parcelable protocol requires a"
- + "non-null Parcelable.Creator object called"
- + "CREATOR on class" + name);
- }
- map.put(name, creator);
- }
- }
- return creator;
- }
我们的测试例子读取 Parcel
Book book1 = parcel.readParcelable(Book.class.getClassLoader());
可以看到我们在使用 readParcelable 的时候, 传入的参数是 Book 类的类加载器, 根据我们上面的代码, 我们知道我们先会通过反射获取定义在 Book 类中的 CREATOR 属性, 我们回想一下在 Book 类中是怎么定义 CREATOR 的
- public static final Creator<Book> CREATOR = new Creator<Book>() {
- // 从 Parcel 中反序列化对象
- @Override
- public Book createFromParcel(Parcel in) {
- // 其内部调用 Parcel 的一系列 readXXX 方法实现反序列化过程
- return new Book(in);
- }
- // 创建序列化数组
- @Override
- public Book[] newArray(int size) {
- return new Book[size];
- }
- };
我们得到 CREATOR 属性后, 调用它的 createFromParcel 方法, 由多态可知调用的实际我们定义在 CREATOR 内的 createFromParcel 方法, 在该方法内我们创建了 Book 对象 (内部实现是通过 Parcel 的一系列 readXXX 方法) 并返回至此我们就得到了反序列化的对象
本篇总结
我们本篇详细分析了 Android 序列化相关知识, 你可以使用 Java 中的 Serializable 也可以使用 Parcelable
来源: http://mp.weixin.qq.com/s/Qj-Bed2G6ekQ4DmogUffWw