Android 对象序列化之 Parcelable 取代 Serializable ?

闪存
Android 存储优化系列专题
  • SharedPreferences 系列

Android 之不要滥用 SharedPreferences
Android 之不要滥用 SharedPreferences(2)— 数据丢失

  • ContentProvider 系列(待更)

《Android 存储选项之 ContentProvider 启动过程源码分析》
《Android 存储选项之 ContentProvider 深入分析》

  • 对象序列化系列

Android 对象序列化之你不知道的 Serializable
Android 对象序列化之 Parcelable 取代 Serializable ?
Android 对象序列化之追求性能完美的 Serial

  • 数据序列化系列(待更)

《Android 数据序列化之 JSON》
《Android 数据序列化之 Protocol Buffer 使用》
《Android 数据序列化之 Protocol Buffer 源码分析》

  • SQLite 存储系列

Android 存储选项之 SQLiteDatabase 创建过程源码分析
Android 存储选项之 SQLiteDatabase 源码分析
数据库连接池 SQLiteConnectionPool 源码分析
SQLiteDatabase 启用事务源码分析
SQLite 数据库 WAL 模式工作原理简介
SQLite 数据库锁机制与事务简介
SQLite 数据库优化那些事儿


前言

对象序列化系列,主要内容包括:Java 原生提供的 Serializable ,更加适合 Android 平台轻量且高效的 Parcelable,以及追求性能完美的 Serial。该系列内容主要结合源码的角度分析它们各自的优缺点以及合适的使用场景。

在上一篇文章《Android 对象序列化之你不知道的 Serializable》为大家介绍了 Serializable 的简单实现机制背后复杂的计算逻辑,由于整个实现过程使用了大量反射和临时变量,而且在序列化对象的时候,不仅需要序列化当前对象本身,还需要递归序列化引用的其他对象。

虽然 Serializable 性能那么差,但是它仍然有一些优势和进阶的使用技巧,正如文中所述:“Java 原生提供的 Serializalbe 对象序列化机制,远比大多数 Java 开发人员想象的更灵活,这使得我们有更多的机会解决棘手的问题”。

说道这里如果你还对 Serializable 序列化机制不熟悉的话可以先去参考下。

Parcelable 产生的背景

由于 Java 的 Serializable 的性能较低,Android 需要重新设计一套更加轻量且高效的对象序列化和反序列化机制。Parcelable 正式在这个背景下产生的,它核心作用就是为了解决 Android 中大量跨进程通信的性能问题

在时间开销和使用成本的权衡上,Serializable 选择了使用成本,而 Parcelable 机制则是选择性能优先。所以在它写入和读取数据都需要手动添加自定义代码,使用起来相比 Serializable 会复杂很多。但是正因为这样,Parcelable 才不需要采用反射的方式去实现序列化和反序列化(有少许反射,文中会分析到)。

Parcelable 实现机制分析

下面我们还是通过一个例子结合源码的角度开始分析:

public final class WebParams implements Parcelable {

    public static final String EXTRA_PARAMS_KEY = "extra_params_key";

    private int age;
    private String name;
    private long serialUid;
    private char[] flag = new char[10];
    private byte[] like = new byte[10];

    public WebParams() {
    }

    protected WebParams(Parcel in) {
        this.age = in.readInt();
        this.name = in.readString();
        this.serialUid = in.readLong();
        in.readCharArray(flag);
        in.readByteArray(like);
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(age);
        dest.writeString(name);
        dest.writeLong(serialUid);
        dest.writeCharArray(flag);
        dest.writeByteArray(like);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator<WebParams> CREATOR = new Creator<WebParams>() {
        @Override
        public WebParams createFromParcel(Parcel in) {
            return new WebParams(in);
        }

        @Override
        public WebParams[] newArray(int size) {
            return new WebParams[size];
        }
    };
}

通过启动 Activity 过程分析 Parcelable 序列化过程:

private void serialParcelable() {
    final Intent intent = new Intent(this, null);
    final WebParams params = new WebParams();
    //...省略参数配置
    //通过设置附加参数到Intent中
    intent.putExtra(WebParams.EXTRA_PARAMS_KEY, params);
    startActivity(intent);
}

熟悉这一过程的朋友过程肯定知道,startActivity 方法最终会通过 AMS(ActivityManagerService)完成跨进程通信调用,但是在通信之前先要将数据序列化后进行传输,这个过程首先会调用 ActivityManagerProxy 的 startActivity 方法

public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
        String resolvedType, IBinder resultTo, String resultWho, int requestCode,
        int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
    //负责写出
    Parcel data = Parcel.obtain();
    //负责读取
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IActivityManager.descriptor);
    data.writeStrongBinder(caller != null ? caller.asBinder() : null);
    data.writeString(callingPackage);
    //我们分析Parcelable序列化重点在这里
    intent.writeToParcel(data, 0);
    data.writeString(resolvedType);
    data.writeStrongBinder(resultTo);
    data.writeString(resultWho);
    data.writeInt(requestCode);
    data.writeInt(startFlags);
    if (profilerInfo != null) {
        data.writeInt(1);
        profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
    } else {
        data.writeInt(0);
    }
    if (options != null) {
        data.writeInt(1);
        options.writeToParcel(data, 0);
    } else {
        data.writeInt(0);
    }
    //开始跨进程通信
    mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
    reply.readException();
    int result = reply.readInt();
    reply.recycle();
    data.recycle();
    return result;
}

