Android 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&gt; CREATOR              = new Parcelable.Creator&lt;MyParcelable&gt;() {               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。
整个流程到这里就结束了。Parcleable 接口的调用大家都明白了吧。

更多相关文章

  1. Android动态部署五:如何从插件apk中启动Service
  2. Android的一些小问题处理
  3. Android(安卓)对话框(Dialog)大全 建立你自己的对话框
  4. android如何调用显示和隐藏系统默认的输入法
  5. Android调用WIFI设置
  6. android > Service
  7. android中的TextView滾動條的設置
  8. 收藏Android下bitmap内存限制OUT OF MEMORY的方法
  9. 让android应用程序获得system权限

随机推荐

  1. android xliff字符串操作
  2. Service Manager进程启动,睡眠等待在进程p
  3. Error:Could not find com.android.tools
  4. Android中话bitmap的简单示例
  5. Android(安卓)4.x 去除输入框的蓝色边框
  6. Unknown Android(安卓)Packaging Problem
  7. How the Dalvik Virtual Machine Works o
  8. android登录窗口——基础编
  9. 图+文+文显示
  10. android 单元测试之dialog测试链接整理