Main menu

Enter EventBus (a.k.a.: Intents! You shaLL NOT PAAASSS!!!)

The Google way: Intents and BroadcastReceivers

Believe me, I tried! I did! I've spent about five minutes trying to figure out what Intents do. About a year ago, when I started coding on Android, was young and stupid, I thought one needs to understand Intents to be a true Android Wizard. But alas! these mysterious concepts are beyond my comprehension.

  • What's with the names?
  • Why do they need data and extras?
  • Why isn't data part of extras?
  • Why do they have about a hundred constructors?
  • Why do I have to write 50 lines of code just to receive them?
  • And how the hell can I receive messages in any Object, not just Activity?
  • Do I really have to create my own Parcelable (hundreds of lines...) to pass my classes around?
  • How fast is constant packaging and unpackaging of basic types?

And so on... So after five minutes I've got tired and gave up on Intents, thinking that the concept is either too complicated or too lame to be understood.

OK, should I really use "lame" keyword without justification? Of course I shouldn't! That would be just... lame! So let me show you an example.

When currently played track changes in XenoAmp music engine, several subsystems have to be notified with new data:

  1. While Playing Screen
  2. Notification
  3. Lockscreen
  4. Scrobbler
  5. Guess what? Many more misc components should know when track changes!

So can my XenoAmpEngine extends CompletelyNothing { just announce: "My dear classes, a new PlayableAbstract is being played"? And could any Object receive this broadcast? Nope... Only BroadcastReceivers can receive Intent broadcasts. Oh, great! so can I add a BroadcastReceiver to my RandomCalss and... Aaaaaah, nope. I can register them only in Activities... Hmmmm... These Intents are pretty useless beasts! But, hey! Just to torture myslef, let me do it the Android Way! It's simple!

Intent bCast = new Intent("something.horrible.has.happened");
bCast.putExtra("playable",currentlyPlayedTrack) // currentlyPlayedTrack is of type PlayableAbstract getContext().sendBroadcast(bCast);

Right... But sending PlayableAbstract over broadcast ain't so easy! To use it as shown above I have to add a Parcelable interface to PlayableAbstract. What does it mean? Well it just means that YourRandomClass has to be enhanced to be elligible to become an Intent extra! Otherwise you have to put each field by hand each time you want to send this broadcast:

Intent bCast = new Intent("something.horrible.has.happened");
bCast.putExtra("title",currentlyPlayedTrack.getTitle())
bCast.putExtra("artist",currentlyPlayedTrack.getArtist())
bCast.putExtra("albumartist",currentlyPlayedTrack.getAlbumArtist())
bCast.putExtra("album",currentlyPlayedTrack.getAlbum())
// 30 or so lines to follow
getContext().sendBroadcast(bCast);

So either way you do it, you need a receiver. Assuming you wanted to save yourself some work and didn't go for Parcelable, you do it this way:

	class UpdateReceiver extends BroadcastReceiver {
		@Override
		public void onReceive(final Context arg0, final Intent intent) {
			if ("something.horrible.has.happened".equals(intent.getAction())) {
				Bundle extra=intent.getExtras();
				PlayableAbstract nowy=new PlayableAbstract();
				nowy.setTitle(extra.getStringExtra("title");
				nowy.setArtist(extra.getStringExtra("artist");
				// stupidity goes on for 32 lines
			}
		}
	}

But wait! There's more! Don't forget to:

updateReceiver = new UpdateReceiver();
IntentFilter onFilter = new IntentFilter("something.horrible.has.happened");
registerReceiver(updateReceiver, onFilter);

in your onResume method and:

unregisterReceiver(updateReceiver);

in your onPause method. Clean and elegant, right? And don't forget that even if you sell your soul to Satan himself, only Services and Activities will get these broadcasts... Sad, sad, sad. Stupid and totally useless...

What else can I say about Intents and BroadcastReceivers? They are not object-oriented. Since you don't extend your own Activities (or do you?) BroadcastReceivers can't be inherited. Since Intents are just pieces of tagged data, not Java classes, no inheritance there either.

The Kewl way: Enter EventBus

There must be some other way! And there is! It's called da EventBus and since I learnt about it I take it anywhere I go!

So how do I send broadcasts in XenoAmp? Just a single line:

EventBus.getDefault().post(new TrackChangeEvent(currentlyPlayedTrack)); // currentlyPlayedTrack is of type PlayableAbstract

Mind that TrackChangeEvent is just a class I've created to make event passing follow some OO convention (like i.e. exception handling), because otherwise EventBus allows passing ANY Object as event! To receive this event in ANY Object in your code just add this:

EventBus.getDefault().register(this);

to your constuctor (or enywhere you think it would work), and then:

EventBus.getDefault().unregister(this);

anywhere you think it is not needed anymore! Of course, instead of this you can pass just any Object that has any of EventBus methods implemented:

  public void onEventPostThread(TrackChangeEvent e) {
} public void onEventMainThread(XenoEvent e) { pokażżmianętraku(e.getChange()); } public void onEventBackgroundThread(TrackChangeEvent e) {
pokażżmianętraku(e.getChange()); } public void onEventAsync(VolimeMagnitudeEvent e) { }

If you've read the above lines carefully, you've may already be full of joy and excitement. YES! EventBus automagically handles task affinity for you! No more ugly:

new Handler(Looper.getMainLooper()).post(new Runnable() {
				@Override
				public void run() {
					// do something on main thread
				}
			});

Just put your code in proper method (post, main, background or async - see EventBus docs for more explanation)!

EventBus gives you added benefit of "onEventXXXXX" inheritance by derived classes and OO event passing system (if you are as anal retentive as I am!).

Now - this is smart, simple, beautiful and useful! Note also the .getDefault() - as you suspect it gets default or global EventBus for you. It's like broadcast channel on CB, but you're not limited to default EventBus object! You can create as many (private, semi-private, accessible to limited number of classes) EventBuses as you want!

FacebookG+TwitterRSS