1. Concept of observer model

1.1 introduction

The Observer Pattern is used when there is a one-to-many relationship between objects. For example, when an object is modified, its dependent objects are automatically notified. The observer model belongs to the behavioral model.

1.2 define

The Observer pattern (also known as Publish/Subscribe) is one of the behavioral patterns that defines a one-to-many dependency that allows multiple observer objects to listen on a subject object at the same time. The topic object notifies all observer objects of state changes so that they can update themselves automatically.

1.3 Application Scenarios
  • One object needs to notify other objects when data is updated, but the object does not want to be tightly coupled to the notified objects.
  • When an object’s data is updated, the object needs to let other objects update their data, but the object does not know how many objects need to update their data.

Observer pattern UML class diagram

Roles are as follows:

  • Abstract Themes: Abstract themes provide an interface to add and remove observer objects. Abstract theme roles are also called Abstract Observable roles.
  • ConcreteSubject: Maintains a list of references to all concrete observers, storing the relevant state in the concrete observer object; All registered observers are notified when the internal status of a specific topic changes. Concrete subject role is also called Concrete Observable role.
  • Abstract Observer: Defines an interface for all concrete observers. Defines an update method that is called when notified of a topic. This interface is called the update interface.
  • ConcreteObserver: Implements the update interface required by the abstract observer role, receiving the ConcreteSubject notification via the Update () method and taking concrete action. The concrete observer role can maintain a reference to the ConcreteSubject object (for passing the ConcreteSubject state information) if desired.

3. Observer mode implementation

Subject:

public class AbstractSubject {

    /** * Save the registered observer */
    private List<Observer> list = new ArrayList<>();

    /** * Add observer *@param observer
     */
    public void attach(Observer observer){
        list.add(observer);
    }

    public void detach(Observer observer){
        list.remove(observer);
    }

    public void notifyObservers(String content){
        for(Observer observer :list ){ observer.update(content); }}}Copy the code
ConcreteSubject:

public class ConcreteSubject extends AbstractSubject{}Copy the code
The Observer:

public interface Observer {

    /** * Update interface *@param state
     */
    public void update(String state);
}
Copy the code
ConcreteObserver:

public class ConcreteObserver implements Observer{

    private String name;

    public ConcreteObserver(String name) {
        this.name = name;
    }

    @Override
    public void update(String content) {
         System.out.println(name+":"+content); }}Copy the code
Client:

public class Client {
    public static void main(String[] args) {
        // Create an observed
        ConcreteSubject subject = new ConcreteSubject();
        // Define an observer
        Observer observer1 = new ConcreteObserver("Observer One.");
        Observer observer2 = new ConcreteObserver("Observer Two");
        Observer observer3 = new ConcreteObserver("Observer Three.");
        Observer observer4 = new ConcreteObserver("Observer four.");
        // The observer observes the observed
        subject.attach(observer1);
        subject.attach(observer2);
        subject.attach(observer3);
        subject.attach(observer4);

        // An observer cancels the observation
        subject.detach(observer2);

        // The observer begins to move
        subject.notifyObservers("Bear updated his article."); }}Copy the code

Result output:


Observer 1: Bear has updated his article observer 3: Bear has updated his article Observer 4: Bear has updated his articleCopy the code

4, Android source mode implementation

In the past, the most commonly used control is the ListView, and the most important point of the ListView is Adapter, after we add data to the ListView, we will call a method: NotifyDataSetChanged (), which uses what we call the observer mode.

Follow up with the method notifyDataSetChanged, which is defined in BaseAdapter as follows:


public abstract class BaseAdapter implements ListAdapter.SpinnerAdapter {
    // Dataset observer
    private final DataSetObservable mDataSetObservable = new DataSetObservable();

    // code omitted

    public void registerDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.registerObserver(observer);
    }

    public void unregisterDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.unregisterObserver(observer);
    }

    /** * Notifies the attached observers that the underlying data has been changed * and any View reflecting the data set Should refresh itself. notify all observers when a dataset changes */
    public void notifyDataSetChanged(a) { mDataSetObservable.notifyChanged(); }}Copy the code

Can be found that when the data changes, the notifyDataSetChanged will call mDataSetObservable. NotifyChanged () method


