Hi 大家好,我是 DHL,大厂程序员,公众号:ByteCode ,在美团、快手、小米工作过。搞过逆向,做过性能优化,研究过系统,擅长鸿蒙、Android、Kotlin、性能优化、职场分享。
微信小程序「猿面试」每日分享一道大厂面试题,涉及 Java
、Android
、鸿蒙和ArkTS
、设计模式
、算法和数据结构
等内容。
Q: Intent 传输数据的大小有限制吗
Intent
传输数据的大小是有限制的,在 Android 中,Intent
用于在不同组件间传递数据,而传输数据的大小是有限制的,但是这个限制并没有一个明确的数字标准,因为这个限制是由 Binder 事务缓冲区的大小决定的。
通常情况下,Binder 事务缓冲区的大小限制在 1MB 左右,如果尝试传递超过限制的数据,会导致 TransactionTooLargeException
异常。但是传输数据大小可能会因为不同的设备、Android 版本和厂商定制的 ROM 而有所不同。
我们一起来看一下源码,抛出 TransactionTooLargeException
异常的地方。
frameworks/base/core/java/android/os/TransactionTooLargeException.java
/**
* The Binder transaction failed because it was too large.
*
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public class TransactionTooLargeException extends RemoteException {
// ...
}
这个异常类 TransactionTooLargeException
是在 Binder 事务过大时抛出的。具体的检查逻辑在 C++实现中,代码位于 frameworks/native/libs/binder/Parcel.cpp
文件中:
status_t Parcel::write(const void* data, size_t len) {
// ...
if ((len > INT32_MAX) || ((INT32_MAX - len) < mDataSize)) {
return NO_MEMORY;
}
// ...
}
在这段代码中,如果写入的数据长度超过了 INT32_MAX
或者超出了当前 Parcel 的剩余空间,就会返回 NO_MEMORY
错误,这是因为 Binder 事务的大小超出了系统允许的范围。
尽管源码中没有明确指出 1MB 的限制,但是在实际开发中,通常建议 Intent
携带的数据在 1MB 以内,以避免 TransactionTooLargeException
异常的发生。
Q:这个大小是所有应用共享的吗
是的,Binder 事务缓冲区的大小是由系统层面上的 Binder 驱动管理的,这个缓冲区是所有应用共享的。这意味着所有的应用在进行 Binder 通信时,都会使用这个共享的缓冲区来传输数据。
当系统中的任何应用或服务尝试执行一个 Binder 事务时,它都必须在这个共享的缓冲区中分配空间来传输数据。如果事务的数据量太大,超过了 Binder 缓冲区的限制,或者缓冲区已经被其他事务占用了大部分空间,那么就可能抛出 TransactionTooLargeException
异常。
系统会尝试管理这个共享缓冲区的使用,以确保所有的应用都能公平地使用这个资源。然而,在资源紧张的情况下,比如当有多个大型事务同时发生时,还是可能会遇到这个限制。
为了避免这个问题,开发者应该尽量减少通过 Intent
传输的数据量,并且考虑使用其他的数据传输机制,比如使用文件、数据库、ContentProvider 或者网络等方式来传输大量数据。
Q: 为什么不能传递大数据
-
性能影响:Intent 对象在 Android 系统中是通过 Binder 机制在进程间传递的。Binder 机制设计用于高效地处理进程间通信(IPC),但它并不适合传输大量数据。如果尝试通过 Intent 传递大量数据,那么这将显著增加 IPC 过程的开销,进而影响应用程序的性能。
-
Binder 事务的限制:在 Android 系统中,Binder 事务有一个最大大小限制,通常是 1MB 左右。这意味着 Intent 及其所有附加数据的总大小不能超过这个限制。尝试传递超出这个大小限制的数据会导致运行时异常,例如
TransactionTooLargeException
。 -
内存使用和崩溃风险:当大量数据通过 Intent 传递时,这不仅增加了内存使用,还可能增加应用程序崩溃的风险。如果系统内存不足,尝试分配过多内存给 Intent 可能会导致应用程序或甚至整个系统的行为变得不稳定。
补充知识点
在上面提到的源码片段中 INT32_MAX
和 mDataSize
分别表示什么?
if ((len > INT32_MAX) || ((INT32_MAX - len) < mDataSize)) {
return NO_MEMORY;
}
INT32_MAX
是一个在 C/C++ 中定义的常量,代表了 32 位整型能够表示的最大值。对于一个有符号的 32 位整数(int32_t
),这个值通常是 (2^{31} - 1),也就是 2, 147, 483, 647。
而 mDataSize
是 Parcel
对象中的一个成员变量,它表示当前 Parcel
中已经存储的数据量大小(以字节为单位)。Parcel
是 Android 中用于封装要通过 Binder 机制传输的数据的一个类。mDataSize
的值会随着向 Parcel
中写入数据而增加。
而这段代码的目的是为了确保在向 Parcel
中写入新的数据时不会溢出。如果要写入的数据长度 len
大于 INT32_MAX
,或者 INT32_MAX
减去 len
后的值小于当前 Parcel
的数据大小 mDataSize
,这意味着新的数据总量将超过 int32_t
能表示的最大值,这会导致溢出,因此函数会返回 NO_MEMORY
错误。
实际上,Binder 传输数据的大小限制并不是 INT32_MAX
,而是由 Binder 驱动的缓冲区大小决定的,这个缓冲区大小通常远小于 INT32_MAX
。
在实际应用中,这个大小通常在 1MB 左右,但这个值可能因设备和系统版本而异,并且可能受到系统内存压力和当前 Binder 缓冲区使用情况的影响。
全文到这里就结束了,感谢你的阅读,如果文章对你有帮助,欢迎在看、点赞、分享给身边的小伙伴,你的点赞是我持续更新的动力。
更多大厂面试题,欢迎前往微信搜索小程序「猿面试」查看。微信小程序(猿面试)包含了 Java
、Android、鸿蒙和ArkTS
、设计模式
、算法和数据结构
相关内容,
推荐阅读:
开源新项目
-
云同步编译工具(SyncKit),本地写代码,远程编译,欢迎前去查看 SyncKit
-
KtKit 小巧而实用,用 Kotlin 语言编写的工具库,欢迎前去查看 KtKit
-
最全、最新的 AndroidX Jetpack 相关组件的实战项目以及相关组件原理分析文章,正在逐渐增加 Jetpack 新成员,仓库持续更新,欢迎前去查看 AndroidX-Jetpack-Practice
-
LeetCode / 剑指 offer,包含多种解题思路、时间复杂度、空间复杂度分析,在线阅读