zoukankan      html  css  js  c++  java
  • 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。

    ok ,整个流程到这里就结束了。Parcleable 接口的调用大家都明白了吧。

  • 相关阅读:
    ThreadLocal
    mysql查看和修改密码策略
    synchronized双重校验问题
    多线程下双重检查锁的问题及解决方法
    compiler explorer网站
    隐藏表格部分内容,开启宏自动显现
    powershell系列学习笔记二:变量
    强制用户启用宏
    poweshell系列学习笔记一:基础
    Cobalt Strike修改证书
  • 原文地址:https://www.cnblogs.com/caoxinyu/p/10568491.html
Copyright © 2011-2022 走看看