除了 Bundle,还有哪些方式可以在 Activity 之间安全地传递复杂对象?

解读

国内面试场景里,这道题常被用来区分“只会写 startActivity(intent.putExtras(...))”的初级选手与真正理解 Android 跨进程/进程内数据安全机制的开发者。
面试官想听的不只是“能传”,而是“为什么安全、性能如何、是否触发 TransactionTooLarge、是否适配折叠屏多窗口、是否能在国内无 GMS 设备上跑”。
回答时要围绕“序列化成本、内存拷贝、版本兼容、数据一致性、用户隐私”五个维度展开,并给出线上验证过的落地方案。

知识点

  1. Parcelable 接口与 Parcel:内核层共享内存,性能优于 Serializable,但手写容易出错,可用 @Parcelize 或 Gradle 插件生成。
  2. AIDL + Binder:把复杂对象抽象成 AIDL 接口,通过 Service 返回 Binder,Activity 之间零拷贝拿到接口引用,适合超大对象或回调场景。
  3. SharedMemory(Android 8.1+ 公开 API):匿名共享内存,配合 Parcelable 文件描述符,跨进程传递 10 MB 以上 Bitmap 不触发内核拷贝,需自己处理同步锁。
  4. 全局单例 Repository + LiveData/Event:单进程内共享,不走 Binder,数据更新可观察;需加 WeakReference 防泄漏,且要在 onSaveInstanceState 里兜底。
  5. 本地持久化:Room/DataStore/MMKV,把对象序列化后存本地,目标 Activity 按主键读取;可防回收重启,但要注意加密(微信开源的 MMKV 支持 AES)。
  6. ContentProvider + FileProvider:把对象序列化为文件,通过 content:// URI 授权读权限,再配 FLAG_GRANT_READ_URI_PERMISSION;适配国内 64K 权限链限制,需主动 revokeUriPermission
  7. EventBus/Kotlin Flow 单进程总线:实质是内存地址传递,适合短时回调;缺点是无法跨进程、易被混淆工具裁剪,需加 @Keep
  8. 启动模式 + onNewIntent 复用:把对象缓存到 Application 层 LruCache<String, WeakReference<Object>>,用 UUID 当 key 随 Intent 传递;内存不足时自动回收,需兜底持久化。
  9. 安全合规:国内隐私监管要求敏感数据(身份证、定位)不得明文落地,需用 Keystore 加密后再传;跨进程场景必须配 SELinux 标签,防止第三方 App 通过 /proc/<pid>/fd 抓包。

答案

线上最常用、且经过国内各大厂验证的“安全三板斧”如下:

  1. 进程内:
    • 把复杂对象设计成 Parcelable,用 @Parcelize 一键生成,通过 Intent.setExtras 传递;超过 1 MB 时改用全局 ViewModel 或单例 Repository,以 LiveData 暴露,Activity 之间共享同一份内存,零序列化。
  2. 跨进程(如小程序插件、多用户分身):
    • 定义 AIDL 接口,让复杂对象实现 Parcelable 并写 writeToParcel,通过前台 Service 返回 Binder;Activity 绑定后直接拿到接口引用,不走 Intent,彻底绕过 Binder 1 MB 限制。
  3. 超大 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 崩溃。

拓展思考

  1. 折叠屏多窗口场景下,同一应用可能运行在两个进程,此时单例 Repository 失效,如何用 androidx.window 提供的 WindowInfoTracker 把数据路由到正确进程?
  2. 国内无 GMS 设备无法使用 BackupManager,如何自己实现一套加密备份通道,让用户在换机时也能把复杂对象恢复到新手机?
  3. Android 14 引入 Intent.setClipData 限制后台启动,如何把 ContentProvider URI 方案平滑迁移到 PhotoPicker 提供的 FLAG_GRANT_PERSISTABLE_URI_PERMISSION,同时兼容国内定制 ROM 的权限弹窗?