Observer Pattern in Mobile: EventBus and NotificationCenter
Observer Pattern is somehow popular. It is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods. It is mainly used to implement distributed event handling systems. The Observer pattern is also a key part in the familiar model–view–controller (MVC) architectural pattern.
Key concepts of Observer Pattern in mobile enviroment are:
- Submit events and notifications from anywhere of code
- Receive and process the events and notifications from anywhere of code, too
- Senders and receivers are loosely coupled
So with Android & iOS, background service processing can do its job, and fire a notification when done without caring about any handler which awaiting for this notification. Or, a foreground screen can submit any event, which background screen can correctly receive it although they does not share the same context.
iOS – NotificationCenter
iOS has a nice built-in observer class, called NotificationCenter
. It helps us to:
- Register any object to response to any event, which indicated by a String. For example:
UserDataFetchedEvent
. - Deregister the observation after the object went to its end of lifetime.
- Attach data within the broadcasted event.
Here is a typical example. We have an UIViewController
, want to handle the event when user has been logged out due to any reason. We register the controller to listen for the event:
override func viewDidLoad() {
...
NSNotificationCenter.defaultCenter().addObserver(self, selector: "userHasLoggedOut", name: "UserLoggedOutEvent", object: nil)
...
}
When the controller is destroyed, we should unregister the observation:
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
And the handler:
func userHasLoggedOut() { ... }
Then, from anywhere inside your code, if user has been logged out, you just call:
NSNotificationCenter.defaultCenter().postNotificationName("UserLoggedOutEvent", object: nil)
All registered objects can receive the event right away. Easy enough for most use cases. You can also use this class to register to many more system events of iOS by their names.
Android – BroadcastReceiver
BroadcastReceiver
of Android is one of a observer-kind. It helps us to register to listen for many system events, as well as custom events too. It works nicely with Android system framework, like Activity
and Service
. However, it depends on Context
and not suitable for non-Android environment, like pure-Java processing modules or classes. We are also unable to control its thread and process. As well as deliver data within the event is far more complicated.
Android – EventBus
But you usually want to resolve observer problem without sticking to Android limitation of context and threading. You should consider EventBus.
In order to use EventBus, place the library .jar into your project, or add it to Gradle dependency:
compile 'de.greenrobot:eventbus:2.4.0'
EventBus works like this:
[
For example, you have an UserService
class responsible of fetching user data, an UserActivity
to display user information.
- You request
UserService
instance to fetch data for user with id is 1. UserService
calls to web-service in background thread, populate response and create the User object.UserService
post() an event using EventBus, indicate that its job has been completed, and attach the object with the event.UserActivity
, which has already register for listening to the event, will receive the event, in main thread. Now it can use the attached user model to display.
Implementation should be like this:
We define User
class:
public class User {
public int id;
public String name;
}
Then, we need to define an event, which can be any class. We prefer to have a BaseEvent
class, with a predefined ID, randomly generated when the event is created:
public class BaseEvent {
public final long id;
public BaseEvent() {
id = System.nanoTime();
}
}
Then the event, which user data is fetched can be like this:
public class UserDataFetchedEvent extends BaseEvent {
public final User user;
public UserDataFetchedEvent(User user) {
super();
this.user = user;
}
}
UserService
class, will post the event right after it had completed loading task:
public void fetchUserData(int id) {
...
EventBus.getDefault().post(new UserDataFetchedEvent(user));
}
Now is the interesting part, UserActivity
will register with the EventBus that it want to observer the event broadcast:
public void onCreate(Bundle savedInstance) {
...
EventBus.getDefault.register(this);
...
}
Implement the listener within UserActivity class:
public void onEventMainThread(UserDataFetchedEvent e) {
if (e != null && e.user != null) { // populate data }
}
We add null checks here just to ensure we do not pass the wrong event. The parameter of the method allows EventBus to deliver exact event type we need. Note onEvent* prefix is belong to EventBus, while *MainThread is postfix which tell the bus to deliver the event on main thread instead of which ever.
Conclusion
You can see, Observer pattern is very powerful within mobile development. It reduces complexity and coupling between totally unrelated objects. While native support from platform can be limited somehow, awesome libraries like EventBus, and other folks like Otto, etc. are always ready to help you create awesome apps with many more features. Please, leave comment here if you have any concern.