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!