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)
  4. 其他场景不再列举

Bundle从字面上解释为“一捆、一批、一包”,结合上述几个应用场合,可以知道Bundle是用来传递数据的,我们暂将Bundle理解为Android中用来传递数据的一个容器。官方文档对Bundle的说明如下:

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

Bundle源码分析

首先看它的声明

public final class Bundle extends BaseBundle implements Cloneable, Parcelable

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

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

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

再看他的内存结构:

ArrayMap<String, Object> mMap = null;

使用的是ArrayMap,这个集合类存储的也是键值对,但是与Hashmap不同的是,hashmap采用的是“数组+链表”的方式存储,而Arraymap中使用的是两个数组进行存储,一个数组存储key,一个数组存储value,内部的增删改查都将会使用二分查找来进行,这个和SparseArray差不多,只不过sparseArray的key值只能是int型的,而Arraymap可以是map型,所以在数据量不大的情况下可以使用这两个集合代替hashmap去优化性能;

我们知道Bundle其实就是一个容器,内部使用了Arraymap去存储数据,那么就必然会提供get,put方法,由于Bundle支持的数据类型太多,这里我们就看一个布尔类型的,其他类型的方式都差不多;

getBoolean

public boolean getBoolean(String key, boolean defaultValue) {unparcel();
    Object o = mMap.get(key);
    if (o == null) {return defaultValue;
    }try {return (Boolean) o;
    } catch (ClassCastException e) {typeWarning(key, o, "Boolean", defaultValue, e);
        return defaultValue;
    }
}

数据读取的逻辑很简单,就是通过key从ArrayMap里读出保存的数据,并转换成对应的类型返回,当没找到数据或发生类型转换异常时返回缺省值。

putBoolean

public void putBoolean(@Nullable String key, boolean value) {unparcel();
    mMap.put(key, value);
}

这里出现了一个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);
    }
}

先来看下BaseBundle中mParcelledData的定义:

Parcel mParcelledData = null;

在大部分情况下mParcelledData都是null,因此unparcel()直接返回。当使用构造函数public Bundle(Bundle b)创建Bundle时,会给mParcelledData赋值;

void copyInternal(BaseBundle from, boolean deep) {synchronized (from) {if (from.mParcelledData != null) {if (from.isEmptyParcel()) {mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL;
            } else {mParcelledData = Parcel.obtain();
                mParcelledData.appendFrom(from.mParcelledData, 0,
                        from.mParcelledData.dataSize());
                mParcelledData.setDataPosition(0);
            }} else {mParcelledData = null;
        }if (from.mMap != null) {if (!deep) {mMap = new ArrayMap<>(from.mMap);
            } else {final ArrayMap<String, Object> fromMap = from.mMap;
                final int N = fromMap.size();
                mMap = new ArrayMap<>(N);
                for (int i = 0; i < N; i++) {mMap.append(fromMap.keyAt(i), deepCopyValue(fromMap.valueAt(i)));
                }}} else {mMap = null;
        }mClassLoader = from.mClassLoader;
    }
}

从上述代码片段可以知道mParcelledData的取值有3种情况:

  • mParcelledData = EMPTY_PARCEL
  • mParcelledData = Parcel.obtain()
  • mParcelledData = null

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

