大家都知道,要想在Intent里面传递一些非基本类型的数据,有两种方式,一种实现Parcelable,另一种是实现Serializable接口。

今天先不说Serializable 接口,只说Parcelable。

我们知道,Parcelable 只是一个接口,里面有几个关键方法:

writeToParcel

   /*** Flatten this object in to a Parcel.* * @param dest The Parcel in which the object should be written.* @param flags Additional flags about how the object should be written.* May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.*/public void writeToParcel(Parcel dest, @WriteFlags int flags);

这个方法会让你把当前你需要保存的数据,写进Parcel 里。flags 可以写0 ,也可以写PARCELABLE_WRITE_RETURN_VALUE。这两个什么区别呢?后面再说。

这个里面,你需要调用传给你的Parcel 对象dest,把你需要的数据传递进去。Such as:

 *     public void writeToParcel(Parcel out, int flags) {*         out.writeInt(mData);*     }

同时需要实现一个Creator, 用来恢复对象,如果没有实现这个Creator,那么恢复的时候,会报错。

    /*** Interface that must be implemented and provided as a public CREATOR* field that generates instances of your Parcelable class from a Parcel.*/public interface Creator<T> {/*** Create a new instance of the Parcelable class, instantiating it* from the given Parcel whose data had previously been written by* {@link Parcelable#writeToParcel Parcelable.writeToParcel()}.* * @param source The Parcel to read the object's data from.* @return Returns a new instance of the Parcelable class.*/public T createFromParcel(Parcel source);/*** Create a new array of the Parcelable class.* * @param size Size of the array.* @return Returns an array of the Parcelable class, with every entry* initialized to null.*/public T[] newArray(int size);}

createFromParcel(Parcel source);

这个方法是,当你恢复对象的时候,会把source 传递给你,让你去读取。

官方给的例子:

 **     public static final Parcelable.Creator MyParcelable&gt; CREATOR*             = new Parcelable.Creator&lt;MyParcelable&gt;() {*         public MyParcelable createFromParcel(Parcel in) {*             return new MyParcelable(in);*         }**         public MyParcelable[] newArray(int size) {*             return new MyParcelable[size];*         }*     };*     *     private MyParcelable(Parcel in) {*         mData = in.readInt();*     }

那么为什么这几个方法就可以把一个对象放到intent 里面呢?然后还可以取出来?

我们看下源码:

  /*** Add extended data to the intent.  The name must include a package* prefix, for example the app com.android.contacts would use names* like "com.android.contacts.ShowAll".** @param name The name of the extra data, with package prefix.* @param value The Parcelable data value.** @return Returns the same Intent object, for chaining multiple calls* into a single statement.** @see #putExtras* @see #removeExtra* @see #getParcelableExtra(String)*/public @NonNull Intent putExtra(String name, Parcelable value) {if (mExtras == null) {mExtras = new Bundle();}mExtras.putParcelable(name, value);return this;}

我们可以看到,其实是放到了mExtras 里面。

    private Bundle mExtras;

他其实是个Bundle.
Bundle 其实也是实现了Parcelable 接口

public final class Bundle extends BaseBundle implements Cloneable, Parcelable {

我们看下Bundle putParcelable 的实现:

    /*** Inserts a Parcelable value into the mapping of this Bundle, replacing* any existing value for the given key.  Either key or value may be null.** @param key a String, or null* @param value a Parcelable object, or null*/public void putParcelable(@Nullable String key, @Nullable Parcelable value) {unparcel();mMap.put(key, value);mFlags &= ~FLAG_HAS_FDS_KNOWN;}

进入unparcel();

    /*** If the underlying data are stored as a Parcel, unparcel them* using the currently assigned class loader.*//* package */ void unparcel() {synchronized (this) {final Parcel source = mParcelledData;if (source != null) {initializeFromParcelLocked(source, /*recycleParcel=*/ true);} else {if (DEBUG) {Log.d(TAG, "unparcel "+ Integer.toHexString(System.identityHashCode(this))+ ": no parcelled data");}}}}

正常的情况下,mParcelledData是null 的。我们可以看到,其实这里面只是简单的put 进去。

ok ,传递数据的时候,Bundle 是要传递过去的,肯定会调用writeToParcel。

