http://www.cnblogs.com/abinxm/archive/2011/11/16/2250949.html

http://www.cnblogs.com/renqingping/archive/2012/10/25/Parcelable.html

什么是Parcelable以及用法可以从上面两篇文章了解一二,本文关注其背后的实现机制是什么?

 * Interface for classes whose instances can be written to
* and restored from a {@link Parcel}. Classes implementing the Parcelable
* interface must also have a static field called <code>CREATOR</code>, which
* is an object implementing the {@link Parcelable.Creator Parcelable.Creator}
* interface.

如上,摘自Parcelable注释:如果想要写入Parcel或者从中恢复,则必须implements Parcelable并且必须有一个static field 而且名字必须是CREATOR....

好吧,感觉好复杂。有如下疑问:

1、Parcelable是干啥的?为什么需要它?

2、Parcel又是干啥的?

3、如果是写入Parcel中、从Parcelable中恢复,那要Parcelable岂不是“多此一举”?

下面逐个回答上述问题:

1、Parcelable是干啥的?从源码看:

public interface Parcelable {
...
public void writeToParcel(Parcel dest, int flags);
...

简单来说,Parcelable是一个interface,有一个方法writeToParcel(Parcel dest, int flags),该方法接收两个参数,其中第一个参数类型是Parcel。看起来Parcelable好像是对Parcelable的一种包装,从实际开发中,会在方法writeToParcel中调用Parcel的某些方法,完成将对象写入Parcelable的过程。

为什么往Parcel写入或恢复数据,需要继承Parcelable呢?我们看Intent的putExtra系列方法:

往Intent中添加数据,无法就是添加以上各种类型,简单的数据类型有对应的方法,如putExtra(String, String),复杂一点的有putExtra(String, Bundle),putExtra(String, Serializable)、putExtra(String, Bundle)、putExtra(String, Parcelable)、putExtra(String, Parcelable[])。现在想想,如果往Intent里添加一个我们自定义的类型对象person(Person类的实例),咋整?总不能用putExtra(String,person)吧?为啥,类型不符合啊!如果Person没有基础任何类,那它不可以用putExtra的任何一个方法,比较不存在putExtra(String, Object)这样一个方法。

那为了可以用putExtra方法,Person就需要继承一个可以用putExtra方法的类(接口),可以继承Parcelable——继承其他类(接口)也没有问题。

现在捋一捋:为了使用putExtra方法,需要继承Parcelable类——事实上,还有更深的含义,且看后面。

2、Parcel又是干啥的?前面说过,继承了Parcelable接口的类,如果不是抽象类,必须实现方法 writeToParcel,该方法有一个Parcel类型的参数,Parcel源码:

public final class Parcel {
...
public static Parcel obtain() {
final Parcel[] pool = sOwnedPool;
synchronized (pool) {
Parcel p;
for (int i=0; i<POOL_SIZE; i++) {
p = pool[i];
if (p != null) {
pool[i] = null;
if (DEBUG_RECYCLE) {
p.mStack = new RuntimeException();
}
return p;
}
}
}
return new Parcel(0);
}
...
public final native void writeInt(int val); public final native void writeLong(long val); ...

Parcel是一个final不可继承类,其代码很多,其中重要的一些部分是它有许多native的函数,在writeToParcel中调用的这些方法都直接或间接的调用native函数完成。

现在有一个问题,在public void writeToParcel(Parcel dest, int flags)中调用dest的函数,这个dest是传入进来的,是形参,那实参在哪里?没有看到有什么地方生成了一个Parcel的实例,然后调用writeToParcel啊??那它又不可能凭空出来。现在回到Intent这边来,看看它的内部做了什么事:

        Intent i = new Intent();
Person person = new Person();
i.putExtra("person", person);
i.setClass(this, SecondeActivity.class);
startActivity(i);

为了简单说明情况,我写了如上的代码,就不解释了。看看putExtra做了什么事情,看源码:

    public Intent putExtra(String name, Parcelable value) {
if (mExtras == null) {
mExtras = new Bundle();
}
mExtras.putParcelable(name, value);
return this;
}

这里调用的putExtra的第二个参数是Parcelable类型的,也印证了前面必须要类型符合(这里多说一句,面向对象的六大原则里有一个非常非常重要的“里氏替换”原则,子类出现的地方可以用父类代替,这样所有继承了Parcelable的类都可以传入这个putExtra中)。原来这里用到了Bundle类,看源码:

    public void putParcelable(String key, Parcelable value) {
unparcel();
mMap.put(key, value);
mFdsKnown = false;
}

mMap是一个Map。看到这里,原来我们传入的person被写入了Map里面了。这个Bundle也是继承自Parcelable的。其他putExtra系列的方法都是调用这个mMap的put。为什么要用Bundle的类里的Map?统一管理啊!所有传到Intent的extra我都不管,交给Bundle类来管理了,这样Intent类就不会太笨重(面向对象六大原则之迪米特原则——我不管你怎么整,整对了就行)。看Bundle源码:

public final class Bundle implements Parcelable, Cloneable {
private static final String LOG_TAG = "Bundle";
public static final Bundle EMPTY;

