zoukankan      html  css  js  c++  java
  • android对象序列化Parcelable浅析

    一、android序列化简介

    我们已经知道在Android使用Intent/Bindler进行IPC传输数据时,需要将对象进行序列化。

    JAVA原本已经提供了Serializable接口来实现序列化,使用起来非常简单,主要用于对象持久化以及对象的网络传输。Serializable开销比较大,因为序列化和反序列化的过程需要大量的I/O操作。

    Android提供了Parcelable对象序列化操作是内存序列化,主要用于Intent/Bindler的IPC数据传输。

    二、Parcelable序列化使用方法

    比如我们使用Parcelable在两个activity直接通过intent进行传输一个Book的对象。

     1 package org.xerrard.demo2;
     2 
     3 import android.os.Parcel;
     4 import android.os.Parcelable;
     5 
     6 /**
     7  * Created by xuqiang on 16-1-20.
     8  */
     9 public class Book implements Parcelable{
    10 
    11     public String bookname;
    12 
    13     public Book(String bookname){
    14         this.bookname = bookname;
    15     }
    16 
    17     protected Book(Parcel in) {
    18         bookname = in.readString();
    19     }
    20 
    21     public static final Creator<Book> CREATOR = new Creator<Book>() {
    22         @Override
    23         public Book createFromParcel(Parcel in) {
    24             return new Book(in);
    25         }
    26 
    27         @Override
    28         public Book[] newArray(int size) {
    29             return new Book[size];
    30         }
    31     };
    32 
    33     @Override
    34     public int describeContents() {
    35         return 0;
    36     }
    37 
    38     @Override
    39     public void writeToParcel(Parcel dest, int flags) {
    40         dest.writeInt(bookname);
    41     }
    42 }

    我们需要完成以下几部。

    1. 实现Parcelable接口 
    2. 添加实体属性 
    3. 覆写writeToParcel(Parcel dest, int flags)方法,指定写入Parcel类的数据。 
    4. 创建Parcelable.Creator静态对象,有两个方法createFromParcel(Parcel in)与newArray(int size),前者指定如何从Parcel中读取出数据对象,后者创建一个数组。 
    5. 覆写describeContents方法,默认返回0。 

    然后我们就可以使用Intent中的putExtra方法将Book对象写入Intent中,然后使用getExtra方法,就可以从Intent中读出Book对象。

    三、Parcelable底层序列化原理

    从上面的例子可以看到,Parcelable的序列化方式使用起来还是比较麻烦的。但是,这种方式效率上是比较好的,因为Parcelable的序列化过程是再底层native通过内存操作实现的。

    详细的JNI和C/C++底层的内存操作可以看这篇文章探索Android中的Parcel机制(上)

    摘抄里面最重要的一句结论

    整个读写全是在内存中进行,主要是通过malloc()、realloc()、memcpy()等内存操作进行,所以效率比JAVA序列化中使用外部存储器会高很多

    因此,在IPC过程中,android推荐使用Parcelable序列化方式

    四:Parcelable的调用关系

    我们知道如果要使用Parcelable,必须按照要求实现这五项操作

    1. 实现Parcelable接口 
    2. 添加实体属性 
    3. 覆写writeToParcel(Parcel dest, int flags)方法,指定写入Parcel类的数据。 
    4. 创建Parcelable.Creator静态对象,有两个方法createFromParcel(Parcel in)与newArray(int size),前者指定如何从Parcel中读取出数据对象,后者创建一个数组。 
    5. 覆写describeContents方法,默认返回0。 

     这里面又是怎样的调用关系呢?

    我们看到,writeToParcel是在startActivity的过程中由intent->Bundle->Parcel 一步一步的调用的,然后WriteToParcel会调用native方法,在底层做序列化操作

    而createFromParcel是在收到Intent之后,由Intent->Bundle->Parcel 一步一步的调用。

    由此可以看出,Parcel的填包解包都是离不开Bundle的。

    这里其实还是有一个疑问,这个Creator是怎么一回事呢?

    我们从源码中截取Creator这部分来看看。

     1     public final <T extends Parcelable> T readParcelable(ClassLoader loader) {
     2         Parcelable.Creator<T> creator = readParcelableCreator(loader);
     3         if (creator == null) {
     4             return null;
     5         }
     6         if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
     7             return ((Parcelable.ClassLoaderCreator<T>)creator).createFromParcel(this, loader);
     8         }
     9         return creator.createFromParcel(this);
    10     }
    11 
    12     /** @hide */
    13     public final <T extends Parcelable> T readCreator(Parcelable.Creator<T> creator,
    14             ClassLoader loader) {
    15         if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
    16             return ((Parcelable.ClassLoaderCreator<T>)creator).createFromParcel(this, loader);
    17         }
    18         return creator.createFromParcel(this);
    19     }
    20 
    21     /** @hide */
    22     public final <T extends Parcelable> Parcelable.Creator<T> readParcelableCreator(
    23             ClassLoader loader) {
    24         String name = readString(); //此处获得类名,还不太清楚如何获得的,如果想深入学习可以再研究
    25         if (name == null) {
    26             return null;
    27         }
    28         Parcelable.Creator<T> creator;
    29         synchronized (mCreators) {
    30             HashMap<String,Parcelable.Creator> map = mCreators.get(loader);
    31             if (map == null) {
    32                 map = new HashMap<String,Parcelable.Creator>();
    33                 mCreators.put(loader, map);
    34             }
    35             creator = map.get(name);
    36             if (creator == null) {
    37                 try {
    38                     Class c = loader == null ?
    39                         Class.forName(name) : Class.forName(name, true, loader);
    40                     Field f = c.getField("CREATOR");
    41                     creator = (Parcelable.Creator)f.get(null);
    42                 }
    43                 catch (IllegalAccessException e) {
    44                     Log.e(TAG, "Illegal access when unmarshalling: "
    45                                         + name, e);
    46                     throw new BadParcelableException(
    47                             "IllegalAccessException when unmarshalling: " + name);
    48                 }
    49                 catch (ClassNotFoundException e) {
    50                     Log.e(TAG, "Class not found when unmarshalling: "
    51                                         + name, e);
    52                     throw new BadParcelableException(
    53                             "ClassNotFoundException when unmarshalling: " + name);
    54                 }
    55                 catch (ClassCastException e) {
    56                     throw new BadParcelableException("Parcelable protocol requires a "
    57                                         + "Parcelable.Creator object called "
    58                                         + " CREATOR on class " + name);
    59                 }
    60                 catch (NoSuchFieldException e) {
    61                     throw new BadParcelableException("Parcelable protocol requires a "
    62                                         + "Parcelable.Creator object called "
    63                                         + " CREATOR on class " + name);
    64                 }
    65                 catch (NullPointerException e) {
    66                     throw new BadParcelableException("Parcelable protocol requires "
    67                             + "the CREATOR object to be static on class " + name);
    68                 }
    69                 if (creator == null) {
    70                     throw new BadParcelableException("Parcelable protocol requires a "
    71                                         + "Parcelable.Creator object called "
    72                                         + " CREATOR on class " + name);
    73                 }
    74 
    75                 map.put(name, creator);
    76             }
    77         }
    78 
    79         return creator;
    80     }

    重点看粗体部分的代码——真想大白:

    在接收端收到parcel之后,解析的时候,会通过反射去获取对象的Creator,然后保存到一个hashmap中。然后调用Creator的createFromParcel方法来实现解包。

    反射在源码中也是无处不在!

  • 相关阅读:
    连接数据库的URL等于多少?
    警告: [SetContextPropertiesRule]{Context} Setting property 'source' to 'org.eclipse.jst.jee.server:20160928' did not find a matching property
    jsp页面中的问题:Date cannot be resolved to a type
    无法找到类:java.lang.ClassNotFoundException: com.tt.javaweb.HttpServletRequest问题解决
    Tomcat无法启动:org.apache.catalina.LifecycleException: Failed to start component 问题解决
    No Suitable Driver Found For Jdbc_我的解决方法
    "aa".equals(name)这种写法为什么就可以避免空指针
    Eclipse迁移到Android studio步骤如下:
    Android 开源框架Universal-Image-Loader完全解析(一)--- 基本介绍及使用
    Android 内存管理 &Memory Leak & OOM 分析
  • 原文地址:https://www.cnblogs.com/xerrard/p/5144386.html
Copyright © 2011-2022 走看看