界面间数据传输那些事儿

随着 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

相关推荐
雨白6 小时前
Jetpack系列(二):Lifecycle与LiveData结合,打造响应式UI
android·android jetpack
kk爱闹8 小时前
【挑战14天学完python和pytorch】- day01
android·pytorch·python
每次的天空10 小时前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频
恋猫de小郭10 小时前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
断剑重铸之日11 小时前
Android自定义相机开发(类似OCR扫描相机)
android
随心最为安11 小时前
Android Library Maven 发布完整流程指南
android
岁月玲珑11 小时前
【使用Android Studio调试手机app时候手机老掉线问题】
android·ide·android studio
还鮟15 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡17 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi0017 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体