随着 UI
架构的变迁和技术的迭代,界面间数据传递的方式也变得多种多样了。没有最好的方式,只有当前适合业务的方式。今天我们就来看看有哪些数据传递的方式,我们又该如何抉择?
在最开始的时候,界面只是使用 startActivityForResult
来进行页面间的数据传递,后面 Fragment
诞生,官方也提供了 FragmentManager.setFragmentResultListener
之类的方式,但 Jetpack Compose
之后,官方就完全弱化了这方面的能力。
在探索具体的技术细节前,我们要先看看我们数据需要传输的场景有哪些:
- 启动新界面是为了获取某些数据,然后返回给当前界面用,例如照片选择器。
- 几个界面是为了配合完成一件事情,每个界面负责一部分流程,例如注册流程。
- 界面的某些改动,需要通知到其它界面,例如用户关注的变更,一旦变更,则期望其它界面也能够同步更新。
大多数情况下,我们所说的数据传输都是针对第一种情况而言。最基础的就是 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
而通信的。如果是多 Activity
多 Page
架构,则需要注意这种情况。我觉得,这种界面一般是通用型的组件界面,所以一般使用 Activity
作为载体就好。
如果是几个界面配合完成某件事情,需要相互共享一些数据,那就可以把这几个界面包裹在同一个 Activity
里,使用 ActivityViewModel
作为数据的载体,每个界面监听 ActivityViewModel
的数据,那么就是完美的数据驱动 UI
了。
在 emo
的 photo
库中,照片选择器有 grid
界面、preview
界面、edit
界面,其三者都收归在 PhotoPickerActivity
, 共享 PhotoPickerViewModel
来进行数据管理, 最后再通过 ActivityResult
交给外部调用者,这也是第一种方式与第二种方式的结合。
而业务上使用最常见的方式应该是第三种了,就是我增删改查了些数据,希望其它界面也能够立刻同步到,不要出现各个界面不对齐的情况。Channel
、StateFlow
、SharedFlow
等数据流工具都可以助力我们数据驱动 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