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> CREATOR
* = new Parcelable.Creator<MyParcelable>() {
* 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。
ok ,整个流程到这里就结束了。Parcleable 接口的调用大家都明白了吧。
Android Parcelable 源码解析的更多相关文章
- Android -- AsyncTask源码解析
1,前段时间换工作的时候,关于AsyncTask源码这个点基本上大一点的公司都会问,所以今天就和大家一起来总结总结.本来早就想写这篇文章的,当时写<Android -- 从源码解析Handle+ ...
- Android -- 从源码解析Handle+Looper+MessageQueue机制
1,今天和大家一起从底层看看Handle的工作机制是什么样的,那么在引入之前我们先来了解Handle是用来干什么的 handler通俗一点讲就是用来在各个线程之间发送数据的处理对象.在任何线程中,只要 ...
- Android AsyncTask 源码解析
1. 官方介绍 public abstract class AsyncTask extends Object java.lang.Object ↳ android.os.AsyncTask&l ...
- Android EventBus源码解析 带你深入理解EventBus
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/40920453,本文出自:[张鸿洋的博客] 上一篇带大家初步了解了EventBus ...
- Android LayoutInflater源码解析:你真的能正确使用吗?
版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 好久没写博客了,最近忙着换工作,没时间写,工作刚定下来.稍后有时间会写一下换工作经历.接下来进入本篇主题,本来没想写LayoutInflater的 ...
- Android HandlerThread源码解析
在上一章Handler源码解析文章中,我们知道App的主线程通过Handler机制完成了一个线程的消息循环.那么我们自己也可以新建一个线程,在线程里面创建一个Looper,完成消息循环,可以做一些定时 ...
- Android——LruCache源码解析
以下针对 Android API 26 版本的源码进行分析. 在了解LruCache之前,最好对LinkedHashMap有初步的了解,LruCache的实现主要借助LinkedHashMap.Lin ...
- 还怕问源码?Github上神级Android三方源码解析手册,已有7.6 KStar
或许对于许多Android开发者来说,所谓的Android工程师的工作"不过就是用XML实现设计师的美术图,用JSON解析服务器的数据,再把数据显示到界面上"就好了,源码什么的,看 ...
- Android DiskLruCache 源码解析 硬盘缓存的绝佳方案
一.概述 依旧是整理东西,所以近期的博客涉及的东西可能会比较老一点,会分析一些经典的框架,我觉得可能也是每个优秀的开发者必须掌握的东西:那么对于Disk Cache,DiskLruCache可以算佼佼 ...
随机推荐
- BZOJ2141:排队(分块,树状数组)
Description 排排坐,吃果果,生果甜嗦嗦,大家笑呵呵.你一个,我一个,大的分给你,小的留给我,吃完果果唱支歌,大家 乐和和.红星幼儿园的小朋友们排起了长长地队伍,准备吃果果.不过因为小朋友们 ...
- BZOJ4530:[BJOI2014]大融合(LCT)
Description 小强要在N个孤立的星球上建立起一套通信系统.这套通信系统就是连接N个点的一个树. 这个树的边是一条一条添加上去的.在某个时刻,一条边的负载就是它所在的当前能够 联通的树上路过它 ...
- Codeforces Round #540 (Div. 3) D1. Coffee and Coursework (Easy version) 【贪心】
任意门:http://codeforces.com/contest/1118/problem/D1 D1. Coffee and Coursework (Easy version) time limi ...
- [19/04/05-星期五] 多线程_Thread(线程、线条)、基本术语
一.基本概念 多线程是Java语言的重要特性,大量应用于网络编程.服务器端程序的开发,最常见的UI界面底层原理.操作系统底层原理都大量使用了多线程. 我们可以流畅的点击软件或者游戏中的各种按钮,其实, ...
- springmvc小结(上)
1.springmvc的整体结构以及流程 ①.前端控制器:只需要在web.xml文件中配置即可 作用:接受请求,处理响应结果,转发器,中央处理器 ②.处理器映射器:根据请求的url找到相应的Handl ...
- PHP日志 LOG4PHP 的配置与使用
维护了 一个老项目, 没有日志功能, 就给加了这个log4php, 主要是集成进去很简单,使用起来也够用了. 1.下载log4php 2.创建配置文件 log4php_config.xml < ...
- 关于webstorm打开项目,文件下方出现了一个小锁的图标,修改文件出现“cannot modify a ready-only directory”的弹窗提示
今天用webstorm打开项目,文件下方出现了一个小锁的图标,修改文件出现“cannot modify a ready-only directory”的弹窗提示 解决办法:运行 sudo chown ...
- 说说DBA职责和目标
数据库管理员(Database Administrator,简称DBA),是从事管理和维护数据库管理系统(DBMS)的相关工作人员的统称,他属于运维工程师的一个分支,主要负责业务数据库从设计.测试到部 ...
- 分享一个可下拉刷新的ScrollView
原理:就是动态改变ScrollView header的margin实现 主要的代码: http://blog.csdn.net/swust_chenpeng/article/details/39289 ...
- HDU 2080(三角函数)
传送门:http://acm.hdu.edu.cn/showproblem.php?pid=2080 夹角有多大II Time Limit: 1000/1000 MS (Java/Others) ...