其中 intent.writeToParcel 是我们要重点跟踪的方法,先来看下它的参数 Parcel 类型,其实从这里的获取方式大家也能猜测的出内部使用了复用机制,就类似于 Message.obtain。

public static Parcel obtain() {
    //当前缓存池,sOwnedPool是一个静态变量
    final Parcel[] pool = sOwnedPool;
    synchronized (pool) {
        Parcel p;
        //获取可以被复用的Parcel, //POOL_SIZE默认大小为6
        for (int i = 0; i < POOL_SIZE; i++) {
            p = pool[i];
            if (p != null) {
                //获取到复用对象,将该位置置为null
                pool[i] = null;
                if (DEBUG_RECYCLE) {
                    p.mStack = new RuntimeException();
                }
                //这是一个默认辅助读写
                p.mReadWriteHelper = ReadWriteHelper.DEFAULT;
                return p;
            }
        }
    }
    //无可复用,直接创建
    return new Parcel(0);
}

从 for 循环获取可复用的 Parcel 过程,不知大家是否能够看得出这一个队列的数据结构。

这里看下 POOL_ZISE 的定义:

private static final int POOL_SIZE = 6;

sOwnedPool 的定义:

private static final Parcel[] sOwnedPool = new Parcel[POOL_SIZE];

如果从复用池获取不到则直接创建 Parcel:

private Parcel(long nativePtr) {
    if (DEBUG_RECYCLE) {
        mStack = new RuntimeException();
    }
    //初始化Parcel
    init(nativePtr);
}

//调用init
private void init(long nativePtr) {
    if (nativePtr != 0) {
        mNativePtr = nativePtr;
        mOwnsNativeParcelObject = false;
    } else {
        //此时传递为0,Parcel内存区域由我们自己创建
        //Native层Parcel地址指针
        mNativePtr = nativeCreate();
        mOwnsNativeParcelObject = true;
    }
}

实际上 Parcel 的核心实现都在 Parcel.cpp,Java 层 Parcel 只是对 native 层接口的调用封装,我们先看下 native 层 Parcel 的创建过程:

private static native long nativeCreate();

//jni注册
{"nativeCreate", "()J",(void*)android_os_Parcel_create},

//nativeCreate的具体实现
static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz)
{
   //创建native层Parcel对象
   Parcel* parcel = new Parcel();
   return reinterpret_cast<jlong>(parcel);
}

有复用就一定有回收的逻辑,看下 Parcel 的回收逻辑:

public final void recycle() {
    if (DEBUG_RECYCLE) mStack = null;
    //释放其native内存
    freeBuffer();

    final Parcel[] pool;
    //使用new Parcel() 默认为true
    //表示我们对它的生命周期负责
    if (mOwnsNativeParcelObject) {
        pool = sOwnedPool;
    } else {
        mNativePtr = 0;
        pool = sHolderPool;
    }

    synchronized (pool) {
        for (int i = 0; i < POOL_SIZE; i++) {
            //获取可以被缓存的位置
            if (pool[i] == null) {
                pool[i] = this;
                return;
            }
        }
    }
}

继续向下分析,执行 intent.writeToParcel 将 Parcel 作为参数,这里我们主要跟踪下 Parcelable 的写入过程,

由于采用 Intent 传递附加参数过程,最终都会保存到 Bundle 中,而 Bundle 用于实际存储数据的则是通过 Map 完成的:

 //添加附加参数
 public @NonNull Intent putExtra(String name, Parcelable value) {
    if (mExtras == null) {
        //创建Bundle实例
        mExtras = new Bundle();
    }
    //实际保存在Bundle中
    mExtras.putParcelable(name, value);
    return this;
}