Android面试题(25)-Bundle机制相关推荐

  1. android面试题总结加强

    在加强版的基础上又再加强的android应用面试题集 有些补充略显臃肿,只为学习 1.activity的生命周期. 方法 描述 可被杀死 下一个 onCreate() 在activity第一次被创建的 ...

  2. 金三银四的面试黄金季节,Android面试题来了!

    金三银四的跳槽季节,你准摆好了吗? 首先我们分享一个Android知识图谱. 下面是一些面试官基本必问的问题,请一定要去了解! 基础知识 – 四大组件(生命周期,使用场景,如何启动) java基础 – ...

  3. Android面试题收集(有具体答案)

    Android面试题目及其答案 1.Android dvm的进程和Linux的进程, 应用程序的进程是否为同一个概念 DVM指dalivk的虚拟机.每个Android应用程序都在它自己的进程中执行,都 ...

  4. android+面试题

    1.常用的存储方式有哪些?(概率50%) (五种,说出哪五种,五种存储方式什么情况下用.)注意sharepreferes对象支持读取不支持写入,写入引用Editor. SQLite: SQLite是一 ...

  5. Android面试题集1

    1. 什么是Activity? 四大组件之一,一般的,一个用户交互界面对应一个activity setContentView() ,// 要显示的布局 , activity 是Context的子类,同 ...

  6. 19、android面试题整理(自己给自己充充电吧)

    (转载,出处丢失,请原作者原谅,如有意见,私信我我会尽快删除本文) JAVA 1.GC是什么? 为什么要有GC? GC是垃圾收集的意思(Gabage Collection),内存处理是编程人员容易出现 ...

  7. Android面试题及答案3

    Java + Android面试题 JAVA相关基础知识 1.面向对象的特征有哪些方面 1.抽象: 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面. 抽象并不打算 ...

  8. android 面试题(三)

    JAVA相关基础知识 1.面向对象的特征有哪些方面   1.抽象: 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面. 抽象并不打算了解全部问题,而只是选择其中的一 ...

  9. Android面试题和答案

    71道经典Android面试题和答案 下列哪些语句关于内存回收的说明是正确的? (b ) A. 程序员必须创建一个线程来释放内存 B.内存回收程序负责释放无用内存 C.内存回收程序允许程序员直接释放内 ...

  10. Android面试题收集(有详细答案)

    Android面试题目及其答案 1.Android dvm的进程和Linux的进程, 应用程序的进程是否为同一个概念 DVM指dalivk的虚拟机.每一个Android应用程序都在它自己的进程中运行, ...

最新文章

  1. Java中import及package的用法
  2. C# 类型实例化的语法糖--unity下诡异结果
  3. 比特币现金的一年回顾
  4. 通用联手谷歌,应用程序和语音助手将整合到车辆中
  5. 计算机网络(10)-----TCP的拥塞控制
  6. android activity 被notification启动,Android通知Notification全面剖析
  7. # 保持最外层获取焦点_大事件!沈阳爱尔白内障焕晶诊疗中心正式启用,两位PanOptix三焦点人工晶体植入患者清晰见证!...
  8. MySQL 语句优化 ICP
  9. 猎户星空否认停发高管薪资:无论遭遇怎样困难 都不会苛扣员工薪酬
  10. 协方差矩阵经线性变化可以变成不相关的
  11. VIM空格和TAB转换
  12. java怎么把程序写入持久化_如何将DataFrame持久化到Hive表?
  13. 在那里可以下载jar包?
  14. html动态表格用数组填充,自动填充数组中的HTML表格
  15. YII2框架AJAX请求报500错误的处理方法
  16. Echarts曲线渐变色lineStyle
  17. 大数据在 IoT 的应用
  18. Vue-Router学习记录
  19. 怎么让照片变年轻_如何用ps把人变年轻水嫩
  20. ATECC508A芯片开发笔记(八):ECDH算法配置方法、执行过程及实现原理

热门文章

  1. lol6月五日服务器维护,lol5月6日维护公告
  2. Linux系统 运行小花仙游戏(针对2021年Flash停止维护的情况)
  3. 记住沃伦巴菲特这三十条
  4. S7-1500 PLC编程
  5. android 隐藏Settings菜单的几种方法
  6. 个人空间岁末大回报活动12月28日获奖名单
  7. 一家企业怎样才算Cool?Gartner告诉你!
  8. 一文读懂Jina生态的Dataclass
  9. 今天是2012年9月20日
  10. 分别利用opencv和matplotlib.pyplot来展示图片