flow<MyMessage> { emit(msg) } 可能只是flowOf(msg),但是将单个项目包装在 Flow 中很奇怪。如果您要对单个事物进行手动请求,则更适合使用返回该事物的挂起函数来处理。您可以使用 suspendCoroutine() 将异步回调代码转换为挂起函数,但 Firebase 已经提供了可以用来代替回调的挂起函数。如果您对随时间变化的数据进行重复请求,流将是合适的,但您需要通过使用callbackFlow 转换异步代码来将其做得更高。
在这种情况下,您似乎正在使用 FirebaseMessagingService,它是一个 Android 服务,它使用此 onMessageReceived 函数直接充当回调。
您可以做的(我之前没有尝试过)将本地 BroadcastReceiver 调整为您可以在应用程序的其他地方使用的 Flow。 FirebaseMessangingService 可以重新广播可以被此类 Flow 拾取的本地 Intent。因此,您可以使用这样的函数从本地广播中创建流。
fun localBroadcastFlow(context: Context, action: String) = callbackFlow {
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
intent.extras?.run(::trySend)
}
}
LocalBroadcastManager.getInstance(context).registerReceiver(receiver, IntentFilter(action))
awaitClose { LocalBroadcastManager.getInstance(context).unregisterReceiver(receiver) }
}
然后在您的服务中,您可以通过伴随对象公开流,映射到您的数据类类型。
class MyMessageService: FirebaseMessagingService() {
companion object {
private const val MESSAGE_ACTION = "mypackage.MyMessageService.MyMessage"
private const val DATA_KEY = "MyMessage key"
private val gson: Gson = TODO()
fun messages(context: Context): Flow<MyMessage> =
localBroadcastFlow(context, MESSAGE_ACTION)
.mapNotNull { bundle ->
val messageData = bundle.getString(DATA_KEY) ?: return@mapNotNull null
gson.fromJson(messageData, MyMessage::class.java)
}
}
override fun onMessageReceived(remoteMessage: RemoteMessage) {
val intent = Intent(MESSAGE_ACTION)
intent.putExtra(DATA_KEY, remoteMessage.data["data"])
LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent)
}
}
然后在您的 Fragment 或 Activity 中,您可以从MyMessageService.messages() 收集。
请注意,LocalBroadcastManager 最近已被弃用,因为它提倡将数据公开到应用的所有层的做法。我真的不明白为什么这应该被认为总是不好的。来自系统的任何广播对应用程序的所有层都是可见的。任何 http 地址对应用程序的所有层都是可见的,等等。他们建议公开可观察或 LiveData 作为替代方案,但这仍会将数据公开给应用程序的所有层。