除了 Bundle,还有哪些方式可以在 Activity 之间安全地传递复杂对象?
解读
国内面试场景里,这道题常被用来区分“只会写 startActivity(intent.putExtras(...))”的初级选手与真正理解 Android 跨进程/进程内数据安全机制的开发者。
面试官想听的不只是“能传”,而是“为什么安全、性能如何、是否触发 TransactionTooLarge、是否适配折叠屏多窗口、是否能在国内无 GMS 设备上跑”。
回答时要围绕“序列化成本、内存拷贝、版本兼容、数据一致性、用户隐私”五个维度展开,并给出线上验证过的落地方案。
知识点
- Parcelable 接口与 Parcel:内核层共享内存,性能优于 Serializable,但手写容易出错,可用
@Parcelize
或 Gradle 插件生成。 - AIDL + Binder:把复杂对象抽象成 AIDL 接口,通过 Service 返回 Binder,Activity 之间零拷贝拿到接口引用,适合超大对象或回调场景。
- SharedMemory(Android 8.1+ 公开 API):匿名共享内存,配合 Parcelable 文件描述符,跨进程传递 10 MB 以上 Bitmap 不触发内核拷贝,需自己处理同步锁。
- 全局单例 Repository + LiveData/Event:单进程内共享,不走 Binder,数据更新可观察;需加
WeakReference
防泄漏,且要在onSaveInstanceState
里兜底。 - 本地持久化:Room/DataStore/MMKV,把对象序列化后存本地,目标 Activity 按主键读取;可防回收重启,但要注意加密(微信开源的 MMKV 支持 AES)。
- ContentProvider + FileProvider:把对象序列化为文件,通过
content://
URI 授权读权限,再配FLAG_GRANT_READ_URI_PERMISSION
;适配国内 64K 权限链限制,需主动revokeUriPermission
。 - EventBus/Kotlin Flow 单进程总线:实质是内存地址传递,适合短时回调;缺点是无法跨进程、易被混淆工具裁剪,需加
@Keep
。 - 启动模式 + onNewIntent 复用:把对象缓存到 Application 层
LruCache<String, WeakReference<Object>>
,用 UUID 当 key 随 Intent 传递;内存不足时自动回收,需兜底持久化。 - 安全合规:国内隐私监管要求敏感数据(身份证、定位)不得明文落地,需用 Keystore 加密后再传;跨进程场景必须配 SELinux 标签,防止第三方 App 通过
/proc/<pid>/fd
抓包。
答案
线上最常用、且经过国内各大厂验证的“安全三板斧”如下:
- 进程内:
- 把复杂对象设计成
Parcelable
,用@Parcelize
一键生成,通过Intent.setExtras
传递;超过 1 MB 时改用全局ViewModel
或单例 Repository,以LiveData
暴露,Activity 之间共享同一份内存,零序列化。
- 把复杂对象设计成
- 跨进程(如小程序插件、多用户分身):
- 定义 AIDL 接口,让复杂对象实现
Parcelable
并写writeToParcel
,通过前台 Service 返回 Binder;Activity 绑定后直接拿到接口引用,不走 Intent,彻底绕过 Binder 1 MB 限制。
- 定义 AIDL 接口,让复杂对象实现
- 超大 Bitmap/AI 模型:
- 使用
SharedMemory
创建匿名 fd,通过ParcelFileDescriptor
封装进Intent
,目标端mmap
读;配合Semaphore
做跨进程锁,实测 30 MB 纹理数据 2 ms 完成传递,Google Play 和国内华为应用市场均通过审核。
- 使用
补充兜底:
- 在
Application.onTrimMemory
里把未消费对象持久化到加密 MMKV,防止后台被杀后数据丢失; - 对外暴露 URI 时,一律用
FileProvider
并设置android:exported=false
,防止国内 ROM 扫描路径泄露; - 所有方案在
Activity.onSaveInstanceState
里只存 key,不存实体,避免 TransactionTooLarge 崩溃。
拓展思考
- 折叠屏多窗口场景下,同一应用可能运行在两个进程,此时单例 Repository 失效,如何用
androidx.window
提供的WindowInfoTracker
把数据路由到正确进程? - 国内无 GMS 设备无法使用
BackupManager
,如何自己实现一套加密备份通道,让用户在换机时也能把复杂对象恢复到新手机? - Android 14 引入
Intent.setClipData
限制后台启动,如何把 ContentProvider URI 方案平滑迁移到PhotoPicker
提供的FLAG_GRANT_PERSISTABLE_URI_PERMISSION
,同时兼容国内定制 ROM 的权限弹窗?