I have many Activities which raise background tasks; the Activities will pass themselves in as having implemented a listener callback, so that the background tasks can raise an event on the Activities. The Activities in turn can show something on the UI to indicate that a background activity passed or failed.
Alternatively, I could use an EventBus, wherein I get the Activity to register itself as a listener/subscriber. I can have a background tasks raise an event on the EventBus and the Activity listening to it can handle it.
What are the advantages of one over the other? When would you use one over the other? (Code cleanliness? Performance? Caveats?)
Follow up - I did end up using EventBus. The code is definitely a lot cleaner and there aren't callbacks hanging out everywhere. The IDE (IntelliJ) thinks that the onEvent methods are unused, so I created an annotation
#Target({ElementType.METHOD})
public #interface EventBusHook {}
and placed it over my onEvent methods. Then Alt+Clicked on it and asked IntelliJ to not treat it as unused.
#EventBusHook
public void onEvent(MyEventType myEventType){
I disagree with #nnuneoi's answer.
Event bus has just one single advantage: it allows for communication between components which are "unaware" of each other's existence.
And there are several disadvantages:
The components become loosely coupled by dependency on both event bus and specific event type
The coupling described in #1 above is not strong
The coupling described in #1 above is not evident
Event bus introduces performance overhead over simple callbacks
If event bus holds a strong reference to subscribers (as is the case with e.g. GreenRobot's EventBus), then unregistered subscribers will cause memory leaks
Given all these disadvantages, simple callbacks should be the default implementation choice.
Event bus should be used only when direct coupling is not desired or hard to implement. For example:
Sending events from Service to Activity
Exchanging events between independent Fragments
Application wide events (e.g. user login/logout)
If the communicating components are already "aware" of each other's existence, there is no need for them communicating via event bus.
Benefits of using EventBus:
Your code will look much more clean
Your code will become more modular which will allow you to easily create test case for your code
Avoid memory leaks from bad object references which lock the object and does not allow Garbage Collector to clean it up
Could have more than one receiver a time, that it much like broadcasting
Simplify multiple interfaces into a single one, EventBus
In an interface class, you need to override every single method in the class that is inherited. With EventBus, you can listen for just an event that you really want
But bad part is you might be a little bit more headache with the function declaration since IDE couldn't help you with auto-complete.
My suggestion is, if you find that you have to create a custom listener, then please consider EventBus, it might be a better choice for most of (if not all) of your requirements/cases.
Anyway, it is all your choice after all =)
You should check if your event is globally unique on the semantic view. Either an subscriber is interested in the event or not. If not he shouldn't subscribe.
Event-Bus mechanism is right if you really have a publisher-subscriber relationship. The event must be totally independent of the receiver.
So a subscriber that discard the event for any reason of responsibility ("I am not responsible the event even if I am registered") is a strong indicator that using an Event-Bus is wrong. Then you should consider using dedicated listeners.
I would go with the EventBus because of the loose coupling and cleaner code. Also, the fact that using an EventBus such as Greenrobot automatically does all the boilerplate for me and allows me to register & deregister observers right from Activity Lifecycle methods (onStart and onDestroy|onStop) is great. Implementing callbacks and still managing to control Activity lifecycle management for those callbacks is an unnecessary headache and involves a lot of unnecessary boilerplate.
Also, apparently all garbage collectors think weak reference is great-Event bus gives your observers and components exactly that. Its the basis of the Observer pattern.
Related
I'm working on this project in Android in which an aspect requires a CountdownTimer with a foreground service. A few other answers on Stack Overflow mentioned that LocalBroadcastManager would be suitable for my needs.
The documentation in Android Developers, however, mentions that it has been deprecated. Any suggestions on what I should use in its place? The documentation mentioned about using LiveData, but I was wondering if there are any easier alternatives.
LocalBroadcastManager is basically an event bus with a lot of unnecessary ceremony around Intents and intent filters. So one replacement is easy, and functions quite similarly: you can use any event bus library. greenrobot's EventBus is a popular choice (here's a guide for it) and Guava also has one, if you're already using Guava (but Guava is pretty heavy to include just for an event bus).
But event buses suffer from the same problems that LocalBroadcastManager does that led to it being deprecated: it's global, it's not lifecycle-aware, and as your app gets larger, it becomes much more difficult to reason about the effects of a change to an event. For cases of observing data, LiveData solves this quite nicely because it's lifecycle-aware, so you won't get change notifications at the wrong time (like before your View is set up, or after onSaveInstanceState) - but it'll handle delivering the change notifications when you're in the right state again. It's also more tightly scoped - each piece of LiveData is accessed separately rather than having (typically) one event bus/LocalBroadcastManager for the entire app.
For cases where it's more of an event rather than a piece of data being changed, you can sometimes convert it to a piece of data. Consider if you have "login" and "logout" events - you could instead create a LiveData that stores an Account for logged-in users, and becomes null when the user is logged out. Components could then observe that.
There are certainly cases where it really is difficult to convert it to a piece of observable data (though I can't immediately think of any examples that would typically be used with an event bus patten). For those, consider writing your own listener interface, similar to how on-click listeners work.
For your example of a countdown timer, I think LiveData is a pretty straightforward solution, and will be much easier than an event bus or even LocalBroadcastManager would be. You can just have a LiveData of the timer's current value, and subscribe to it from whatever needs to show the value.
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 LocalBroadcastManager with 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 Activity that 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:
LiveData is easiest to use in an Activity or Fragment as part of a ViewModel. However, ViewModel is only meant to be used from those classes that directly deal with the UI. Accessing the ViewModel from within the FirebaseMessagingService takes an ugly hack and is a really bad idea from an architectural perspective. Also, different activities and fragments have different ViewModel objects, 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 LiveData properties, have the FirebaseMessagingService update those LiveData objects from the incoming messages, and have the Activity observe those changes and copy them into its own ViewModel's LiveData properties. The problem with that is twofold: first, it requires me to have two identical LiveData objects for each piece of data, one in the ViewModel and one in the object; and second, it doesn't help me with handling the "log out event", because LiveData is meant to handle changing data, not listening to a stream of events. (I may be able to handle the second issue using this LiveData Event 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 Channels or 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 suspend with Flow for this case.
In summary:
Are Kotlin Coroutines and their associated concurrency structures a good way to handle communication between Android Service and Activity?
If yes, which Kotlin type makes more sense to use, Channel or Flow?
Coroutines don't really help with the handoff of data from one software component to another. They help with the processing multiple units of asynchronous work using syntax that appears as if they were synchronous. That's the bottom line for coroutines. They're analogous to async/await syntax in JavaScript. While you might use a coroutine to access data from asynchronous sources, it doesn't give you any primitves to proxy that data on to other components.
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).
Can I use the Event Bus to decouple all of the application layers? I m trying to use the clean architecture. Normally the decoupling is made by a boundaries interfaces, I have seen some examples using RX observers for that. The question is can i use Event Bus to decouple the layers? and can the event bus handle such a job?
Event Bus is perfect for cross-cutting activities so you don't need to pass trough a middle layer to deliver an event if you don't need to.
For clean/onion/multi-layer architecture you don't need Event Bus but clear contracts between the layers i.e. boundary interfaces. They may or may not use RX.
You can fully decouple modules using on Event Bus with no interfaces whatsoever and then all components will be extremely decoupled, however it will become a hell to debug, maintain and super difficult to do anything meaningful :) So some kind of contract is always a good idea even when using Event Bus.
Combining Reactive Programming and Event Bus you can create highly decoupled event driven pico services bounded by some contracts around your Event Bus in order to improve clarity of the event/command/data flow.
I am personally using and working on RxHub which was born exactly from the need of passing cross-cutting events and easy data-flow operators chaining.
I have implemented a service, where I handle the state changes(connect, disconnect, onServiceDiscoverd, onCharacteristicChange etc) and receiving data from another device through gatt Server.
My question is, Can the events be handled efficiently using Greenrobot Eventbus replacing broadcast receiver between service and Activity?
Unlike LocalBroadcastManager, EventBus is more simple to use. You only go via 3 steps:
1- Create an event class. A simple Java class that represent the response when
the action occur.
2- Register the event bus as a subscriber in your Activity onCreate method
EventBus.getDefault().register(this);
And of course, unregister it in your Activity onDestroy method
EventBus.getDefault().unregister(this);
3- The subscribing method is created in the same activity that registered for the EventBus. Example in WorkOrderActivity
#Subscribe
public void onEvent(EventClass event)
When the event occur, you should call the post method, passing the event object you created before.
EventBus.getDefault().post(new EventClass (Data));
As kmaini mentioned, you can replace it with LocalBroadcastManager, but you will have to map the data from the intent by yourself. Unlike EventBus which can pass objects.
Also, greenrobot, the creators of EventBus Library, answered this question here:
Q: How's EventBus different to Android's BroadcastReceiver/Intent
system?
A: Unlike Android's BroadcastReceiver/Intent system, EventBus uses
standard Java classes as events and offers a more convenient API.
EventBus is intended for a lot more uses cases where you wouldn't
want to go through the hassle of setting up Intents, preparing Intent
extras, implementing broadcast receivers, and extracting Intent
extras again. Also, EventBus comes with a much lower overhead.
From another perspective, I believe broadcast managers in Android uses main thread handler's message queue to process events. So, if you are free to use a different thread (if you have no-UI events/jobs/tasks) with a proper queue (like using another HandlerThread) then you can take advantage of using that thread's specific queue to process your jobs, without interfering UI events and mixing your stuff with UI work. You can also play with the thread's priority value to balance the work.
Now, if GreenRobot provides that all functionality in a few lines of code, then I would definitely try it to see any performance gain.
EventBus makes things much easier because you can pass is arbitrary Java objects along in the event.You not do the same with Intents because your object has to implement Parcelable and the "tedious" parcelable implementation which is something you might not what to do on an existing code base.
Is it good practice to use Otto event bus bi-directionally?
I mean, send events from controller to view, and view to controller?
Or is it just meant to publish results meaning its purpose is only events from controller to view?
Thanks
Good question. Here is my thoughts on this. I use Otto for a while and use it bi-directionally. From my experience there is nothing against doing this. I have just defined couple of rules helping me keep everything under control.
One-to-many pattern must be confirmed. I mean here, that one producer should normally notify multiple subscribers. Of course, at different point in time there can be zero or more subscribers. But if you have a case, where by-design maximum number of subscribers is just one, then you trying to dispatch rather a "command", than an "event". For such cases I would use a direct call instead of posting a event.
Another thing to avoid should be a situation, when one event triggers another event, which, in turn, triggers first event again. You can run in infinite event chain here. This might happen when same class has the both subscriber and producer methods. If I have such classes, I try to keep those methods as independent as possible.
And of course I always use Android components' lifecycle to register and unregister publishers and subscribers dynamically. I start with onResume() and onPause() methods and, if needed, I'm going up to onStart() or even onCreate().