使用 Kotlin Coroutines 替换 LocalBroadcastManager 进行 Firebase 消息传递

Posted

技术标签:

【中文标题】使用 Kotlin Coroutines 替换 LocalBroadcastManager 进行 Firebase 消息传递【英文标题】:Using Kotlin Coroutines to replace LocalBroadcastManager for Firebase messaging 【发布时间】:2020-01-14 23:06:03 【问题描述】:

android 上使用 Firebase Cloud Messaging 时,通常需要通知当前的 Activity 传入推送通知。推荐的方法之一是使用LocalBroadcastManagerIntentFirebaseMessagingService 实现发送到Activity (*** example answer)。

但是,从版本 1.1.0-alpha01 (2018-12-17) 开始,LocalBroadcastManager is deprecated:

LocalBroadcastManager 是一个应用程序范围的事件总线,并在您的应用程序中包含层违规:任何组件都可以侦听来自其他任何组件的事件。您可以将 LocalBroadcastManager 的使用替换为可观察模式的其他实现,具体取决于您的用例,合适的选项可能是 LiveData 或反应流。

虽然这个类很可能会在一段时间内保持可用,但我还是想开始清理我们的应用程序,所以我想在 Google 真正删除旧方法之前迁移到更好的东西。

目前,这些本地广播在我们的应用中具有两个主要作用:

    使用来自推送通知的新数据更新 UI。其工作方式是,每个关心传入推送数据的Activity 都有一个广播接收器,该接收器侦听适当的消息并更新其自己的视图数据。 如果服务器发送通知以结束会话,则强制用户注销。这适用于每个具有广播接收器实例的活动,该实例侦听注销事件、结束活动并启动登录活动。

在我看来,这些用例的两个建议的替代方案都存在问题:

LiveData 最容易在ActivityFragment 中作为ViewModel 的一部分使用。但是,ViewModel 仅用于那些直接处理 UI 的类。从FirebaseMessagingService 中访问ViewModel 是一个丑陋的黑客,从架构的角度来看是一个非常糟糕的主意。此外,不同的活动和片段有不同的ViewModel 对象,我不希望服务需要全部访问它们。 我可以用一堆LiveData 属性创建一个Kotlin object(又名Singleton),让FirebaseMessagingService 从传入的消息中更新那些LiveData 对象,让Activity 观察这些变化并将它们复制到它自己的ViewModelLiveData 属性中。这样做的问题是双重的:首先,它要求我为每条数据有两个相同的LiveData 对象,一个在ViewModel 中,一个在object 中;其次,它对处理“注销事件”没有帮助,因为LiveData 旨在处理不断变化的数据,而不是监听事件流。 (我或许可以使用 LiveData Event Wrapper 处理第二个问题,但这仍然感觉像是对不应该以这种方式工作的东西的糟糕破解。) 虽然 RxJava 等反应式流可能会满足我的需求,但在过去几个月里,我已经强迫我的团队学习 Kotlin、Android 数据绑定、Android ViewModel 和一堆其他新东西,但我没有不认为他们可以采取更多。 RxJava 也是为了这个用途而添加的一个大东西,我们没有计划重写整个应用程序以利用它来证明它的添加是合理的。

我发现的一个建议是将 Kotlin 协程与 Channels 或 Flows 一起使用。这些可以与反应流非常相似,但(与 RxJava 不同)旨在与 Kotlin 一起使用,并从 Kotlin 对 Java 的改进中受益。既然 Google 宣布他们将重点放在 Kotlin 进行 Android 开发而不是 Java 开发,这个选项就特别有吸引力。

虽然在我看来这似乎是最好的选择,但我没有设法从其他人那里找到任何关于它是否有效以及这种实现是否存在副作用和/或陷阱的反馈。我发现的唯一内容是kotlinx.coroutines 存储库上的open issue,它说明需要提供这样的应用程序示例。虽然我很乐意提供这样的示例,但我认为我对它的了解不足以创建一个好的示例,并且我不希望我的生产应用程序成为豚鼠。我也不知道在这种情况下使用显式 couroutines 和 Channel 还是使用 suspendFlow 是否更好(或更合适)。

总结:

Kotlin 协程及其关联的并发结构是处理 Android ServiceActivity 之间通信的好方法吗? 如果是,哪种 Kotlin 类型更适合使用,ChannelFlow

【问题讨论】:

有一个名为 EventBus 的库。看看。我个人从未使用过它,但我认为它可以满足您的要求。它也很小,不要使用BroadcastReceiver @ManoharReddy 我不想使用 EventBus,因为首先我想看看现有的语言功能是否能满足我的需求。这就是这个问题的重点。 @MosheKatz 您决定采用什么解决方案?与频道有关的东西?你能发布你的想法吗,我也有类似的问题 @11m0 我还没有时间回到这个话题。当我这样做时,我会发布答案。 @xuiqzy 我还没有时间解决这个问题,因为旧方法仍然有效。当我可以这样做时,我会发布我自己的答案。使用此问题上的“关注”按钮可收到后续帖子的通知。 【参考方案1】:

协程并不能真正帮助将数据从一个软件组件传递到另一个软件组件。它们使用看起来好像它们是同步的语法来帮助处理多个异步工作单元。这是协程的底线。它们类似于 javascript 中的 async/await 语法。虽然您可能会使用协程来访问来自异步源的数据,但它并没有为您提供将数据代理到其他组件的任何原语。

LiveData 可能适用于您正在尝试做的事情。不要将 ViewModel 与 LiveData 混为一谈——它们解决不同的问题。虽然您认为 ViewModel 只能由处理 UI 的代码访问是正确的,但该准则并未扩展到 LiveData。公开一个反映来自 FirebaseMessagingService 的当前数据的 LiveData 是完全合理的,这些数据后来被 ViewModel 拾取、转换并传递给视图。此 LiveData 可以是单例,也可以通过您选择的任何依赖注入基础架构获得。

请记住,LiveData 实际上只应该用于管理状态更改。它不是您的应用可以收听的数据“流”。你需要确保你的基础设施是基于状态的,这样才能很好地工作。 FCM 本身不是基于状态的,但如果您希望视图响应来自 FCM 的消息,则需要在每条消息之间保留足够的上下文,以确保您的 UI 始终如一地响应新消息(或完全没有消息) .

【讨论】:

谢谢。我知道协程本身无助于数据的传递。这个问题应该更多地集中在协同程序周围的FlowChannel 构造上。您在第三段中提出的问题正是我提出这个问题的原因——我只是认为LiveData 不适用于这个用例。就ViewModelLiveData 而言,我提出ViewModel 的唯一原因是解释我如何需要一打重复的LiveData 对象——一组在FCM 和Activity 之间共享,另一组在ViewModel 自己。

以上是关于使用 Kotlin Coroutines 替换 LocalBroadcastManager 进行 Firebase 消息传递的主要内容,如果未能解决你的问题,请参考以下文章

模拟返回 Kotlin Coroutines Deferred 类型的方法的返回值

Kotlin Coroutines 协程实现原理全解析

Kotlin Coroutines 的现有 3 函数回调

Kotlin Coroutines 选择 Dispatcher

使用 Kotlin Coroutines 更新我的 TextView 会导致它崩溃:

Kotlin Coroutines:等待多个线程完成