I am planning to add Otto event bus to decouple my communications. One of the things I want use event bus for is to communicate between a button click handler and the activity.
The idea is that the button click (from my custom view) would generate a text submission event which would notify the activity. The activity would then decide what to do with it. If the activity deems it proper, it would send the text to a service for upload or whatever.
Is this a proper way to use an event bus?
Also, what are some good practices when using event buses?
I still think this question should be closed as not proper for the StackOverflow model.
But for anyone looking on how on can organize user events around a Bus, that's kinda of how we've done on the place I work.
Remember, that type of structure only makes sense if you're creating a big project where achieving a high level of separation makes the life of a team of developers easier. For small, quick projects or test apps that's too much effort.
PS.: all the code below is typed 100% by heart without checking any real code, so there will be typos and small errors, but should be enough to get an idea of the approach. I also didn't write any annotation like @override
, too lazy for it.
First: Activity overrides getSystemService
to supply a Bus via Context and register/unregister event handlers as needed.
public MyActivity extends AppCompatActivity {
private static final String BUS_SERVICE = "bus_service";
private List<EventHandler> eventHandlers = new ArrayList();
private Bus bus = new Bus();
public void onCreate(Bundle savedState){
super.onCreate(savedState);
.... layout creation, etc, etc, etc
if(isLoggedIn()) {
eventHandlers.add(new LoggedUserNavigationHandler());
eventHandlers.add(new RestPostRequestHandler());
} else{
eventHandlers.add(new GuestUserNavigation());
}
eventHandlers.add(new AnalyticsTrackingHandler());
if(DEBUG) {
// log all events in debug mode
eventHandlers.add(new EventHandler(){
@Subscribe
public void onEvent(Object o){
Log.d(TAG, "Event: " + o.toString);
}
});
}
}
}
public Object getSystemService(String name){
if(BUS_SERVICE.equals(name)) return bus;
else return super.getSystemService(name);
}
public void onStart(){
super.onStart();
for(int i=0, size=eventHandlers.size(); i<size; i++) {
eventHandlers.get(i).activity = this; // pass reference, might be usefull
bus.register(eventHandlers.get(i));
}
}
public void onStop(){
for(int i=0, size=eventHandlers.size(); i<size; i++) {
bus.unregister(eventHandlers.get(i));
eventHandlers.get(i).activity = null;
}
super.onStop();
}
}
Then: You have all the RecyclerView.ViewHolder
(or custom widget) to be the click listener and dispatch appropriate events. For example in a ViewHolder for a photo item.
public class PhotoHolder extends ViewHolder implements OnClickListener {
TextView user;
ImageButton like;
ImageView photo;
Photo data; // assume this was set during `bindViewHolder`
public PhotoHolder(View itemView) {
super(itemView);
user = (TextView) itemView.findViewById(...
like = (ImageButton) itemView.findViewById(...
photo = (ImageView) itemView.findViewById(...
user.setOnClickListener(this);
like.setOnClickListener(this);
photo.setOnClickListener(this);
}
public void onClick(View view){
switch(view.getId()){
case R.id.user:
((Bus)view.getSystemService(BUS_SERVICE))
.post(new Event.PhotoEvent.UserTap(data);
break;
case R.id.like:
((Bus)view.getSystemService(BUS_SERVICE))
.post(new Event.PhotoEvent.LikeUnlike(data);
break;
case R.id.photo:
((Bus)view.getSystemService(BUS_SERVICE))
.post(new Event.PhotoEvent.PhotoTap(data);
break;
}
}
}
and the last of course: is to create those events objects and add all the events to your appropriate handlers.
// add all the app events under this class, or maybe create a `Event` package and then all the events in that package
public final class Event {
public static class PhotoEvent {
public final Photo data;
public Photo(Photo data){
this.data=data;
}
public static class UserTap extends PhotoEvent{
// copy matching constructor
}
public static class LikeUnlike extends PhotoEvent{
// copy matching constructor
}
public static class PhotoTap extends PhotoEvent{
// copy matching constructor
}
}
}
finally, handling events
public class RestPostRequestHandler {
@Subscribe
public void onPhotoLikeUnlike(Event.Photo.LikeUnlike event){
// make your POST request here
}
}
a handler for navigating:
public class LoggedUserNavigationHandler extends EventHandler{
@Subscribe
public void on(Event.Photo.UserTap event){
Intent i = new Intent( ... create here intent for the "user profile"
// activity reference was passed during onStart
activity.startActivity(i);
}
}
a handler for analitics:
public class AnalyticsTrack {
@Subscribe
public void on(Event.Photo.UserTap event){
// send event "user tap" ?
}
}
I agree with some of the comments that it's possible to create a huge, weird spaghetti code when having "tap" events going through the bus. But if from the start a good structured approach is defined and all the developers follow it, you can achieve a project that is easy to follow and with a very clear separation of responsibilities.