欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android Parcleable接口的調(diào)用源碼層分析

 更新時間:2022年12月15日 08:31:41   作者:Tech Ranger  
這篇文章主要給大家介紹了關(guān)于利用Kotlin如何實現(xiàn)Android開發(fā)中的Parcelable的相關(guān)資料,并且給大家介紹了關(guān)于Android Parcleable源碼層問題,需要的朋友可以參考下

Android Parcelable 源碼解析

大家都知道,要想在Intent里面?zhèn)鬟f一些非基本類型的數(shù)據(jù),有兩種方式,一種實現(xiàn)Parcelable,另一種是實現(xiàn)Serializable接口。今天先不說Serializable 接口,只說Parcelable。我們知道,Parcelable 只是一個接口,里面有幾個關(guān)鍵方法:

一、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); 

這個方法會讓你把當(dāng)前你需要保存的數(shù)據(jù),寫進(jìn)Parcel 里。flags 可以寫0 ,也可以寫PARCELABLE_WRITE_RETURN_VALUE。這兩個什么區(qū)別呢?后面再說。

這個里面,你需要調(diào)用傳給你的Parcel 對象dest,把你需要的數(shù)據(jù)傳遞進(jìn)去。Such as:

     public void writeToParcel(Parcel out, int flags) { 
         out.writeInt(mData); 
     } 

同時需要實現(xiàn)一個Creator, 用來恢復(fù)對象,如果沒有實現(xiàn)這個Creator,那么恢復(fù)的時候,會報錯。

  /** 
     * 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)

這個方法是,當(dāng)你恢復(fù)對象的時候,會把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 其實也是實現(xiàn)了Parcelable 接口

public final class Bundle extends BaseBundle implements Cloneable, Parcelable { 

我們看下Bundle putParcelable 的實現(xiàn):

/** 
 * 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; 
} 

進(jìn)入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 進(jìn)去。

ok ,傳遞數(shù)據(jù)的時候,Bundle 是要傳遞過去的,肯定會調(diào)用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); 
        } 
    } 

調(diào)用了 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); 
} 

里面寫了一堆,關(guān)鍵是 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()); 
        } 
.... 
    } 

如果發(fā)現(xiàn)寫的是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 對象的類名字,然后調(diào)用了Parcelable 對象的writeToParcel。也就是自己實現(xiàn)的方法,就會把我們想要傳遞的數(shù)據(jù)寫到Parcel 里面去。

OK ,這樣,Parcelable 接口的writeToParcel 方法就被調(diào)用了。

我們再看下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; 
    } 

很簡單,把當(dāng)前的mParcelledData 賦了值。

我們調(diào)用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)

最終調(diào)用了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(); 
} 

調(diào)用了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 類,如果發(fā)現(xiàn)沒有creator 就會拋異常。等等,最終調(diào)用了你的類的createFromParcel。

到此這篇關(guān)于Android Parcleable接口的調(diào)用源碼層分析的文章就介紹到這了,更多相關(guān)Android Parcleable接口內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論