When using Firebase Cloud Messaging on Android, it is often desirable to notify the current
Activity of an incoming push notification. One of the recommended ways to do this has been to use
LocalBroadcastManager to send an
Intent from the
FirebaseMessagingService implementation to the
Activity (StackOverflow example answer).
However, as of version 1.1.0-alpha01 (2018-12-17),
LocalBroadcastManager is deprecated:
LocalBroadcastManager is an application-wide event bus and embraces layer violations in your app: any component may listen events from any other. You can replace usage of
LocalBroadcastManagerwith other implementation of observable pattern, depending on your usecase suitable options may be LiveData or reactive streams.
While it is highly likely that this class will remain available for a while longer, I would like to start cleaning up our applications anyway, so I want to migrate to something better before Google actually removes the old way.
Right now, there are two main roles that these local broadcasts have in our apps:
- Update the UI with the new data from the push notification. The way this worked was that each
Activitythat cares about the incoming push data has a broadcast receiver that listens for the appropriate message and updates its own view data.
- Force the user to log out if the server sends a notification to end the session. This works with each activity having an instance of a broadcast receiver that listens for a logout event, ends the Activity, and starts the Login Activity.
As I see it, these use-cases have issues with both of their suggested alternatives:
LiveDatais easiest to use in an
Fragmentas part of a
ViewModelis only meant to be used from those classes that directly deal with the UI. Accessing the
ViewModelfrom within the
FirebaseMessagingServicetakes an ugly hack and is a really bad idea from an architectural perspective. Also, different activities and fragments have different
ViewModelobjects, and I don’t want the service to need to access them all.
- I can create a Kotlin
object(a.k.a. Singleton) with a bunch of
LiveDataproperties, have the
LiveDataobjects from the incoming messages, and have the
Activityobserve those changes and copy them into its own
LiveDataproperties. The problem with that is twofold: first, it requires me to have two identical
LiveDataobjects for each piece of data, one in the
ViewModeland one in the
object; and second, it doesn’t help me with handling the “log out event”, because
LiveDatais meant to handle changing data, not listening to a stream of events. (I may be able to handle the second issue using this
LiveDataEvent Wrapper, but that still feels like a bad hack over something that isn’t meant to work this way.)
- While reactive streams, such as RxJava, will probably do what I need, I already forced my team to learn Kotlin, Android Databinding, Android ViewModel, and a bunch of other new stuff in the last few months, and I don’t think they can take much more. RxJava is also a large thing to add for just this one use, and we have no plans to rewrite the entire application to take advantage of it in order to justify its addition.
One suggestion I found was to use Kotlin Coroutines with
Flows. These can be used very similar to reactive streams, but (unlike RxJava) are intended to be used with Kotlin and benefit from Kotlin’s improvements over Java. This option is especially attractive now that Google has announced that they are focusing on Kotlin for Android development instead of Java.
While this seems to me to be the best option, I have not managed to find any feedback from others about whether it works and if there are side-effects and/or pitfalls to such an implementation. The only thing I found was an open issue on the
kotlinx.coroutines repository about the need for providing an example of an application like this. While I’d love to contribute such an example, I don’t think I know enough about it to create a good example, and I don’t want my production apps to be the guinea pig. I also don’t know whether it is better (or proper) to use explicit couroutines with
Channel or to use
Flow for this case.
- Are Kotlin Coroutines and their associated concurrency structures a good way to handle communication between Android
- If yes, which Kotlin type makes more sense to use,
LiveData probably works just fine for what you’re trying to do. Don’t conflate ViewModel with LiveData – they solve different problems. While you’re correct that ViewModel should only be accessed by code that deals with UI, that guideline doesn’t extend to LiveData. It’s perfectly reasonable to expose a LiveData that reflects current data from FirebaseMessagingService that is later picked up by a ViewModel, transformed, and passed on to a view. This LiveData could be a singleton, or obtained via whatever dependency injection infrastructure you choose.
Bear in mind that LiveData is really only supposed to be used for managing changes in state. It’s not a “stream” of data that your app can listen to. You will need to make sure that your infrastructure is state-based in order for this to work out well. FCM itself is not state-based, but if you want your views to respond to messages from FCM, you’ll need to retain enough context between each message to make sure your UI responds consistently to new messages (or the lack of messages altogether).