Design Patterns in Android – Observer
The Observer pattern is one of the Behavioral Design Patterns, which means it enables communication between different classes and objects. Here, you can learn how to implement it step-by-step.
Design patterns are reusable solutions to the most commonly occurring software problems. They can speed up the development process by providing a proven way of resolving frequent issues. In my previous post, I introduced the Builder pattern. Now, it’s time to meet the Observer!
Introduction
The Observer pattern is one of the Behavioral Design Patterns, which means it gives us the way to communicate between different classes and objects. This pattern is the best approach if we have a one-to-many relation and we’d like to inform other objects about some changes or actions.
You may know the Observer pattern from RxJava, where it’s implemented in a very extensive way. We have Observable
, which is an object we’d like to observe (it may be also called Subject
in the Observer pattern) and we can subscribe for observing changes in those object. Every object interested in those changes will be called Observer
. We can have one Subject and many Observers, and these single Observers may know nothing about each other. We can also stop observing changes at any time.
The Observer pattern is also a great solution for designing your application to follow OCP (the open/closed principle), one of the SOLID rules which say that your application should be open for extension, yet closed for modification.
Observer – example
In this article, I’d like to introduce the Observer via a real example. Let’s say we have some Button, which is checkable. Every time someone clicks on it, it changes states between ON and OFF. In the Observer pattern, we’ll call this Button Subject
.
We also have some objects which are interested in the Button’s state, e.g. every time the Button is clicked, we’d like to show a different message in every visible Fragment. Every Fragment interested in Button’s state, here in this pattern, will be called Observer
.
Observer patterns give us the possibility to unsubscribe from observing the Subject at every moment, e.g. when we don’t want to show a message, such as when a Fragment is not visible.
To sum up, we have three fragments – Fragments A and B (our Observers
) are interested in the Button’s state. The Button itself is located in Fragment C (it’s our Subject). Everything is placed in the Activity. Now, let’s start!
NEED A SUCCESSFUL TEAM?
We’re 100% office based team with 7-years’ experience
in mobile & web app development
Implementation
How can we implement the Observer pattern? We need to create interfaces for Subject
and Observer
. Our Subject
needs to handle subscribing, unsubscribing and the updating of data:
1 2 3 4 5 | public interface Subject { public void register(Observer observer); public void unregister(Observer observer); public void notifyObservers(); } |
And there’s complementary Observer
, with the main function of receiving info about updates from Subject
:
1 2 3 | public interface Observer { public void update(final boolean checked); } |
Now we need to implement those interfaces in the proper places. Our Subject
is Fragment C (ThirdFragment), because it has a Button:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | public class ThirdFragment extends Fragment implements Subject { private List observers; private ToggleButton button; public ThirdFragment() { observers = new ArrayList<>(); } @Override public void onViewCreated(final View view, @Nullable final Bundle savedInstanceState) { button = (ToggleButton) view.findViewById(R.id.toggle_button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { notifyObservers(); } }); } @Override public void register(final Observer observer) { if (!observers.contains(observer)) { observers.add(observer); } } @Override public void unregister(final Observer observer) { observers.remove(observer); } @Override public void notifyObservers() { for (final Observer observer : observers) { observer.update(button.isChecked()); } } } |
Fragment C implements three methods – for registering, unregistering, and notifying observers. Thanks to these methods, we’ll store our observers in the list and, if there’s a need to notify them about some action, we can iterate through the list and run the proper callbacks.
We can also see the listener method set to the Button. Every time we click on the Button, we call notifyObservers()
, so they all know about the event.
Now we can go to the Fragments A and B. They are both our Observers, so they need to implement one method:
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class SecondFragment extends Fragment implements Observer { ... @Override public void update(final boolean checked) { if (checked) { textView.setText("ON"); } else { textView.setText("OFF"); } } } |
Whenever some action occurs, we’ll be notified about this in this method, so we can show the proper UI, for example. Notice that the boolean value is checked — it’s the value passed from the Button. To make it all work, we need to tell our Fragment C (Subscriber) to register both Fragment A and B (Observers). We can use the main activity to do this:
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class ObserverActivity extends AppCompatActivity { @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... thirdFragment.register(firstFragment); thirdFragment.register(secondFragment); } } |
From now on, Fragments A and B are registered to Fragment C and they will be updated about the state from Fragment C. The last thing we need to remember about is unregistering from observing this fragment, to avoid memory leaks. And that’s all!
Conclusion
The Observer pattern gives us loose coupling between single places in our application (e.g. between many fragments). Using this pattern, a subject can register an unlimited number of observers, interested on watching its state, without them knowing about each other. It’s a great solution for implementing communication in modules with one-to-many relationships.
About the author
Ready to take your business to the next level with a digital product?
We'll be with you every step of the way, from idea to launch and beyond!
Very good! objective explanation and beyond the examples of Design Patterns that do not have real application.
Thanks! 🙂
Thanks! 🙂
Really cool article. You make it look very simple! Thank you Paulina.
Maybe you could add that if a third fragment was destroyed without properly unregister, that it checks for null and if it is, your Subject should unregister it. But that can lead to overcomplicated article.
This is really helpful .