  /*** Writes the Bundle contents to a Parcel, typically in order for* it to be passed through an IBinder connection.* @param parcel The parcel to copy this bundle to.*/@Overridepublic void writeToParcel(Parcel parcel, int flags) {final boolean oldAllowFds = parcel.pushAllowFds((mFlags & FLAG_ALLOW_FDS) != 0);try {super.writeToParcelInner(parcel, flags);} finally {parcel.restoreAllowFds(oldAllowFds);}}

调用了 super.writeToParcelInner(parcel, flags);
我们看下BaseBundle 的 writeToParcelInner(parcel, flags);:

/*** Writes the Bundle contents to a Parcel, typically in order for* it to be passed through an IBinder connection.* @param parcel The parcel to copy this bundle to.*/void writeToParcelInner(Parcel parcel, int flags) {// If the parcel has a read-write helper, we can't just copy the blob, so unparcel it first.if (parcel.hasReadWriteHelper()) {unparcel();}// Keep implementation in sync with writeToParcel() in// frameworks/native/libs/binder/PersistableBundle.cpp.final ArrayMap<String, Object> map;synchronized (this) {// unparcel() can race with this method and cause the parcel to recycle// at the wrong time. So synchronize access the mParcelledData's content.if (mParcelledData != null) {if (mParcelledData == NoImagePreloadHolder.EMPTY_PARCEL) {parcel.writeInt(0);} else {int length = mParcelledData.dataSize();parcel.writeInt(length);parcel.writeInt(BUNDLE_MAGIC);parcel.appendFrom(mParcelledData, 0, length);}return;}map = mMap;}// Special case for empty bundles.if (map == null || map.size() <= 0) {parcel.writeInt(0);return;}int lengthPos = parcel.dataPosition();parcel.writeInt(-1); // dummy, will hold lengthparcel.writeInt(BUNDLE_MAGIC);int startPos = parcel.dataPosition();parcel.writeArrayMapInternal(map);int endPos = parcel.dataPosition();// Backpatch lengthparcel.setDataPosition(lengthPos);int length = endPos - startPos;parcel.writeInt(length);parcel.setDataPosition(endPos);}

里面写了一堆,关键是 parcel.writeArrayMapInternal(map); 这句把map 写到了parcel 里面。

我们看下Parcel 的writeArrayMapInternal方法:

    /*** Flatten an ArrayMap into the parcel at the current dataPosition(),* growing dataCapacity() if needed.  The Map keys must be String objects.*//* package */ void writeArrayMapInternal(ArrayMap<String, Object> val) {if (val == null) {writeInt(-1);return;}// Keep the format of this Parcel in sync with writeToParcelInner() in// frameworks/native/libs/binder/PersistableBundle.cpp.final int N = val.size();writeInt(N);if (DEBUG_ARRAY_MAP) {RuntimeException here =  new RuntimeException("here");here.fillInStackTrace();Log.d(TAG, "Writing " + N + " ArrayMap entries", here);}int startPos;for (int i=0; i<N; i++) {if (DEBUG_ARRAY_MAP) startPos = dataPosition();writeString(val.keyAt(i));writeValue(val.valueAt(i));if (DEBUG_ARRAY_MAP) Log.d(TAG, "  Write #" + i + " "+ (dataPosition()-startPos) + " bytes: key=0x"+ Integer.toHexString(val.keyAt(i) != null ? val.keyAt(i).hashCode() : 0)+ " " + val.keyAt(i));}}

首先写了长度,然后写k,写 value。我们看下这里的writeValue方法

    public final void writeValue(Object v) {if (v == null) {writeInt(VAL_NULL);} else if (v instanceof String) {writeInt(VAL_STRING);writeString((String) v);} else if (v instanceof Integer) {writeInt(VAL_INTEGER);writeInt((Integer) v);} else if (v instanceof Map) {writeInt(VAL_MAP);writeMap((Map) v);} else if (v instanceof Bundle) {// Must be before ParcelablewriteInt(VAL_BUNDLE);writeBundle((Bundle) v);} else if (v instanceof PersistableBundle) {writeInt(VAL_PERSISTABLEBUNDLE);writePersistableBundle((PersistableBundle) v);} else if (v instanceof Parcelable) {// IMPOTANT: cases for classes that implement Parcelable must// come before the Parcelable case, so that their specific VAL_*// types will be written.writeInt(VAL_PARCELABLE);writeParcelable((Parcelable) v, 0);} else if (v instanceof Short) {writeInt(VAL_SHORT);writeInt(((Short) v).intValue());}....}

如果发现写的是Parcelable 的话,就writeParcelable

    public final void writeParcelable(Parcelable p, int parcelableFlags) {if (p == null) {writeString(null);return;}writeParcelableCreator(p);p.writeToParcel(this, parcelableFlags);}public final void writeParcelableCreator(Parcelable p) {String name = p.getClass().getName();writeString(name);}

这里首先会写一下Parcelable 对象的类名字,然后调用了Parcelable 对象的writeToParcel。也就是自己实现的方法,就会把我们想要传递的数据写到Parcel 里面去。

OK ,这样,Parcelable 接口的writeToParcel 方法就被调用了。

我们再看下Parcel 的readFromParcel

    /*** Reads the Parcel contents into this Bundle, typically in order for* it to be passed through an IBinder connection.* @param parcel The parcel to overwrite this bundle from.*/public void readFromParcel(Parcel parcel) {super.readFromParcelInner(parcel);mFlags = FLAG_ALLOW_FDS;maybePrefillHasFds();}
super.readFromParcelInner(parcel);
    private void readFromParcelInner(Parcel parcel, int length) {if (length < 0) {throw new RuntimeException("Bad length in parcel: " + length);} else if (length == 0) {// Empty Bundle or end of data.mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL;return;}final int magic = parcel.readInt();if (magic != BUNDLE_MAGIC) {throw new IllegalStateException("Bad magic number for Bundle: 0x"+ Integer.toHexString(magic));}if (parcel.hasReadWriteHelper()) {// If the parcel has a read-write helper, then we can't lazily-unparcel it, so just// unparcel right away.synchronized (this) {initializeFromParcelLocked(parcel, /*recycleParcel=*/ false);}return;}// Advance within this Parcelint offset = parcel.dataPosition();parcel.setDataPosition(MathUtils.addOrThrow(offset, length));Parcel p = Parcel.obtain();p.setDataPosition(0);p.appendFrom(parcel, offset, length);p.adoptClassCookies(parcel);if (DEBUG) Log.d(TAG, "Retrieving "  + Integer.toHexString(System.identityHashCode(this))+ ": " + length + " bundle bytes starting at " + offset);p.setDataPosition(0);mParcelledData = p;}

很简单,把当前的mParcelledData 赋了值。

我们调用getParcelable 的时候,会首先 unparcel();

    public <T extends Parcelable> T getParcelable(@Nullable String key) {unparcel();Object o = mMap.get(key);if (o == null) {return null;}try {return (T) o;} catch (ClassCastException e) {typeWarning(key, o, "Parcelable", e);return null;}}
    /* package */ void unparcel() {synchronized (this) {final Parcel source = mParcelledData;if (source != null) {initializeFromParcelLocked(source, /*recycleParcel=*/ true);} else {if (DEBUG) {Log.d(TAG, "unparcel "+ Integer.toHexString(System.identityHashCode(this))+ ": no parcelled data");}}}}
private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel) {if (LOG_DEFUSABLE && sShouldDefuse && (mFlags & FLAG_DEFUSABLE) == 0) {Slog.wtf(TAG, "Attempting to unparcel a Bundle while in transit; this may "+ "clobber all data inside!", new Throwable());}if (isEmptyParcel(parcelledData)) {if (DEBUG) {Log.d(TAG, "unparcel "+ Integer.toHexString(System.identityHashCode(this)) + ": empty");}if (mMap == null) {mMap = new ArrayMap<>(1);} else {mMap.erase();}mParcelledData = null;return;}final int count = parcelledData.readInt();if (DEBUG) {Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))+ ": reading " + count + " maps");}if (count < 0) {return;}ArrayMap<String, Object> map = mMap;if (map == null) {map = new ArrayMap<>(count);} else {map.erase();map.ensureCapacity(count);}try {parcelledData.readArrayMapInternal(map, count, mClassLoader);} catch (BadParcelableException e) {if (sShouldDefuse) {Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);map.erase();} else {throw e;}} finally {mMap = map;if (recycleParcel) {recycleParcel(parcelledData);}mParcelledData = null;}if (DEBUG) {Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))+ " final map: " + mMap);}}

