Bundle机制

Bundle的使用场景

Bundle对于Android开发者来说肯定非常眼熟,它经常出现在以下场合:

  1. Activity状态数据的保存与恢复涉及到的两个回调:void onSaveInstanceState (Bundle
    outState)、void onCreate (Bundle savedInstanceState)
  2. Fragment的setArguments方法:void setArguments (Bundle args)
  3. 消息机制中的Message的setData方法:void setData (Bundle data)

Bundle从字面上解释为“一捆、一批、一包”,可以将Bundle理解为Android中用来传递数据的一个容器。

官方文档对Bundle的说明如下:
Bundle实现了Parcelable接口,所以他可以方便的在不同进程间传输,这里要注意我们传输的数据必须能够被序列化;

Bundle源码分析

Bundle的继承关系:

/**
 * A mapping from String keys to various {@link Parcelable} values.
 *
 * @see PersistableBundle
 */
public final class Bundle extends BaseBundle implements Cloneable, Parcelable {

第一,Bundle使用final修饰,所以不可以被继承

第二,Bundle实现了两个接口,cloneable和Parcelable,这就意味着他必须实现以下方法:

public Object clone()

public int describeContents()
public void writeToParcel(Parcel parcel, int flags)
public static final Parcelable.Creator<Bundle> CREATOR = new Parcelable.Creator<Bundle>()

Bundle有两个重要成员变量:mMap 和 mParcelledData

    // Invariant - exactly one of mMap / mParcelledData will be null
    // (except inside a call to unparcel)

    ArrayMap<String, Object> mMap = null;

    /*
     * If mParcelledData is non-null, then mMap will be null and the
     * data are stored as a Parcel containing a Bundle.  When the data
     * are unparcelled, mParcelledData willbe set to null.
     */
    Parcel mParcelledData = null;

可以看到,Bundle其实就是一个容器,内部使用了Arraymap去存储数据。

当Bundle里的数据还没有被打包(parcel)或者已经被unparcel时,数据存储在mMap中,即mMap非null,而mParcelledData = null。
当Bundle里的数据已经被打包时,数据存储在mParcelledData 中,而mMap = null。

Parcelable

Bundle实现了Parcelable接口,意味着Bundle对象可以转为Parcel对象。

ArrayMap

ArrayMap是Android特有的容器,它设计上更多的是考虑内存的优化,在数据量比较小时性能比较好,用于取代HashMap。

Parcel

Parcel 和 Parcelable(Parcelable接口用于将任意实现了Parcelable接口的对象转为Parcel对象) 是Android专门针对高性能IPC传输而设计的,并不是为了通用的序列化方案而设计的。因此,Parcel数据不能进行持久化,因为Parcel 里面的数据的实现方式可能会改变,这会导致旧的Parcel数据无法被读取。

在Android系统中,定位为针对内存受限的设备,因此对性能要求更高,另外系统中采用了新的IPC(进程间通信)机制,必然要求使用性能更出色的对象传输方式。因此,Parcel被设计出来,其定位就是用于IPC的轻量级的高效的对象序列化和反序列化机制。

Android中序列化有以下几个特征:
1. 整个读写全是在内存中进行。
2. 读写时是4字节对齐的
3. 如果预分配的空间不够时,会一次多分配50%;
4. 对于普通数据,使用的是mData内存地址,对于IBinder类型的数据以及FileDescriptor使用的是mObjects内存地址。后者是通过flatten_binder()和unflatten_binder()实现的,目的是反序列化时读出的对象就是原对象而不用重新new一个新对象。

writeToParcel方法

将Bundle对象里的数据写成一个Parcel对象。

CREATOR

CREATOR对象是实现了Parcelable.Creator接口的一个对象,用于从Parcel对象中生成Parcelable的实例(这里就是用于生成Bundle对象)。

unparcel()方法

/* package */ void unparcel() {
    synchronized (this) {
        final Parcel parcelledData = mParcelledData;
        if (parcelledData == null) {
            if (DEBUG) Log.d(TAG, "unparcel "
                    + Integer.toHexString(System.identityHashCode(this))
                    + ": no parcelled data");
            return;
        }

        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()) {
            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;
        }

        int N = parcelledData.readInt();
        if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
                + ": reading " + N + " maps");
        if (N < 0) {
            return;
        }
        ArrayMap<String, Object> map = mMap;
        if (map == null) {
            map = new ArrayMap<>(N);
        } else {
            map.erase();
            map.ensureCapacity(N);
        }
        try {
            parcelledData.readArrayMapInternal(map, N, 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;
            parcelledData.recycle();
            mParcelledData = null;
        }
        if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
                + " final map: " + mMap);
    }
}

mParcelledData的取值有3种情况:

  1. mParcelledData = EMPTY_PARCEL
  2. mParcelledData = Parcel.obtain()
  3. mParcelledData = null

在unparcel()方法中就对上述几种情况做了不同的处理,当mParcelledData为null时,直接返回;当mParcelledData为EMPTY_PARCEL时,会创建一个容量为1的ArrayMap对象;当mParcelledData为Parcel.obtain()时,则会将里面的数据读出,并创建一个ArrayMap,并将数据存储到ArrayMap对象里面,同时将mParcelledData回收并置为null;

参考:
Android面试题(25)-Bundle机制
Android中的Parcel机制 实现Bundle传递对象
Android Bundle总结

Android跨进程通信IPC之6——Parcel–Binder对象的写入和读出

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值