Android equivalent to NSNotificationCenter
In the process of porting an iPhone application over to android, I am looking for the best way to communicate within the app. Intents seem to be the way to go, is this the best (only) option? NSUserDefaults seems much lighter weight than Intents do in both performance and coding.
I should also add I have an application subclass for state, but I need to make another activity aware of an event.
The best equivalent I found is LocalBroadcastManager which is part of the Android Support Package.
From the LocalBroadcastManager documentation:
Helper to register for and send broadcasts of Intents to local objects within your process. This is has a number of advantages over sending global broadcasts with sendBroadcast(Intent):
- You know that the data you are broadcasting won't leave your app, so don't need to worry about leaking private data.
- It is not possible for other applications to send these broadcasts to your app, so you don't need to worry about having security holes they can exploit.
- It is more efficient than sending a global broadcast through the system.
When using this, you can say that an Intent
is an equivalent to an NSNotification
. Here is an example:
ReceiverActivity.java
An activity that watches for notifications for the event named "custom-event-name"
.
@Override
public void onCreate(Bundle savedInstanceState) {
...
// Register to receive messages.
// This is just like [[NSNotificationCenter defaultCenter] addObserver:...]
// We are registering an observer (mMessageReceiver) to receive Intents
// with actions named "custom-event-name".
LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,
new IntentFilter("custom-event-name"));
}
// Our handler for received Intents. This will be called whenever an Intent
// with an action named "custom-event-name" is broadcasted.
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// Get extra data included in the Intent
String message = intent.getStringExtra("message");
Log.d("receiver", "Got message: " + message);
}
};
@Override
protected void onDestroy() {
// Unregister since the activity is about to be closed.
// This is somewhat like [[NSNotificationCenter defaultCenter] removeObserver:name:object:]
LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
super.onDestroy();
}
SenderActivity.java
The second activity that sends/broadcasts notifications.
@Override
public void onCreate(Bundle savedInstanceState) {
...
// Every time a button is clicked, we want to broadcast a notification.
findViewById(R.id.button_send).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sendMessage();
}
});
}
// Send an Intent with an action named "custom-event-name". The Intent sent should
// be received by the ReceiverActivity.
private void sendMessage() {
Log.d("sender", "Broadcasting message");
Intent intent = new Intent("custom-event-name");
// You can also include some extra data.
intent.putExtra("message", "This is my message!");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
With the code above, every time the button R.id.button_send
is clicked, an Intent is broadcasted and is received by mMessageReceiver
in ReceiverActivity
.
The debug output should look like this:
01-16 10:35:42.413: D/sender(356): Broadcasting message
01-16 10:35:42.421: D/receiver(356): Got message: This is my message!
Here is something similar to @Shiki answer, but from the angle of iOS developers and Notification center.
First create some kind of NotificationCenter service:
public class NotificationCenter {
public static void addObserver(Context context, NotificationType notification, BroadcastReceiver responseHandler) {
LocalBroadcastManager.getInstance(context).registerReceiver(responseHandler, new IntentFilter(notification.name()));
}
public static void removeObserver(Context context, BroadcastReceiver responseHandler) {
LocalBroadcastManager.getInstance(context).unregisterReceiver(responseHandler);
}
public static void postNotification(Context context, NotificationType notification, HashMap<String, String> params) {
Intent intent = new Intent(notification.name());
// insert parameters if needed
for(Map.Entry<String, String> entry : params.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
intent.putExtra(key, value);
}
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
}
Then, you will also need some enum type to be secure of mistakes in coding with strings - (NotificationType):
public enum NotificationType {
LoginResponse;
// Others
}
Here is usage(add/remove observers) for example in activities:
public class LoginActivity extends AppCompatActivity{
private BroadcastReceiver loginResponseReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// do what you need to do with parameters that you sent with notification
//here is example how to get parameter "isSuccess" that is sent with notification
Boolean result = Boolean.valueOf(intent.getStringExtra("isSuccess"));
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
//subscribe to notifications listener in onCreate of activity
NotificationCenter.addObserver(this, NotificationType.LoginResponse, loginResponseReceiver);
}
@Override
protected void onDestroy() {
// Don't forget to unsubscribe from notifications listener
NotificationCenter.removeObserver(this, loginResponseReceiver);
super.onDestroy();
}
}
and here is finally how we post notification to NotificationCenter from some callback or rest service or whatever:
public void loginService(final Context context, String username, String password) {
//do some async work, or rest call etc.
//...
//on response, when we want to trigger and send notification that our job is finished
HashMap<String,String> params = new HashMap<String, String>();
params.put("isSuccess", String.valueOf(false));
NotificationCenter.postNotification(context, NotificationType.LoginResponse, params);
}
that's it, cheers!
You could try this: http://developer.android.com/reference/java/util/Observer.html
You could use this: http://developer.android.com/reference/android/content/BroadcastReceiver.html, which gives a similar behavior.
You can register receivers programmatically through Context.registerReceiver(BroadcastReceiver, IntentFilter) and it will capture intents sent through Context.sendBroadcast(Intent).
Note, though, that a receiver will not get notifications if its activity (context) has been paused.
I found that the usage of EventBus of Guava lib is the simplest way for publish-subscribe-style communication between components without requiring the components to explicitly register with one another
see their sample on https://code.google.com/p/guava-libraries/wiki/EventBusExplained
// Class is typically registered by the container.
class EventBusChangeRecorder {
@Subscribe public void recordCustomerChange(ChangeEvent e) {
recordChange(e.getChange());
}
// somewhere during initialization
eventBus.register(this);
}
// much later
public void changeCustomer() {
eventBus.post(new ChangeEvent("bla bla") );
}
you can add this lib simply on Android Studio by adding a dependency to your build.gradle:
compile 'com.google.guava:guava:17.0'