Hi 大家好,我是 DHL,大厂程序员,公众号:ByteCode ,在美团、快手、小米工作过。搞过逆向,做过性能优化,研究过系统,擅长鸿蒙、Android、Kotlin、性能优化、职场分享。
为什么使用 Bundle 而不使用 HashMap
Bundle
内部是由 ArrayMap
实现的,ArrayMap
和 HashMap
是 Android 中常用的两种键值对数据结构,关于它们的区别可以前往微信小程序「猿面试」中查看 「ArrayMap和HashMap的区别」。
这篇文章我们一起来看一下在 Android 中为什么使用 Bundle 而不使用 HashMap。
Bundle 相比 HashMap 占用更少内存
Bundle
内部是由 ArrayMap
实现的,ArrayMap
的内部实现是两个数组,一个记录所有 key 的 hashcode 值组成的数组,是从小到大的排序方式,一个存放 key-value。
内部使用二分法进行查找,时间复杂度为 O (log n),但由于数组操作(如插入、删除)需要移动元素,插入和删除的时间复杂度为 O (n),因此只适合于小数据量操作。
ArrayMap
满足条件会扩容后的大小为原来的 1.5 倍,并且有容量收缩机制,当内存使用量不足 1/3 的情况下,内存数组会收紧 50%。
HashMap(1.8)内部则是数组 + 链表 + 红黑树结构,默认大小为 16,超过容量 3/4 的时候,双倍扩容,且没有容量收缩机制,所以占用内存更多,因此 ArrayMap
比 HashMap
占用更少的内存。
系统的限制
Activity
之间通过 Intent
传递数据,因为其内部使用了 Binder
通信机制。
由于系统的限制,Binder
事务缓冲区的大小限定在 1MB,这个大小是共享的,所以不能传递大数据,因此使用 Bundle
来传递数据,可以保证更快的速度和更少的内存占用。
Parcelable 比 Serializable 性能更好
HashMap
使用 Serializable
进行序列化,而 Bundle
则是使用 Parcelable
进行序列化。
Serializable
Serializable
是 Java 原生序列化的方式,主要通过 ObjectInputStream
和 ObjectOutputStream
来实现对象序列化和反序列化。
在整个过程中用到了大量的反射和临时变量,会频繁的触发 GC,序列化的性能会非常差。
序列化时需要手动指定 serialVersionUID
,用来辅助序列化和反序列化过程的,序列化与反序列化的 serialVersionUID
必须相同才能够使序列化操作成功,如果不显式声明 serialVersionUID
,类的任何细节改变,都可能改变默认的 serialVersionUID
这可能导致反序列化失败。
Parcelable
Parcelable
是为了解决了在 Android 中跨进程通信性能差的问题,Parcelable
写入和读取的时候都是采用自定义序列化存储的方式,不需要使用反射来推断它,因此 Parcelable
比 Serializable
要快很多。
虽然性能得到提升,但是使用起来比 Serializable
要复杂,因此系统封装了 Bundle
类,方便我们进行数据的传输。
如何选择 Parcelable 比 Serializable
Serializable
的设计初衷是为了序列化对象到本地文件、数据库、网络流、RMI 以便数据传输,当然这种传输可以是程序内的也可以是两个程序间的。
而 Android 的 Parcelable
的设计初衷是由于 Serializable
效率过低,消耗大,而 Android 中数据传递主要是在内存环境中(内存属于 Android 中的稀有资源),因此 Parcelable
的出现为了满足数据在内存中低开销而且高效地传递问题。
因此 Android 应用程序在内存间数据传输时推荐使用 Parcelable
。
如果数据持久化存储,推荐使用 Serializable
更加方便,因为 Parcelables
与数据存储一起使用是不安全的,因为数据格式可能会在 Android 版本之间改变。因此在序列化到存储设备或者网络传输方面还是尽量选择 Serializable
接口