Android: Managing Multiple BroadcastReceivers

When I was developing the Presently Android client I ran into an interesting problem. If a user is in the main timeline activity and a new message comes in, the active timeline just needs to update with the new message. However, if the user is in another activity stream, (like viewing @ replies or DM's for example) and a new message comes in to the main stream the application should not only update the main stream but should also show the user a notification so they can be aware that new messages are coming into another activity.

In this post I will show you how I solved this problem.

There is a background service to pull new messages from the server. When the service receives a new message, a broadcast will be sent:

public class RefreshService extends Service {
public void refreshTimeline() {
//build the intent with new messages
sendBroadcast(intent);
}
}

Then, we have a BroadcastReceiver in TimelineActivity to receive the broadcast.

public class TimelineActivity {
private RefreshTimelineReceiver refreshReceiver;
public void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter(RefreshService.REFRESH_TIMELINE);
refreshReceiver = new RefreshTimelineReceiver();
registerReceiver(refreshReceiver, filter);
}
public void onPause() {
super.onPause();
unregisterReceiver(refreshReceiver);
}
public class RefreshTimelineReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
showNewMessages();
showNotification();
}
}
}

Now, when new messages come in the user can see the messages update to listview and get a notification in the status bar. But there is a small problem: it only works in TimelineActivity, because when the user opens a new activity or presses the Home button, the BroadcastReceiver is unregistered in the onPause() method.

So I tried to put the register/unregister operation in the onCreate()/onDestroy() methods. This fixed the problem of new messages and notifications not showing in other activities. But then there was another problem: in TimelineActivity, the user receives an additional and unnecessary notification. So I set out to fix this particular problem.

The first question I asked myself was, “Can I only use one BroadcastReceiver?” The answer was “No!” So, I used two BroadcastReceiver(s) to fix this problem.

public class RefreshService extends Service {
public void refreshTimeline() {
//build the intent with new messages
this.sendOrderedBroadcast(intent, null);
}
}
public class TimelineActivity {
private RefreshTimelineReceiver refreshReceiver;
private RefreshTimelineAndNotifyReceiver refreshAndNotifyReceiver;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
IntentFilter filter = new IntentFilter(RefreshService.REFRESH_TIMELINE);
filter.setPriority(FILTER_LOW_PRIORITY);
refreshAndNotifyReceiver = new RefreshTimelineAndNotifyReceiver();
registerReceiver(refreshAndNotifyReceiver, filter);
}
public void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter(RefreshService.REFRESH_TIMELINE);
filter.setPriority(FILTER_HIGH_PRIORITY);
refreshReceiver = new RefreshTimelineReceiver();
registerReceiver(refreshReceiver, filter);
}
public void onPause() {
super.onPause();
unregisterReceiver(refreshReceiver);
}
public void onDestroy() {
super.onDestroy();
unregisterReceiver(refreshAndNotifyReceiver);
}
public class RefreshTimelineReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
showNewMessages();
this.abortBroadcast();
}
}
public class RefreshTimelineAndNotifyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
showNewMessages();
showNotification();
}
}
}

In this code there are some key points I want to outline:

  • Broadcast the new messages with sendOrderedBroadcast() instead of sendBroadcast(). It will be passed one by one unless you stop it by abortBroadcast().

  • Register/unregister RefreshTimelineReceiver in onResume()/onPause(), and RefreshTimelineAndNotifyReceiver in onCreate()/onDestroy(). This makes certain there is no RefreshTimelineReceiver in other activities.

  • Give FILTER_HIGH_PRIORITY to RefreshTimelineReceiver and FILTER_LOW_PRIORITY to RefreshTimelineAndNotifyReceiver. In TimelineActivity, RefreshTimelineReceiver receives the broadcast first.

  • AbortBroadcast after showNewMessages() in RefreshTimelineReceiver. Don't pass the broadcast to next receiver.

And this is how I solved the problem of notifying a Presently Android user of new incoming messages to the main activity if they are active in a separate activity stream. Hopefully this might help someone else out who might be working through a similar problem!