接着看下 Bundle 的保存附加数据过程:

public void putParcelable(@Nullable String key, @Nullable Parcelable value) {
    unparcel();
    //该mMap是一个ArrayMap实例
    mMap.put(key, value);
    mFlags &= ~FLAG_HAS_FDS_KNOWN;
}

此时将携带的附加参数进行序列化,实际是调用 Bundle 的 writeToParcel 方法,这里实际操作在其父类 BaseBundle 的 writeToParcelInner 方法中:

void writeToParcelInner(Parcel parcel, int flags) {
    if (parcel.hasReadWriteHelper()) {
        //这里(mReadWriteHelper != null) && (mReadWriteHelper != ReadWriteHelper.DEFAULT);
        //mReadWriteHelper默认是DEFAULT
        unparcel();
    }
    final ArrayMap<String, Object> map;
    synchronized (this) {
        if (mParcelledData != null) {
            //当前Bundle是否有被解析过
            if (mParcelledData == NoImagePreloadHolder.EMPTY_PARCEL) {
                parcel.writeInt(0);
            } else {
                int length = mParcelledData.dataSize();
                parcel.writeInt(length);
                parcel.writeInt(mParcelledByNative ? BUNDLE_MAGIC_NATIVE : BUNDLE_MAGIC);
                parcel.appendFrom(mParcelledData, 0, length);
            }
            return;
        }
        map = mMap;
    }

    if (map == null || map.size() <= 0) {
        parcel.writeInt(0);
        return;
    }
    //parcel当前内存起始位置
    int lengthPos = parcel.dataPosition();
    //这里是附加参数的长度,临时占位
    parcel.writeInt(-1); // dummy, will hold length
    //魔数
    parcel.writeInt(BUNDLE_MAGIC);
    //附加数据起始位置
    int startPos = parcel.dataPosition();
    //写入当前所有的附加参数
    parcel.writeArrayMapInternal(map);
    //附加数据结束位置
    int endPos = parcel.dataPosition();
    //重新指到lengthPos
    parcel.setDataPosition(lengthPos);
    //此时可以计算出附加数据的真实长度
    int length = endPos - startPos;
    parcel.writeInt(length);
    //重新指到当前结束位置endpos
    parcel.setDataPosition(endPos);
}

代码中已经标注了详细的注释,这里我们重点看下 Parcelable 的序列化机制parcel.writeArrayMapInternal 方法:

void writeArrayMapInternal(ArrayMap<String, Object> val) {
    if (val == null) {
        writeInt(-1);
        return;
    }
    //附件参数的长度
    final int N = val.size();
    //写入时Map的长度
    writeInt(N);
    int startPos;
    for (int i = 0; i < N; i++) {
        //写入key
        writeString(val.keyAt(i));
        //写入value,每个value类型都会额外浪费4字节(Int)
        writeValue(val.valueAt(i));
    }
}

写入当前附加参数的总长度,遍历 Map 容器,由于 key 是固定类型 String,这里我们重点关注下 writeValue 方法:

public final void writeValue(Object v) {
    if (v == null) {
        writeInt(VAL_NULL);
    } else if (v instanceof String) {
        //String类型
        writeInt(VAL_STRING);
        writeString((String) v);
    } else if (v instanceof Integer) {
        //Integer类型
        writeInt(VAL_INTEGER);
        writeInt((Integer) v);
    } else if (v instanceof Map) {
        //Map类型
        writeInt(VAL_MAP);
        writeMap((Map) v);
    } else if (v instanceof Bundle) {
        // Must be before Parcelable
        writeInt(VAL_BUNDLE);
        writeBundle((Bundle) v);
    } else if (v instanceof PersistableBundle) {
        writeInt(VAL_PERSISTABLEBUNDLE);
        writePersistableBundle((PersistableBundle) v);
    } else if (v instanceof Parcelable) {
        //Parcelable类型
        writeInt(VAL_PARCELABLE);
        writeParcelable((Parcelable) v, 0);
    } else if (v instanceof Short) {
        writeInt(VAL_SHORT);
        writeInt(((Short) v).intValue());
    } else if (v instanceof Long) {
        writeInt(VAL_LONG);
        writeLong((Long) v);
    }
    
    //... 省略
}

大家是否有注意到 Value 的写入过程,系统自己定义了一套类型映射关系,根据Value 数据类型先写四个字节的类型信息 writeInt(VAL_NULL)。看下这个类型映射关系:

private static final int VAL_NULL = -1;
private static final int VAL_STRING = 0;
private static final int VAL_INTEGER = 1;
private static final int VAL_MAP = 2;
private static final int VAL_BUNDLE = 3;
private static final int VAL_PARCELABLE = 4;
private static final int VAL_SHORT = 5;
private static final int VAL_LONG = 6;
private static final int VAL_FLOAT = 7;
private static final int VAL_DOUBLE = 8;
private static final int VAL_BOOLEAN = 9;
private static final int VAL_CHARSEQUENCE = 10;

//... 省略

这里不知大家是否有意识到,每个 Value 写入都会额外附加 4 个字节的类型信息。用于表示当前 Value 的数据类型,这在后续反序列化时要根据该数据类型进行创建实例。

看下 Parcelable 的序列化过程 writeParcelable 方法:

public final void writeParcelable(Parcelable p, int parcelableFlags) {
    if (p == null) {
        writeString(null);
        return;
    }
    //写入Parcelable的全限定名,反序列化时,需要根据该全限定名查找一个类:Classloader.loadClass
    writeParcelableCreator(p);
    //这里是否大家熟悉呢?其实回到了我们自定义的Parcelable中
    p.writeToParcel(this, parcelableFlags);
}

public final void writeParcelableCreator(Parcelable p) {
    //写入Parceable的全限定名
    String name = p.getClass().getName();
    writeString(name);
}

其中 p.writeToParcel 是否感到熟悉呢?其实这里就是回到了我们自定义 WebParams 的 writeToParcel 方法中:

@Override
public void writeToParcel(Parcel dest, int flags){
    dest.writeInt(age);
    dest.writeString(name);
    dest.writeLong(serialUid);
    dest.writeCharArray(flag);
    dest.writeByteArray(like);
}

此时就是我们按照按照实际要序列化内容写入到 Parcel 内存了。

分析到这里,实际上整个 Parcelable 数据序列化过程就已经一幕了然了,Parcelable 只是一个序列化规则,它向开发人员暴露 Parcel 操作对象,自行写入要序列化的数据。它的核心实现都在 native 层 Parcel.cpp,Java 层 Parcel 是对其接口的封装。

下面来分析下 Parcelable 的反序列化过程:

final Bundle extra = getIntent().getExtras();
final WebParams params = extra.getParcelable(WebParams.EXTRA_PARAMS_KEY);

还是通过上面示例进行分析

@Nullable
public <T extends Parcelable> T getParcelable(@Nullable String key) {
    //解析Parcel数据
    unparcel();
    //解析数据会封装在该map中
    Object o = mMap.get(key);
    if (o == null) {
        return null;
    }
    try {
        return (T) o;
    } catch (ClassCastException e) {
        typeWarning(key, o, "Parcelable", e);
        return null;
    }
}

这里我们重点跟踪下 unparcel 方法:

void unparcel() {
    synchronized (this) {
        final Parcel source = mParcelledData;
        if (source != null) {
            //这里开始从Parcel读取序列化的数据
            initializeFromParcelLocked(source, /*recycleParcel=*/ true, mParcelledByNative);
        } else {
            //...忽略
        }
    }
}

//在unparcel方法调用该方法
private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel,
        boolean parcelledByNative) {

    //如果Parcel数据为空
    if (isEmptyParcel(parcelledData)) {
        if (mMap == null) {
            mMap = new ArrayMap<>(1);
        } else {
            //将Map中每个位置元素置为null,
            mMap.erase();
        }
        mParcelledData = null;
        mParcelledByNative = false;
        return;
    }
    //获取附加参数的长度,这里对应写入时Map的size
    final int count = parcelledData.readInt();
    if (count < 0) {
        return;
    }

    ArrayMap<String, Object> map = mMap;
    if (map == null) {
        //按照size创建ArrayMap
        map = new ArrayMap<>(count);
    } else {
        map.erase();
        //调整为新的长度
        map.ensureCapacity(count);
    }
    try {
        if (parcelledByNative) {
            parcelledData.readArrayMapSafelyInternal(map, count, mClassLoader);
        } else {
            parcelledData.readArrayMapInternal(map, count, mClassLoader);
        }
    } catch (BadParcelableException e) {
        if (sShouldDefuse) {
            map.erase();
        } else {
            throw e;
        }
    } finally {
        mMap = map;
        if (recycleParcel) {
            recycleParcel(parcelledData);
        }
        //解析过后一定置为null
        //避免后续get相关内容时再次发生解析
        mParcelledData = null;
        mParcelledByNative = false;
    }
}

