Android之Parcelable解析


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); //調用我們自定義的那個方法
    }

最后終於從我們自定義的方法中恢復那個保存的實例。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM