界面间数据传输那些事儿

随着 UI 架构的变迁和技术的迭代,界面间数据传递的方式也变得多种多样了。没有最好的方式,只有当前适合业务的方式。今天我们就来看看有哪些数据传递的方式,我们又该如何抉择?

在最开始的时候,界面只是使用 startActivityForResult 来进行页面间的数据传递,后面 Fragment 诞生,官方也提供了 FragmentManager.setFragmentResultListener 之类的方式,但 Jetpack Compose 之后,官方就完全弱化了这方面的能力。

在探索具体的技术细节前,我们要先看看我们数据需要传输的场景有哪些:

  1. 启动新界面是为了获取某些数据,然后返回给当前界面用,例如照片选择器。
  2. 几个界面是为了配合完成一件事情,每个界面负责一部分流程,例如注册流程。
  3. 界面的某些改动,需要通知到其它界面,例如用户关注的变更,一旦变更,则期望其它界面也能够同步更新。

大多数情况下,我们所说的数据传输都是针对第一种情况而言。最基础的就是 startActivityForResult。当然,现在应该没人直接用这个了,都是用官方封装的 LauncherForActivityResult 了:

kotlin 复制代码
val launcher = rememberLauncherForActivityResult(contract = ActivityResultContracts.GetContent(), onResult = {uri ->
    // ...
})

如果使用的是 Fragment, 以前可以使用非常不好用的 setTargetFragment, 现在则可以使用 FragmentManager.setFragmentResultListener,与 Activity 的使用是大同小异。

而如果是使用 Jetpack Compose Navigation, 那不好意思,目前还非常不成熟,一个可用的方式是使用 previousBackStackEntry:

kotlin 复制代码
// 新界面使用 previousBackStackEntry 获取到前一个界面的数据存储,然后往里面设置数据
navController.previousBackStackEntry
    ?.savedStateHandle
    ?.set("your_key", "your_value")
navController.popBackStack()

// 返回到原本的界面后,通过 currentBackStackEntry 去读取数据存储。
val savedStateHandle = navController.currentBackStackEntry?.savedStateHandle

虽然我们也可以参照 Fragment 的实现用 ActivityViewModel 构建一套 ComposeResultListener 之类的实现。但估计随着版本的迭代和群众的呼声,官方也会出品接口更友好的方式。而如果我们自己实现的话,就可以在官方出品后对比下,看看差距,才知道怎么写出更好的代码。

但需要记住的是,这二者都是不能跨越 Activity 而通信的。如果是多 ActivityPage 架构,则需要注意这种情况。我觉得,这种界面一般是通用型的组件界面,所以一般使用 Activity 作为载体就好。

如果是几个界面配合完成某件事情,需要相互共享一些数据,那就可以把这几个界面包裹在同一个 Activity 里,使用 ActivityViewModel 作为数据的载体,每个界面监听 ActivityViewModel 的数据,那么就是完美的数据驱动 UI 了。

emophoto 库中,照片选择器有 grid 界面、preview 界面、edit 界面,其三者都收归在 PhotoPickerActivity, 共享 PhotoPickerViewModel 来进行数据管理, 最后再通过 ActivityResult 交给外部调用者,这也是第一种方式与第二种方式的结合。

而业务上使用最常见的方式应该是第三种了,就是我增删改查了些数据,希望其它界面也能够立刻同步到,不要出现各个界面不对齐的情况。ChannelStateFlowSharedFlow 等数据流工具都可以助力我们数据驱动 UI,当然,最方便的形式就是使用 emo 封装的 EmoBus

kotlin 复制代码
// 定义数据变更事件
data class AddCommentSuccessEvent(val thinkId: Long, val id: Long, val draftId: Long)

// A 界面发送事件
EmoBus.default.emit(AddCommentSuccessEvent(content.itemId, id, draft.id))

// B 界面监听事件
EmoBus.default.flowOf<AddCommentSuccessEvent>().collect {
    // ...
}

基本上我们可以用于页面间数据传递与使用的就这些形式了,我们只需要在需要的场景选用恰当的方式就可以了。


我是古哥E下,前微信读书客户端程序猿 / 自学 5 年中医,维护过上万 Star 开源项目 QMUI Android,现独立维护好用简洁的 Android 组件库 emo

关注我可得:ChatGPT 开发玩法 | 程序员学习经验 | 组件库新变动 | 中医健康调理 。

emo官网:emo.qhplus.cn

相关推荐
weixin_4493108424 分钟前
高效集成:聚水潭采购数据同步到MySQL
android·数据库·mysql
Zender Han39 分钟前
Flutter自定义矩形进度条实现详解
android·flutter·ios
白乐天_n3 小时前
adb:Android调试桥
android·adb
姑苏风7 小时前
《Kotlin实战》-附录
android·开发语言·kotlin
数据猎手小k10 小时前
AndroidLab:一个系统化的Android代理框架,包含操作环境和可复现的基准测试,支持大型语言模型和多模态模型。
android·人工智能·机器学习·语言模型
你的小1010 小时前
JavaWeb项目-----博客系统
android
风和先行11 小时前
adb 命令查看设备存储占用情况
android·adb
AaVictory.12 小时前
Android 开发 Java中 list实现 按照时间格式 yyyy-MM-dd HH:mm 顺序
android·java·list
似霰13 小时前
安卓智能指针sp、wp、RefBase浅析
android·c++·binder
大风起兮云飞扬丶13 小时前
Android——网络请求
android