  //Bundle类一加载就生成了一个Bundle实例
static {
EMPTY = new Bundle();
EMPTY.mMap = Collections.unmodifiableMap(new HashMap<String, Object>());
}
/* package */ Map<String, Object> mMap = null; /* package */ Parcel mParcelledData = null;

看到这里,还是没有发现Parcel实例在什么地方生成,继续往下看,看startActivity这个方法,找到最后会发现最终启动Activity的是一个ActivityManagerNative类,查看对应的方法:

    public int startActivity(IApplicationThread caller, Intent intent,
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, String profileFile,
ParcelFileDescriptor profileFd, Bundle options) throws RemoteException {
Parcel data = Parcel.obtain(); //在这里生成了Parcel实例
Parcel reply = Parcel.obtain(); //又生成了一个Parcel实例
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
intent.writeToParcel(data, 0);
data.writeString(resolvedType);
data.writeStrongBinder(resultTo);
data.writeString(resultWho);
data.writeInt(requestCode);
data.writeInt(startFlags);
data.writeString(profileFile);
if (profileFd != null) {
data.writeInt(1);
profileFd.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;
}

千呼万唤的Parcel对象终于出现了,这里生成了俩Parcel对象:data和reply,主要的是data这个实例。obtain是一个static方法,用于从Parcel池(pool)中找出一个可用的Parcel,如果都不可用,则生成一个新的。每一个Parcel(Java)都与一个C++的Parcel对应。

    public static Parcel obtain() {
final Parcel[] pool = sOwnedPool;
synchronized (pool) {
Parcel p;
for (int i=0; i<POOL_SIZE; i++) {
p = pool[i];
if (p != null) {
pool[i] = null;
if (DEBUG_RECYCLE) {
p.mStack = new RuntimeException();
}
return p;
}
}
}
return new Parcel(0);
}
在intent.writeToParcel(data, 0)里,查看源码:
    public void writeToParcel(Parcel out, int flags) {
out.writeString(mAction);
Uri.writeToParcel(out, mData);
out.writeString(mType);
out.writeInt(mFlags);
out.writeString(mPackage);
ComponentName.writeToParcel(mComponent, out); if (mSourceBounds != null) {
out.writeInt(1);
mSourceBounds.writeToParcel(out, flags);
} else {
out.writeInt(0);
} if (mCategories != null) {
out.writeInt(mCategories.size());
for (String category : mCategories) {
out.writeString(category);
}
} else {
out.writeInt(0);
} if (mSelector != null) {
out.writeInt(1);
mSelector.writeToParcel(out, flags);
} else {
out.writeInt(0);
} if (mClipData != null) {
out.writeInt(1);
mClipData.writeToParcel(out, flags);
} else {
out.writeInt(0);
} out.writeBundle(mExtras); //终于把我们自定义的person实例送走了
}

看到这里Parcel实例终于生成了,但是我们重写的从Parcelable接口而来的writeToParcel这个方法在什么地方被调用呢?从上面的Intent中的out.writeBundle(mExtras)-->writeBundle(Bundle val)-->writeToParcel(Parcel parcel, int flags)-->writeMapInternal(Map<String,Object> val)-->writeValue(Object v)-->writeParcelable(Parcelable p, int parcelableFlags)(除了out.writeBundle(mExtras)这个方法,其他的方法都是在Bundle和Parcel里面调来调去的,真心累!):

    public final void writeParcelable(Parcelable p, int parcelableFlags) {
if (p == null) {
writeString(null);
return;
}
String name = p.getClass().getName();
writeString(name);
p.writeToParcel(this, parcelableFlags);//调用自己实现的方法
}

OK,终于出来了。。。到这里,写入的过程已经出来了。

那如何恢复呢?这里用到的是我们自己写的createFromParcel这个方法,从一个Intent中恢复person:

        Intent i= getIntent();
Person p = i.getParcelableExtra("person");

调啊调,调到这个:

    public final <T extends Parcelable> T readParcelable(ClassLoader loader) {
String name = readString();
if (name == null) {
return null;
}
Parcelable.Creator<T> creator;
synchronized (mCreators) {
HashMap<String,Parcelable.Creator> map = mCreators.get(loader);
if (map == null) {
map = new HashMap<String,Parcelable.Creator>();
mCreators.put(loader, map);
}
creator = map.get(name);
if (creator == null) {
try {
Class c = loader == null ?
Class.forName(name) : Class.forName(name, true, loader);
Field f = c.getField("CREATOR");
creator = (Parcelable.Creator)f.get(null);
}
catch (IllegalAccessException e) {
Log.e(TAG, "Class not found when unmarshalling: "
+ name + ", e: " + e);
throw new BadParcelableException(
"IllegalAccessException when unmarshalling: " + name);
}
catch (ClassNotFoundException e) {
Log.e(TAG, "Class not found when unmarshalling: "
+ name + ", e: " + e);
throw new BadParcelableException(
"ClassNotFoundException when unmarshalling: " + name);
}
catch (ClassCastException e) {
throw new BadParcelableException("Parcelable protocol requires a "
+ "Parcelable.Creator object called "
+ " CREATOR on class " + 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 "
+ "Parcelable.Creator object called "
+ " CREATOR on class " + name);
} map.put(name, creator);
}
} if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
return ((Parcelable.ClassLoaderCreator<T>)creator).createFromParcel(this, loader);
}
return creator.createFromParcel(this); //调用我们自定义的那个方法
}

最后终于从我们自定义的方法中恢复那个保存的实例。

Android之Parcelable解析的更多相关文章