parcelledData.readArrayMapInternal(map, count, mClassLoader);
最终调用了Parcel 类的readArrayMapInternal

    /* package */ 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;while (N > 0) {if (DEBUG_ARRAY_MAP) startPos = dataPosition();String key = readString();Object value = readValue(loader);if (DEBUG_ARRAY_MAP) Log.d(TAG, "  Read #" + (N-1) + " "+ (dataPosition()-startPos) + " bytes: key=0x"+ Integer.toHexString((key != null ? key.hashCode() : 0)) + " " + key);outVal.append(key, value);N--;}outVal.validate();}

调用了readValue

public final Object readValue(ClassLoader loader) {int type = readInt();switch (type) {case VAL_NULL:return null;case VAL_STRING:return readString();case VAL_INTEGER:return readInt();case VAL_MAP:return readHashMap(loader);case VAL_PARCELABLE:return readParcelable(loader);.......}

readParcelable(loader);

    public final <T extends Parcelable> T readParcelable(ClassLoader loader) {Parcelable.Creator<?> creator = readParcelableCreator(loader);if (creator == null) {return null;}if (creator instanceof Parcelable.ClassLoaderCreator<?>) {Parcelable.ClassLoaderCreator<?> classLoaderCreator =(Parcelable.ClassLoaderCreator<?>) creator;return (T) classLoaderCreator.createFromParcel(this, loader);}return (T) creator.createFromParcel(this);}
 public final Parcelable.Creator<?> readParcelableCreator(ClassLoader loader) {String name = readString();if (name == null) {return null;}Parcelable.Creator<?> 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 {// If loader == null, explicitly emulate Class.forName(String) "caller// classloader" behavior.ClassLoader parcelableClassLoader =(loader == null ? getClass().getClassLoader() : loader);// Avoid initializing the Parcelable class until we know it implements// Parcelable and has the necessary CREATOR field. http://b/1171613.Class<?> parcelableClass = Class.forName(name, false /* initialize */,parcelableClassLoader);if (!Parcelable.class.isAssignableFrom(parcelableClass)) {throw new BadParcelableException("Parcelable protocol requires that the "+ "class implements Parcelable");}Field f = parcelableClass.getField("CREATOR");if ((f.getModifiers() & Modifier.STATIC) == 0) {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)) {// Fail before calling Field.get(), not after, to avoid initializing// parcelableClass unnecessarily.throw new BadParcelableException("Parcelable protocol requires a "+ "Parcelable.Creator object called "+ "CREATOR on class " + name);}creator = (Parcelable.Creator<?>) f.get(null);}catch (IllegalAccessException e) {Log.e(TAG, "Illegal access when unmarshalling: " + name, e);throw new BadParcelableException("IllegalAccessException when unmarshalling: " + name);}catch (ClassNotFoundException e) {Log.e(TAG, "Class not found when unmarshalling: " + name, 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);}map.put(name, creator);}}return creator;}

里面会加载你的Parcel 类,如果发现没有creator 就会抛异常。等等,最终调用了你的类的createFromParcel。

ok ,整个流程到这里就结束了。Parcleable 接口的调用大家都明白了吧。

Android Parcelable 源码解析相关推荐

  1. 【Android】Android Parcelable 源码解析

    Android Parcelable 源码解析 大家都知道,要想在Intent里面传递一些非基本类型的数据,有两种方式,一种实现Parcelable,另一种是实现Serializable接口.今天先不 ...

  2. Android Lifecycle源码解析(一)

    Android Lifecycle源码解析(一) 首先我们看HomeActivity中我们添加到一行代码 public class HomeActivity extends AppCompatActi ...

  3. 【Android】Android Broadcast源码解析

    Android Broadcast源码解析 一.静态广播的注册 静态广播是通过PackageManagerService在启动的时候扫描已安装的应用去注册的. 在PackageManagerServi ...

  4. Android xUtils3源码解析之图片模块

    本文已授权微信公众号<非著名程序员>原创首发,转载请务必注明出处. xUtils3源码解析系列 一. Android xUtils3源码解析之网络模块 二. Android xUtils3 ...

  5. Android xUtils3源码解析之注解模块

    本文已授权微信公众号<非著名程序员>原创首发,转载请务必注明出处. xUtils3源码解析系列 一. Android xUtils3源码解析之网络模块 二. Android xUtils3 ...

  6. Android xUtils3源码解析之数据库模块

    本文已授权微信公众号<非著名程序员>原创首发,转载请务必注明出处. xUtils3源码解析系列 一. Android xUtils3源码解析之网络模块 二. Android xUtils3 ...

  7. [Android] Handler源码解析 (Java层)

    之前写过一篇文章,概述了Android应用程序消息处理机制.本文在此文基础上,在源码级别上展开进行概述 简单用例 Handler的使用方法如下所示: Handler myHandler = new H ...

  8. android sdk 源码解析

    AndroidSdkSourceAnalysis:https://github.com/LittleFriendsGroup/AndroidSdkSourceAnalysis 第一期 Class 分析 ...

  9. Android LayoutInflater源码解析:你真的能正确使用吗?

    版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 好久没写博客了,最近忙着换工作,没时间写,工作刚定下来.稍后有时间会写一下换工作经历.接下来进入本篇主题,本来没想写LayoutInflater的 ...

最新文章

  1. python encodings模块_ImportError:没有名为'encodings'的模块
  2. C++条件编译:#ifdef
  3. -%3e运算符在c语言中的作用,C语言逻辑运算符知识整理
  4. JSP 标准标签库(JSTL)
  5. 2021年恩阳中学高考成绩查询,巴中市恩阳中学2021年排名
  6. NPM是什么?我们该怎么使用呢
  7. 尴尬……无人车美国赌城上路不到一小时就撞了
  8. Ansible详解(五)——Ansible其他模块使用详解
  9. 计算机没有光驱降无法启动,windows 未能启动 原因可能是最近更改了硬件或软件 没有光驱怎么办...
  10. 操作系统笔记-信号量机制
  11. 人工神经网络与遗传算法,神经网络和算法的关系
  12. Exp2 后门原理与实践 20164302 王一帆
  13. c语言题库-1012循环结构习题:公式求π值
  14. windows 时间同步工具软件
  15. 计算机未检测到任何网络硬件,电脑提示windows没有检测到任何网络硬件怎么办...
  16. air dots 配对_AirDots 使用分享
  17. Spring事务管理-tx:advice标签
  18. JAVA的内存回收机制(快速入门版)
  19. 一个tesseract ocr box 文件查看toy,python
  20. 进程与线程基础day02--------守护进程、系统日志、文件锁

热门文章

  1. 远程计算机管理权限,肿么获得远程计算机管理员权限
  2. java代码如何与界面联系在一起_如何在Visual Studio Code 中编写Java代码
  3. openssh windows_WINDOWS环境配置SSH服务
  4. C++知识点49——类继承与类的构造、拷贝、operator=和析构函数
  5. ANACONDA安装pytorch后Pycharm无法识别到解释器
  6. Web 开发学习笔记(1) --- 搭建你的第一个 Web Server
  7. firefox的plugin-container.exe进程如何关闭?
  8. OC基础数据类型-NSData-NSMutableData-NSString
  9. Oracle 用数据泵导入导出数据
  10. CFile类打开文件的有趣现象