public class DataSetObservable extends Observable<DataSetObserver> {
    /** * Invokes onChanged on each observer. Called when the data set being observed has * changed, and which when read contains the new state of the data. */
    public void notifyChanged(a) {
        synchronized(mObservers) {
            // Call onChanged for all observers
            for (int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onChanged(); }}}}Copy the code

MDataSetObservable. NotifyChanged traverse all observers in the (), and invoke them onChanged method.

So where do these observers come from? First the ListView sets the Adapter using the setAdapter method


 @Override
    public void setAdapter(ListAdapter adapter) {
        // If you already have an adapter, unregister the observer corresponding to that adapter
        if(mAdapter ! =null&& mDataSetObserver ! =null) {
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        }

        // code omitted

        super.setAdapter(adapter);

        if(mAdapter ! =null) {
            mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
            mOldItemCount = mItemCount;
            // Get the amount of data
            mItemCount = mAdapter.getCount();
            checkFocus();
            // Note here: create a dataset observer
            mDataSetObserver = new AdapterDataSetObserver();
            // Register the observer with Adapter, which is actually registered with DataSetObservable
            mAdapter.registerDataSetObserver(mDataSetObserver);

            // code omitted
        } else {
            // code omitted
        }

        requestLayout();
    }
Copy the code

When the Adapter is set up, an AdapterDataSetObserver is built. Finally, the observer is registered with the Adapter, so that we have both the observed and the observer.

AdapterDataSetObserver is defined in ListView’s parent class, AbsListView, as follows:


 class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
        @Override
        public void onChanged(a) {
            super.onChanged();
            if(mFastScroll ! =null) { mFastScroll.onSectionsChanged(); }}@Override
        public void onInvalidated(a) {
            super.onInvalidated();
            if(mFastScroll ! =null) { mFastScroll.onSectionsChanged(); }}}Copy the code

It is made by AdapterDataSetObserver, which inherits from the parent class AdapterView of the AbsListView, with the following code:


class AdapterDataSetObserver extends DataSetObserver {

        private Parcelable mInstanceState = null;
        // The onChanged method for all observers is called when the Adapter notifyDataSetChanged is called, where the core implementation is
        @Override
        public void onChanged(a) {
            mDataChanged = true;
            mOldItemCount = mItemCount;
            // Get the amount of data in the Adapter
            mItemCount = getAdapter().getCount();

            // Detect the case where a cursor that was previously invalidated has
            // been repopulated with new data.
            if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState ! =null
                    && mOldItemCount == 0 && mItemCount > 0) {
                AdapterView.this.onRestoreInstanceState(mInstanceState);
                mInstanceState = null;
            } else {
                rememberSyncState();
            }
            checkFocus();
            // Rearrange the ListView, GridView and other AdapterView components
            requestLayout();
        }

        // code omitted

        public void clearSavedState(a) {
            mInstanceState = null; }}Copy the code

When the ListView data changes, the Adapter notifyDataSetChanged function is called, which in turn calls the DataSetObservable notifyChanged function. This function calls the onChanged method of all observers (Adapterdata Server). This is the observer mode!

Conclusion:

There’s an internal class in AdapterView, AdapterDataSetObserver, and when you set the Adapter in the ListView, you build an AdapterDataSetObserver, and you register it with the Adapter, and that’s an observer. The Adapter included in a data set can be DataSetObservable observer, the data quantity change developers call AdapternotifyDataSetChanged manually, NotifyDataSetChanged actually calls the notifyChanged function of DataSetObservable, which iterates over the onChanged function of all observers. The onChanged function of AdapterDataSetObserver retrieves the new number of data sets in the Adapter, and then calls the ListView requestLayout() method to rearrange the layout and update the user interface.

One of the better known open source frameworks for using the Observer pattern is EventBus RxJava

5. Model summary

5.1 the advantages
  • The observer and the observed are abstractly coupled.
  • Establish a trigger mechanism.
5.2 disadvantages
  • If an observed object has many direct and indirect observers, it can take a long time to notify all of them.
  • If there is a cyclic dependency between the observer and the observing target, the observing target will trigger a cyclic call between them, possibly causing the system to crash.
  • The observer mode has no corresponding mechanism to let the observer know how the observed object has changed, but only that the observed object has changed.