博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android之Parcelable解析
阅读量:6241 次
发布时间:2019-06-22

本文共 9909 字,大约阅读时间需要 33 分钟。

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 CREATOR, 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

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
()); } /* package */ Map
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
在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 readParcelable(ClassLoader loader) { String name = readString(); if (name == null) { return null; } Parcelable.Creator
creator; synchronized (mCreators) { HashMap
map = mCreators.get(loader); if (map == null) { map = new HashMap
(); 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
)creator).createFromParcel(this, loader); } return creator.createFromParcel(this); //调用我们自定义的那个方法 }

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

 

转载于:https://www.cnblogs.com/littlefishxu/p/4432802.html

你可能感兴趣的文章
Script to Collect Data Guard Diagnostic Information
查看>>
cell manager opening cell等待事件
查看>>
Autodesk 首届中国开发者夏令营将在6月19-20在北京举行
查看>>
Visual Studio 2012 Update 2 (KB2707250)
查看>>
MDX Step by Step 读书笔记(三) - Understanding Tuples (理解元组)
查看>>
Android 封装http请求的工具类
查看>>
黑书上的DP例题
查看>>
每天一个新标签/方法/属性/兼容性/问题
查看>>
《Linux内核设计与实现》读书笔记(九)- 内核同步介绍
查看>>
Delphi-IOCP 共同学习研究群号 320641073
查看>>
sql2008中已存在已有数据表修改主键为自增不让更改的解决方案
查看>>
控件路径自定义控件遇到的两个小问题
查看>>
【BZOJ】2648: SJY摆棋子 & 2716: [Violet 3]天使玩偶(kdtree)
查看>>
数据仓库与数据挖掘的一些基本概念
查看>>
Android学习系列(23)--App主界面实现
查看>>
jquery validate的漂亮css样式验证
查看>>
OAF_解决OAF与Windows版本不兼容黑屏
查看>>
如何让编码更加的标准
查看>>
阿里云收集服务器性能指标的python脚本
查看>>
Docker源码分析(一):Docker架构
查看>>