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. Tomcat连接配置

    DBCP连接池配置: <bean class="org.apache.tomcat.jdbc.pool.PoolProperties"> <property na ...

  2. 美团CodeM资格赛第二题

    锦标赛 时间限制:1秒 空间限制:32768K 组委会正在为美团点评CodeM大赛的决赛设计新赛制.比赛有 n 个人参加(其中 n 为2的幂),每个参赛者根据资格赛和预赛.复赛的成绩,会有不同的积分. ...

  3. 链接脚本再探和VMA与LMA

    链接脚本简单描述 连接脚本的描述都是以节(section)的单位的,网上也有很多描述链接脚本语法的好文章,再不济还有官方的说明文档可以用来学习,其实主要就是对编译构建的整个过程有了深入的理解后就能对链 ...

  4. Linux 驱动框架---驱动中的异步

    异步IO是对阻塞和轮询IO的机制补充,所谓异步IO就是在设备数据就绪时主动通知所属进程进行处理的机制.之所以说是异步是相对与被通知进程的,因为进程不知道也无法知道什么时候会被通知:这一机制非常类似于硬 ...

  5. HLOD System

    1.1 HLOD System简介 首先,HLOD System主要的目标是为了减少Draw Call.然后,进行更多的Batch批处理,从而大大提高渲染性能,减少面数和纹理,这样我们相应地节省了内存 ...

  6. 如何使用 iMovie 去除视频里面的声音

    如何使用 iMovie 去除视频里面的声音 视频去除背景音 iMovie https://www.apple.com/imovie/ https://books.apple.com/book/id14 ...

  7. SVG 场馆图

    SVG 场馆图 https://www.infoq.cn/article/1BVg9VDSmqyHv3W3TeNH https://mp.weixin.qq.com/s/aNPAfJIHL14NFtL ...

  8. Flutter Navigator2.0

    Example 1 import 'package:dart_printf/dart_printf.dart'; import 'package:flutter/material.dart'; cla ...

  9. BGV等 DeFi产品暴涨背后隐藏着什么?

    比特币突破两万七千美金,在此创造了历史.在比特币一路飙升的背后,到底是谁注入了"强心针".笔者认为今年以来推动BTC长期上涨的主要动力主要包括四个:经济形势恶化.央行大量放水(主要 ...

  10. 基于tcp的应用层消息边界如何定义

    聊聊基于tcp的应用层消息边界如何定义 背景 2018年笔者有幸接触一个项目要用到长连接实现云端到设备端消息推送,所以借机了解过相关的内容,最终是通过rabbitmq+mqtt实现了相关功能,同时在心 ...