preface

The Observer pattern is a development pattern that we often use in our development work. This pattern is also used in many places in the Android source code. Examples include ListView, ContentProvider, Broadcast, and so on. This article will introduce the observer mode, implement an observer mode and analyze the Android source code.

define

Defines a dependency between objects on which all dependent objects are notified and automatically updated when an object’s state changes.

introduce

The observer pattern is also known as the publish/subscribe pattern. So one person publishes and N people subscribe. The observer pattern is mainly used for decoupling, decoupling the observed from the observer so that there is little or no dependency between them. It is most useful in GUI systems, subscription-publish systems. For example, the status bar in the upper left corner of an App developed by our company needs to display the status of alarm clock in real time. If there is a set alarm clock, the alarm clock icon will be displayed; otherwise, it will not be displayed. Because you store the ContentProvider used by the alarm clock, you can use a listener to listen for changes in the alarm data, determine the current alarm state and display or close the icon if it does. In this way, the real-time display of the alarm clock status function is realized.

UML class diagrams

Manual implementation

  • Create observer

Implement the method in the Abstract Observer, which creates a Coder class and defines the actions to take when notified:

/** ** Programmers are observers ** /
public class Coder implements Observer{
	public String name;
	
	public Coder(String mName) {
		name = mName;
	}

	@Override
	public void update(Observable arg0, Object arg1) {
		// TODO Auto-generated method stub
		System.out.println("Hey," + name + "Your delivery has arrived.");
	}
	
	public String toString(String name) {
		return "Programmer :"+name; }}Copy the code
  • Create an Observable that notifies programmers to pick up an Observable when it arrives:
/** * Courier is a person who notifies all observers (programmers who have a Courier) when they arrive at the company to pick up the Courier@author Rickon
 *
 */
public class Courier extends Observable {
	public void postNewExpress(String content) {
		// Indicate that the status or content has changed.
		setChanged();
		
		// Notify all observersnotifyObservers(content); }}Copy the code
  • test
public class Test {

	public static void main(String[] args) {
		// Observed
		Courier courier = new Courier();
		/ / observer
		Coder coder1 = new Coder("Programmer Joe");
		Coder coder2 = new Coder("Programmer Li Si.");
		Coder coder3 = new Coder("Programmer Wang Er Zi.");
		
		// Register the observer to the observed observer list
		courier.addObserver(coder1);
		courier.addObserver(coder2);
		courier.addObserver(coder3);
		
		// The package arrived
		courier.postNewExpress("It arrived!"); }}Copy the code
  • The output
Hey, programmer Wang Erzi, your delivery has arrived Hey, programmer Li Si, your delivery has arrived hey, programmer Zhang SAN, your delivery has arrivedCopy the code

This code implements a simple Observer pattern using the JDK’s built-in Observable and Observer. The built-in observer pattern in the JDK also illustrates the wide application and importance of the observer pattern.

Application scenarios

  • Multi-level event triggering scenario;
  • When an object must inform other objects, it cannot assume who the object is;
  • Cross-system message exchange scenarios, such as message queues, event bus processing mechanisms.

The advantages and disadvantages

advantages

  • Abstract coupling between observer and observed can cope with business changes well.
  • Enhance system flexibility and scalability.

disadvantages

  • In the development process, there may be one observed, multiple observers, development and debugging will become more complicated. In addition, too many observers take longer to receive notifications.

Observer mode in Android

  • The notifyDataSetChanged() method is called when the ListView is used to add data. Let’s find out!

NotifyDataSetChanged we find that notifyDataSetChanged is defined in BaseAdapter as follows:

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

    public boolean hasStableIds(a) {
        return false;
    }
    
    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. */
    public void notifyDataSetChanged(a) {
        mDataSetObservable.notifyChanged();
    }

    // Omit some code
}
Copy the code

BaseAdapter looks like observer mode, so how does it work? And how to implement the observer model, let’s continue to analyze.

Take a look first at mDataSetObservable. NotifyChanged () method:

public class DataSetObservable extends Observable<DataSetObserver> {
    /**
     * Invokes {@link DataSetObserver#onChanged} on each observer.
     * Called when the contents of the data set have changed.  The recipient
     * will obtain the new contents the next time it queries the data set.
     */
    public void notifyChanged() {synchronized(mObservers) {// Traverse calls each observer's onChanged() method to notify the observed of the changefor(int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onChanged(); }} // omit some code}Copy the code

It’s easy to see that this method iterates through all observers and calls the onChnged() method for all observers to tell them that something has changed. So how are these observers set up? These observers are actually generated by the ListVIew using the setAdapter method. Let’s look at this code:

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

        // Omit some code
        super.setAdapter(adapter);

        if(mAdapter ! =null) {
            mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
            mOldItemCount = mItemCount;
            // Get the amount of data
            mItemCount = mAdapter.getCount();
            checkFocus();
            // Create a dataset observer here
            mDataSetObserver = new AdapterDataSetObserver();
            // To register an observer with Adapter, you are actually registering with DatasetObservable
            mAdapter.registerDataSetObserver(mDataSetObserver);

            // Omit some code
        } else {
           // Omit some code
        }

        requestLayout();
    }
Copy the code

As you can see from the above code, an AdapterDataSetObserver is built when the Adapter is set up, and the observer is then registered with the Adapter, so that both the observed and the observer are built. So how does AdapterData TobServer work? It 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 in turn inherits from the AdapterDataSetObserver of the AbsListView parent class AdapterView as follows

class AdapterDataSetObserver extends DataSetObserver { private Parcelable mInstanceState = null; // The onChanged method for all observers is called when the Adapter notifyDataSetChanged is called. The core implementation code is here @override public voidonChanged() {
            mDataChanged = true; mOldItemCount = mItemCount; MItemCount = getAdapter().getCount(); // Detect thecase 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(); // Refresh the ListView, GridView, etc. AdapterView components requestLayout(); } // omit some code}Copy the code

When we call the Adapter notifyDataSetChanged method, the method calls the DataSetObservable notifyDataSetChanged, This method calls the onChanged method of all observers, in which case the ListView relayout method is called to refresh the UI, which is a complete observer pattern.

Summary of ListView Observer pattern implementation: I recap the process: The Adapter contains an observable DataSetObservable that changes the amount of data Developers manual calling Adapter. NotifyDataSetChanged is actually call DataSetObservable onChanged method, this method will iterate through all the observer onChanged method. The onChanged function of AdapterDataSetObserver retrieves the new number of data sets in the Adapter, and then calls ListView’s requestLayout() method to rearrange and update the UI.

  • ContentProvider The observer pattern is also used in the use of ContentProvider at the beginning of this article. First create an observer object as follows:
ContentObserver alarmObserver = new ContentObserver(new Handler()) {
        // Create an observer object
        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);
            // Execute business code
        }

        @Override
        public void onChange(boolean selfChange, Uri uri) {
            super.onChange(selfChange, uri); }};Copy the code

Registered Observer:

mContext.getContentResolver().registerContentObserver(url, true, alarmObserver);
Copy the code

See what the three parameters of this registration method mean:

  • Uri: A URI that listens for changes in interest;
  • NotifyForDescendents: True indicates that any changes starting with the URI prefix are notified; False indicates that the notification is performed only when a match is complete.
  • Observer: IContentObserver interface that provides a method onChange that is called when Cursor needs to be updated when a change occurs

This allows you to listen only when the data in the database of interest changes by specifying the Uri. Specific source code is no longer analyzed, you can be interested in their own analysis of the source code, the observer mode is the case.