Intent传输数据的大小有限制吗?这个大小是所有应用共享的吗?为什么不能传递大数据?

647 阅读7分钟

Hi 大家好,我是 DHL,大厂程序员,公众号:ByteCode ,在美团、快手、小米工作过。搞过逆向,做过性能优化,研究过系统,擅长鸿蒙、Android、Kotlin、性能优化、职场分享。

微信小程序「猿面试」每日分享一道大厂面试题,涉及 JavaAndroid鸿蒙和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: 为什么不能传递大数据

  1. 性能影响:Intent 对象在 Android 系统中是通过 Binder 机制在进程间传递的。Binder 机制设计用于高效地处理进程间通信(IPC),但它并不适合传输大量数据。如果尝试通过 Intent 传递大量数据,那么这将显著增加 IPC 过程的开销,进而影响应用程序的性能。

  2. Binder 事务的限制:在 Android 系统中,Binder 事务有一个最大大小限制,通常是 1MB 左右。这意味着 Intent 及其所有附加数据的总大小不能超过这个限制。尝试传递超出这个大小限制的数据会导致运行时异常,例如 TransactionTooLargeException

  3. 内存使用和崩溃风险:当大量数据通过 Intent 传递时,这不仅增加了内存使用,还可能增加应用程序崩溃的风险。如果系统内存不足,尝试分配过多内存给 Intent 可能会导致应用程序或甚至整个系统的行为变得不稳定。

补充知识点

在上面提到的源码片段中 INT32_MAXmDataSize 分别表示什么?

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。

mDataSizeParcel 对象中的一个成员变量,它表示当前 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,包含多种解题思路、时间复杂度、空间复杂度分析,在线阅读