Android Parcelable 源码解析

大家都知道,要想在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. */ @Override public 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 length parcel.writeInt(BUNDLE_MAGIC); int startPos = parcel.dataPosition(); parcel.writeArrayMapInternal(map); int endPos = parcel.dataPosition(); // Backpatch length parcel.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 Parcelable writeInt(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 Parcel int 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。
整个流程到这里就结束了。Parcleable 接口的调用大家都明白了吧。

【Android】Android Parcelable 源码解析相关推荐

  1. http://a.codekk.com/detail/Android/grumoon/Volley 源码解析

    http://a.codekk.com/detail/Android/grumoon/Volley 源码解析

  2. BAT高级架构师合力熬夜15天,肝出了这份PDF版《Android百大框架源码解析》,还不快快码住。。。

    前言 为什么要阅读源码? 现在中高级Android岗位面试中,对于各种框架的源码都会刨根问底,从而来判断应试者的业务能力边际所在.但是很多开发者习惯直接搬运,对各种框架的源码都没有过深入研究,在面试时 ...

  3. Android通知系统源码解析

    Android通知系统源码解析 1. 概述 2. 流程图 2.1. 发送通知流程图 3. 源码解析 3.1. 使用通知--APP进程 3.1.1. 创建通知: 3.1.2. 发送(更新)通知: 3.1 ...

  4. Android Gradle Plugin 源码解析(上)

    一.源码依赖 本文基于: android gradle plugin版本: com.android.tools.build:gradle:2.3.0 gradle 版本:4.1 Gradle源码总共3 ...

  5. Android之EasyPermissions源码解析

    转载请标明出处:[顾林海的博客] 个人开发的微信小程序,目前功能是书籍推荐,后续会完善一些新功能,希望大家多多支持! 前言 我们知道在Android中想要申请权限就需要在AndroidManifest ...

  6. Android之AsyncTask源码解析

    转载请标明出处:[顾林海的博客] 个人开发的微信小程序,目前功能是书籍推荐,后续会完善一些新功能,希望大家多多支持! ##前言 AsyncTask是一种轻量级的异步任务类,内部封装了Thread和Ha ...

  7. Android之DiskLruCache源码解析

    转载请标明出处: http://blog.csdn.net/hai_qing_xu_kong/article/details/73863258 本文出自:[顾林海的博客] 个人开发的微信小程序,目前功 ...

  8. Android Hawk的源码解析,一款基于SharedPreferences的存储框架

    转载请标注:http://blog.csdn.net/friendlychen/article/details/76218033 一.概念 SharedPreferences的使用大家应该非常熟悉啦. ...

  9. Android之LocalBroadcastManager源码解析

    转载请标明出处:[顾林海的博客] 个人开发的微信小程序,目前功能是书籍推荐,后续会完善一些新功能,希望大家多多支持! 前言 广播想必大家都不陌生,日常开发中同一个APP中的多个进程之间需要进行传输信息 ...

最新文章

  1. 斗地主发牌编程PHP,JAVA代码之斗地主发牌详解
  2. css 选择器(标签选择器、类选择器、层级选择器、id选择器、组选择器、伪类选择器、通配符选择器)
  3. 【盘点】北美顶尖学府的人工智能与智能车实验室
  4. 松开手,你可以拥有更多
  5. 软件质量保证与测试——Smoke Test
  6. java数据从本地文件中取出_java 从数据库取数据并存入本地文本中
  7. map怎么转化dto_java实现Object和Map之间的转换3种方式
  8. POJ2527(两多项式取余)
  9. LQ训练营(C++)学习笔记_广度优先搜索
  10. bzoj1013,luogu4035-[JSOI2008]球形空间产生器【高斯消元】
  11. WdatePicker日历控件使用方法
  12. 预测未来的环境,提前做出改变
  13. 基于JSP的图书销售管理系统
  14. Android数据库大批量数据插入优化
  15. Qt官方示例-语法高亮器
  16. (转载)洪磊并不是反叛英雄
  17. Cobot与Jenkins集成
  18. java计算机毕业设计郑工社团交流服务信息平台源码+mysql数据库+系统+lw文档+部署
  19. uniapp开发app过程中集成友盟统计
  20. Mysql:<foreach> </foreach>

热门文章

  1. app读取低功耗蓝牙设备的数据并返回数据
  2. python之布尔值
  3. 让 CheckStyle 支持增量检查的一次落地经验总结
  4. 如何用手机上网给电信固话充值
  5. 人工智能和人类智能的本质区别是什么?
  6. S7-200 PLC和组态王组态温度PID控制加热炉电阻炉
  7. nordic 网站教程
  8. 初学c语言数据类型——变量
  9. 1024统信举办首届技术开放日,硬核技术引领操作系统“大迁移”
  10. NandFlash详解