本文作者

作者:承香墨影

本文转载自承香墨影

以前也推送过一篇类似文章,可以一起阅读:

学Android 这么久,intent传递数据最大多少呢?


一、序

作为 Android 开发,日常 Coding 时,最频繁的操作应该就是操作 App 内的一系列 Activity。而在 Activity 间传递数据,就需要借助 Intent。

不少资料中写到,Intent 在 Activity 间传递基础类型数据或者可序列化的对象数据。但是 Intent 对数据大小是有限制的,当超过这个限制后,就会触发 TransactionTooLargeException 异常。

那么今天就来聊聊 Intent 传递大数据时,为什么会抛异常,以及如何解决它。

二、为什么会出现异常?

2.1 异常原因

Intent 传递大数据,会出现 TransactionTooLargeException 的场景,这本身也是一道面试题,经常在面试中被问到。


其实这个问题,如果遇到了,查查文档就知道了。

在 TransactionTooLargeException(https://developer.android.com/reference/android/os/TransactionTooLargeException.html) 的文档中,其实已经将触发原因详细说明了。

简单来说,Intent 传输数据的机制中,用到了 Binder。Intent 中的数据,会作为 Parcel 被存储在 Binder 的事务缓冲区(Binder transaction buffer)中的对象进行传输。

而这个 Binder 事务缓冲区具有一个有限的固定大小,当前为 1MB。你可别以为传递 1MB 以下的数据就安全了,这里的 1MB 空间并不是当前操作独享的,而是由当前进程所共享。也就是说 Intent 在 Activity 间传输数据,本身也不适合传递太大的数据。

2.2 Bundle 的锅?

这里再补充一些细节,Intent 使用 Bundle 存储数据,到底是值传递(深拷贝)还是引用传递?

Intent 传输的数据,都存放在一个 Bundle 类型的对象 mExtras 中,Bundle 要求所有存储的数据,都是可被序列化的。

在 Android 中,序列化数据需要实现 Serializable 或者 Parcelable。对于基础数据类型的包装类,本身就是实现了 Serializable,而我们自定义的对象,按需实现这两个序列化接口的其中一个即可。

那是不是只要通过 Bundle 传递数据,就会面临序列化的问题?

并不是,Activity 之间传递数据,首先要考虑跨进程的问题,而 Android 中又是通过 Binder 机制来解决跨进程通信的问题。涉及到跨进程,对于复杂数据就要涉及到序列化和反序列化的过程,这就注定是一次值传递(深拷贝)的过程。

这个问题用反证法也可以解释,如果是引用传递,那传递过去的只是对象的引用,指向了对象的存储地址,就只相当于一个 Int 的大小,也就根本不会出现 TransactionTooLargeException 异常。

传输数据序列化和 Bundle 没有关系,只与 Binder 的跨进程通信有关。

为什么要强调这个呢?

在 Android 中,使用 Bundle 传输数据,并非 Intent 独有的。例如使用弹窗时,DialogFragment 中也可以通过 setArguments(Bundle) 传递一个 Bundle 对象给对话框。

Fragment 本身是不涉及跨进程的,这里虽然使用了 Bundle 传输数据,但是并没有通过 Binder,也就是不存在序列化和反序列化。和 Fragment 数据传递相关的 Bundle,其实传递的是原对象的引用。

有兴趣可以做个试验,弹出 Dialog 时传递一个对象,Dialog 中修改数据后,在 Activity 中检查数据是否被修改了。

三、如何解决这个异常?

3.1 解决思路

知道异常的原因,就好解决了。

既然原因在于 Binder 传输限制了数据的大小,那我们不走 Binder 通信就好了。

可以从数据源上来考虑。

例如 Bitmap,本身就已经实现了 Parcelable 是可以支持序列化的。用 Intent 传输,稍微大一点的图一定会出现 TransactionTooLargeException。当然真是业务场景,肯定不存在传递 Bitmap 的情况。

那就先看看这个图片的数据源。Drawable?本地文件?线上图片?无论数据源在哪里,我们只需要传递一个 drawable_id、路径、URL,就可以还原这张图片,无需将这个 Bitmap 对象传递过去。大数据总有数据源,从数据源还原数据,对我们而言只是调用一个方法而已。

此前阿里发布的《Android 开发者手册》中,就提到了这个问题的解决建议。


阿里给出的方案,是通过 EventBus 来传递数据。

3.2 EventBus 的 粘性事件

很多商业项目其实都用到了 EventBus,这里就简单介绍如何使用 EventBus 的粘性事件来完成数据在 Activity 间的传递。

EventBus 是一个 Android 端优化的 Publish/subscribe 消息总线,简化了应用程序内各个组件间、组件与后台线程间的通信。

在 Activity 中使用 EventBus,需要根据 Activity 的生命周期,成对调用 register()unregister() 方法。普通的事件,只会发生在 register() 之后,在注册前发生的事件,统统都收不到。

这里利用的 EventBus 的粘性事件(Sticky Event)来实现,EventBus 内部维护了一个 Map 对象 stickyEvents,用于缓存粘性事件。

粘性事件使用 postSticky() 方法发送,它会将事件缓存到 stickyEvents 这个 Map 对象中,以待下次注册时,将这个事件取出,抛给注册的组件。以此来达到一个粘性的滞后事件发送和接收。

接下来我们看看 EventBus 粘性事件的使用细节。

1. 注解的区别

粘性事件的注解和普通事件的注解略有区别,需要添加 threadMode 和 sticky 参数。

@Subscribe(threadMode = ThreadMode.MAIN,sticky = true)public void onStickyEvent(MyStickyEvent event){    //...}

注意,这两个额外的参数是必须的。

2. 调用方法的区别

在发送消息的时候,需要使用 postSticky() 来替换掉 post() 方法。

需要注意的是,粘性事件是使用 Map 结构缓存的,并且是使用事件对象类型当 Key 进行缓存,所以对于同类型的数据,它只会缓存最后发送的数据。

3. 注意清理事件

前面也提到,粘性事件是存储在一个 Map 对象中的,它是不会主动清理其中存储的对象的,需要开发者手动清理。

EventBus 提供了两类方法 removeStickyEvent()removeAllStickyEvents() 方法,分别用来清理固定数据以及全部数据。

我们需要在合适的时机,手动的调用这两类方法,清理粘性事件。如果不对粘性事件进行清理,每次 register() 的时候,都会收到粘性事件。

4. EventBus 粘性事件的问题

粘性事件本身是脱离了 Android Intent 数据传递的这一套机制的,要知道 Activity 会在一些特殊情况下被销毁重建,在此情况下,通过 Intent 传递的数据,是可以继续从 Intent 中获取恢复到上一次页面传递的数据。

而通过 EventBus 的粘性事件,则可能在销毁重建时,造成数据丢失。

如果想要使用 EventBus 的粘性事件,来在页面间传递大数据,还是有不少细节,需要根据业务来调整的。

四、小结时刻

今天我们聊到了在 Activity 间,通过 Intent 传递大数据会触发 TransactionTooLargeException 异常的原因,以及如何解决它,最后再简单总结一下。

  1. Intent 无法传递大数据是因为其内部使用了 Binder 通信机制,Binder 事务缓冲区限制了传递数据的大小。

  2. Binder 事务缓冲区的大小限定在 1MB,但是这个尺寸是共享的,也就是并不是传递 1MB 以下的数据就绝对安全,要视当前的环境而定。

  3. 不要挑战 Intent 传递数据大小的极限,对于大数据,例如长字符串、Bitmap 等,不要考虑 Intent 传递数据的方案。

  4. 解决大数据传递问题,可以从数据源出发,根据数据的标识,还原数据,或者先持久化再还原。也可以使用 EventBus 的粘性事件来解决。

reference:

https://developer.android.com/reference/android/os/TransactionTooLargeException.html

https://www.wanandroid.com/blogimgs/a2609aed-1000-4039-93c3-7541aaa2013b.pdf

推荐阅读:

玩Android 功能实现大揭秘!滴滴的开源的Booster分析 是如何修复系统bug的?关于MVC/MVP/MVVM的一些错误认识

扫一扫 关注我的公众号

如果你想要跟大家分享你的文章,欢迎投稿~

┏(^0^)┛明天见!

android windowmanager 无法传递事件_面试常客:Intent 能传递多大 Size 的数据?| 附阿里的建议方案!...相关推荐

  1. 分式求二阶导数_近10年高考数学“导数大题”分析,附2021备考建议

    今天小马给大家整理了近10年高考数学"导数大题"分析,以及2021高考备考建议 相关推荐(点击跳转)???▶语文干货 | 高中必修1-5古代文化常识大汇总!▶高中数学 | 知识点结 ...

  2. intent传递较大数据的解决和intent不能传递较大数据的原因

    一.首先提供需要在activity之间用intent传递较大数据的方法.intent不能传递较大数据,需要传递数据的时候,写一个操作类: public class DataHelper {privat ...

  3. android checkbox 选中事件_挖穿Android第四十九天

    设置向导 完成第一个向导页面Setup1Activity的布局文件 - style样式介绍 - 用到的系统图片 android:drawableLeft="@android:drawable ...

  4. c++ 不插入重复元素但也不排序_面试官爱问的 10 大经典排序算法,20+ 张图来搞定...

    (给算法爱好者加星标,修炼编程内功) 作者:技术让梦想更伟大 / 李肖遥 (本文来自作者投稿) 冒泡排序 简介 冒泡排序是因为越小的元素会经由交换以升序或降序的方式慢慢浮到数列的顶端,就如同碳酸饮料中 ...

  5. 实现时间排序_面试官:手撕十大排序算法,你会几种?

    推荐阅读: 去面试大厂被 Kafka 虐了,后悔没有早点看到这份Kafka手写笔记 面试阿里,京东,百度,快手归来,三年Java开发总结了这些经验 阿里,字节,腾讯,面试题都涵盖了,这一份Java面试 ...

  6. Android中的两个Activity用Intent来传递java bean实体

    先定义java bean实体: public class MC_bean implements Serializable {private int code;private String messag ...

  7. android 手环获取步数_荣耀手环5 篮球版深度体验:专业数据精细到“毛孔”

    9月4日,荣耀手环5 篮球版在武汉正式发布.头顶"麦迪同款"的光环,加上四年一届的篮球世界杯正在国内举办,引起一波篮球风.相信很多人都会好奇,"篮球版"又有怎样 ...

  8. xml中else if写法_面试官:优化代码中大量的if/else,你有什么方案?

    一个快速迭代的项目,时间久了之后,代码中可能会充斥着大量的if/else,嵌套6.7层,一个函数几百行,简!直!看!死!人! 这个无限循环嵌套,只是总循环的一部分...我已经绕晕在黄桷湾立交 仔细数了 ...

  9. js大屏导出图片_整理了30个实用可视化大屏模板,附源文件+工具

    最近新接手了一个大屏项目,从前期的调研需求到原型设计再到模型开发,前前后后折腾了大半个月,这个过程中也踩了不少坑,深感大屏项目开发的不易(领导要求实在是太高),也借此把我之前收集的可视化大屏模板分享给 ...

最新文章

  1. 俄罗斯将用机器人当探月先锋
  2. mybatis入门-第一个程序
  3. linux内核参数优化 for 高并发服务器
  4. 计算机应用基础20级月考,中职计算机应用基础月考试题
  5. linux命令的导入,[导入]Linux基本命令
  6. 哈夫曼编码压缩率计算_程序员的算法课(8)-贪心算法:理解霍夫曼编码
  7. 我的vscode配置 利用Settings Sync一键安装
  8. mysql 非最佳查询_Mysql 查询优化
  9. [2013.8.16]小议innerText/HTML以及outerText/HTML
  10. mysql group by cube_SQL Server 之 GROUP BY、GROUPING SETS、ROLLUP、CUBE
  11. python FIFO命名管道
  12. MySqlException: The user specified as a definer ('root'@'%') does not exist解决方法
  13. 达梦数据库查看某个表的字段类型、常用数据库驱动类名以及URL
  14. 谈谈R中的乱码(二)
  15. 第7章 特种文献检索
  16. 群晖服务器白群晖有哪些型号,白群晖和黑群晖,有什么区别?
  17. 五星大饭店续集剧情大放送(最新更新)
  18. 数据库密码忘了如何修改
  19. igraph java_igraph从邻接列表生成邻接矩阵
  20. (附源码)计算机毕业设计SSM装修信息分享管理系统

热门文章

  1. 机器翻译先驱 Jaime Carbonell 去世,创立卡内基梅隆大学语言技术学院
  2. 华为百度美团驰援抗击疫情;自由软件基金会建议开源 Windows 7;印度超越美国成第二大智能手机市场 | 极客头条...
  3. 十年后,每天 24 小时离不开传感技术
  4. 华为电脑终于又能搭载正版 Windows 系统了!
  5. Python 分析国庆热门旅游景点,告诉你哪些地方好玩、便宜、人又少!
  6. 平均年薪 70 万!刚刚,这类程序员又涨薪了!佩服!
  7. Oracle 扼杀 Java EE!
  8. 阿里云泄露 40 家名企源代码!
  9. IT 从业者要如何在国企「活」下去?
  10. 微软 VS Code 或将取代 Visual Studio!