Android Parcleable接口的调用源码层分析
Tech Ranger 人气:0Android 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。
加载全部内容