前面是一些列的数据校验和缓存设置,这里我们重点分析下 parcelledData.readArrayMapInternal 方法:

void readArrayMapInternal(ArrayMap outVal, int N,
                                        ClassLoader loader) {
    if (DEBUG_ARRAY_MAP) {
        RuntimeException here = new RuntimeException("here");
        here.fillInStackTrace();
        Log.d(TAG, "Reading " + N + " ArrayMap entries", here);
    }
    int startPos;
    //根据写入时Map长度
    while (N > 0) {
        if (DEBUG_ARRAY_MAP) startPos = dataPosition();
        //读取key
        String key = readString();
        //读取Value
        Object value = readValue(loader);
        //追加到ArrayMap中,可以直接理解成put(key, valu)
        //append系统留给自己使用的
        outVal.append(key, value);
        N--;
    }
    outVal.validate();
    //此时一系列读取完毕之后,全部都保存在Bundle的Map中,
    //后续我们通过Bundle的get操作直接从该Map中获取
}

还记得在前面分析写入 Parcel 数据时,都是通过键值对的形式,key 是固定的 String 类型,所以读取时也是先通过 readString 读取 key,紧接着 readValue 方法读取对应的 value:

 public final Object readValue(ClassLoader loader) {
    //先读取类型
    int type = readInt();

    //根据value类型进行匹配
    switch (type) {
        case VAL_NULL:
            //null类型
            return null;

        case VAL_STRING:
            //String类型
            return readString();

        case VAL_INTEGER:
            //int类型
            return readInt();

        case VAL_MAP:
            //Map类型
            return readHashMap(loader);

        case VAL_PARCELABLE:
            //Parcelable类型读取
            return readParcelable(loader);

        case VAL_SHORT:
            return (short) readInt();
            
            // ... 省略
    }
}

读取 value 相比 key 要麻烦一些,前面分析序列化过程写入 value 数据时,先写入该 value 数据对应的 int 类型,该类型在反序列化时会用到,此时系统就是根据该 int 值对应的 value 类型反序列化对应数据。我们以 readParcelable 类型为例:

public final <T extends Parcelable> T readParcelable(ClassLoader loader) {
    //获取对应Parcelable的Creator
    Parcelable.Creator<?> creator = readParcelableCreator(loader);
    if (creator == null) {
        return null;
    }
    if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
        //Creator也可以是ClassLoaderCreator
        //ClassLoaderCreator是Creator的子类
        Parcelable.ClassLoaderCreator<?> classLoaderCreator =
                (Parcelable.ClassLoaderCreator<?>) creator;
        return (T) classLoaderCreator.createFromParcel(this, loader);
    }
    //直接通过creatorFromParcel创建对应Parcelable
    //此时已经回到了自定义Parcelable中CREATOR内部类的createFromParcel方法
    return (T) creator.createFromParcel(this);
}

先来看 Parcelable 的 CREATOR 的获取方式:

public final Parcelable.Creator<?> readParcelableCreator(ClassLoader loader) {
    String name = readString();
    if (name == null) {
        return null;
    }
    Parcelable.Creator<?> creator;
    synchronized (mCreators) {
        //系统根据ClassLoader缓存Parcelable的Creator
        //获取当前类加载器缓存过的Parcelable的Creator实例
        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);
                //反射获取该类对象
                Class<?> parcelableClass = Class.forName(name, false /* initialize */,
                        parcelableClassLoader);
                if (!Parcelable.class.isAssignableFrom(parcelableClass)) {
                    //必须是Parcelable类型
                    throw new BadParcelableException("Parcelable protocol requires subclassing "
                            + "from Parcelable on class " + name);
                }
                //反射获取Parcelable中CREATOR Field
                Field f = parcelableClass.getField("CREATOR");
                if ((f.getModifiers() & Modifier.STATIC) == 0) {
                    //必须是static的
                    throw new BadParcelableException("Parcelable protocol requires "
                            + "the CREATOR object to be static on class " + name);
                }
                Class<?> creatorType = f.getType();
                if (!Parcelable.Creator.class.isAssignableFrom(creatorType)) {
                    //必须是Parcelable.Creator类型
                    throw new BadParcelableException("Parcelable protocol requires a "
                            + "Parcelable.Creator object called "
                            + "CREATOR on class " + name);
                }
                //获取到该Parcelable对应的Creator实例。
                creator = (Parcelable.Creator<?>) f.get(null);
            } catch (IllegalAccessException e) {
                throw new BadParcelableException(
                        "IllegalAccessException when unmarshalling: " + name);
            } catch (ClassNotFoundException e) {
                throw new BadParcelableException(
                        "ClassNotFoundException when unmarshalling: " + name);
            } catch (NoSuchFieldException e) {
                throw new BadParcelableException("Parcelable protocol requires a "
                        + "Parcelable.Creator object called "
                        + "CREATOR on class " + name);
            }
            if (creator == null) {
                throw new BadParcelableException("Parcelable protocol requires a "
                        + "non-null Parcelable.Creator object called "
                        + "CREATOR on class " + name);
            }
            //注意,系统缓存每个使用到的Parcelable的Creator实例
            //这样下次创建对应的Parcelable时,直接通过Creator实例createFromParcel创建,
            //避免了再次反射
            map.put(name, creator);
        }
    }
    return creator;
}

系统首先根据 Classloader(不同的 Classloader 加载的 Class 对象不相等) 获取保存 CREATOR 的 Map 容器,然后根据 value 类型的全限定名在该 Map 中查找是否已经存在对应的 CREATOR 实例,否则通过 Classloader 加载该类,并反射获取该类的 CREATOR 字段;

从这里我们可以看出:Parcelable 中为什么要包含一个 CREATOR 的字段,并且一定要声明为 static,而且系统会缓存每个已经使用过的 Parcelable 的 CREATOR 实例,便于下次反序列化时直接通过 new 创建 该 Parcelable 实例。

回到 readParcelable 方法:直接调用 CREATOR 的 createFromParcel 方法,此时就回到了我们自定义的 WebParams 中:

@Override
    public WebParams createFromParcel(Parcel in) {
        return new WebParams(in);
    }

然后在 WebParams 的构造方法中根据写入时顺序将其赋值给 WebParams 成员:

protected WebParams(Parcel in) {
    this.age = in.readInt();
    this.name = in.readString();
    this.serialUid = in.readLong();
    in.readCharArray(flag);
    in.readByteArray(like);
}

至于为什么一定要按照顺序读取写入时,通过分析源码,相信大家也一定能够理解,数据是按照顺序进行排列的。至此关于 Parcelable 的序列化以及反序列化的 Java 层部分就分析完了。

通过分析我们发现 Android 中 Parcelable 序列化和 Java 原生提供的 Serializalbe 序列化机制差别还是比较大的,Parcelable 只会在内存中序列化操作,并不会将数据存储到磁盘里。一般来说,如果需要持久化存储的话,一般还是不得不选择性能更差的 Serializable 方案,那有没有其它选择呢?你可以参考下篇文章《Android 对象序列化之追求性能完美的 Serial》。

总结

Parcelable 的原理还是比较简单的,它的核心实现都在 Parcel.cpp。

正如前面给大家讲到:Parcelable 时间开销和使用成本的权衡上,Parcelable 机制选择的是性能优先。所以它在写入和读取的时候,都需要手动添加自定义代码,使用起来相比 Serializable 会复杂很多。但是正因为这样,Parcelable 才不需要采用反射的方式去实现序列化和反序列化(当然,实际 Parcelable 也用到了少许反射,这里是相对 Serialzable 而言微不足道)

Parcelable 的持久化存储

虽然 Parcelable 默认不支持持久化存储,但是我们也可以通过一些取巧的方式实现,在 Parcel.java 中 marshall 接口获取 byte 数组,然后存储在文件中从而实现 Parcelable 的永久存储。

public final byte[] marshall() {
    return nativeMarshall(mNativePtr);
}

但是它也存在一些问题:

  1. 系统版本的兼容性。由于 Parcelable 设计本意是在内存中使用,我们无法保证所有 Android 版本的 Parcel.cpp 实现都完全一致。如果不同系统版本实现有所差异i,或者厂商修改了实现,可能会存在问题。
  2. 数据前后兼容性。Parcelable 并没有版本管理的设计,如果我们类的版本出现升级,写入的顺序及字段类型的兼容都需要格外注意,这也带来了很大的维护成本。

一般来说,如果需要持久化存储的话,一般还是不得不选择性能更差的 Serializable 方案。


以上便是个人在学习 Serial 时的心得和体会,文中分析如有不妥或更好的分析结果,还请大家指出!

文章如果对你有帮助,就请留个赞吧!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342