  1. Android Service完全解析,关于服务你所需知道的一切(下)

    转载请注册出处:http://blog.csdn.net/guolin_blog/article/details/9797169 在上一篇文章中,我们学习了Android Service相关的许多重要 ...

  2. android源码解析(十七)-->Activity布局加载流程

    版权声明:本文为博主原创文章,未经博主允许不得转载. 好吧,终于要开始讲讲Activity的布局加载流程了,大家都知道在Android体系中Activity扮演了一个界面展示的角色,这也是它与andr ...

  3. 【转】Android Service完全解析,关于服务你所需知道的一切(下) ---- 不错

    原文网址:http://blog.csdn.net/guolin_blog/article/details/9797169 转载请注册出处:http://blog.csdn.net/guolin_bl ...

  4. Android中Parcelable序列化总结

    在使用Parcelable对android中数据的序列化操作还是比较有用的,有人做过通过对比Serializable和Parcelable在android中序列化操作对象的速度比对,大概Parcela ...

  5. 《Android源代码设计模式解析》读书笔记——Android中你应该知道的设计模式

    断断续续的,<Android源代码设计模式解析>也看了一遍.书中提到了非常多的设计模式.可是有部分在开发中见到的几率非常小,所以掌握不了也没有太大影响. 我认为这本书的最大价值有两点,一个 ...

  6. [转]Android Service完全解析,关于服务你所需知道的一切

      目录(?)[+] Android Service完全解析,关于服务你所需知道的一切(上) 分类: Android疑难解析2013-10-31 08:10 6451人阅读 评论(39) 收藏 举报 ...

  7. 【Android开发精要笔记】Android组件模型解析

    Android组件模型解析 Android中的Mashup 将应用切分成不同类别的组件,通过统一的定位模型和接口标准将他们整合在一起,来共同完成某项任务.在Android的Mashup模式下,每个组件 ...

  8. Android Service完全解析,关于服务你所需知道的一切(下) (转载)

    转自:http://blog.csdn.net/guolin_blog/article/details/9797169 转载请注册出处:http://blog.csdn.net/guolin_blog ...

  9. Android Service完全解析,关于服务你所需知道的一切(上)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/11952435 相信大多数朋友对Service这个名词都不会陌生,没错,一个老练的A ...

随机推荐

  1. kubeadm---高可用安装

    1.修改主机名 如何使用hostnamectl set-hostname name来为每台主机设置不同的机器名 #hostnamectl set-hostname k8s-master01 或者使用以 ...

  2. Linux系统CentOS进入单用户模式和救援模式详解

    一.概述 目前在运维日常工作中,经常会遇到服务器异常断电.忘记root密码.系统引导文件损坏无法进入系统等等操作系统层面的问题,给运维带来诸多不便,现将上述现象的解决方法和大家分享一下,本次主要以Ce ...

  3. Cron表达式在 定时执行专家 5.0 中的使用方式

    在<定时执行专家 V5.0>程序内部使用了包含 6 位的 Cron表达式,第一个字段(second)没有使用.程序内部一直 second 位是 0.在 Cron表达式的界面上可以设置 5位 ...

  4. LWIP再探----内存堆管理

    LWIP的内存管理主要三种:内存池Pool,内存堆,和C库方式.三种方式中C库因为是直接从系统堆中分配内存空间且易产生碎片因此,基本不会使用,其他两种是LWIP默认全部采用的方式,也是综合效率和空间的 ...

  5. TypeScript Developer Roadmap

    TypeScript Developer Roadmap https://github.com/xgqfrms/TypeScript-Developer-Roadmap https://typescr ...

  6. CSS3实现 垂直居中 水平居中 的技巧

    1 1 1 How To Center Anything With CSS Front End posted by Code My Views 1 Recently, we took a dive i ...

  7. 树莓派 4B 入门教程

    树莓派 4B 入门教程 Raspberry Pi, Raspberry Pi 3B, Raspberry Pi 4B 树莓派 4B 入门手册 PDF Raspberry Pi Beginners Gu ...

  8. input support upload excel only

    input support upload excel only demo https://codepen.io/xgqfrms/pen/vYONpLB <!-- <input placeh ...

  9. TypeScript——02——TS基本数据类型介绍和使用

    一,TS的数据类型 ES6的数据类型: 6种基本数据类型 Boolean Number String Symbol undefined null 3种引用类型 Array Function Objec ...

  10. go好用的类型转换第三方组件

    Cast介绍 开源地址 https://github.com/spf13/cast Cast是什么? Cast是一个库,以一致和简单的方式在不同的go类型之间转换. Cast提供了简单的函数,可以轻松 ...