软件编程
位置:首页>> 软件编程>> Android编程>> Android Parcleable接口的调用源码层分析

Android Parcleable接口的调用源码层分析

作者:Tech  发布时间:2023-07-09 20:43:48 

标签:Android,Parcelable,接口,调用,源码

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。

来源:https://blog.csdn.net/sinat_36955332/article/details/108350457

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com