Android Design Patterns - Observer Patterns

Preface

Android Design Patterns series articles, welcome attention, ongoing updates:

Android Design Patterns - Six Principles of Design Patterns
Creative mode:
Android Design Patterns - Singleton Patterns
Android Design Patterns - Builder Patterns
Android Design Patterns - Factory Method Patterns
Android Design Patterns - Simple Factory Patterns
Android Design Patterns - Abstract Factory Patterns
Android Design Patterns - Prototype Patterns
Behavioral patterns:
Android Design Patterns - Policy Patterns
Android Design Patterns - State Patterns
Android Design Patterns - Responsibility Chain Patterns
Android Design Patterns - Observer Patterns

1. definition

Define a one-to-many dependency between objects. When the state of an object is changed, the objects that depend on it are notified and updated automatically.

2. introduction

  • The observer belongs to behavioral pattern.
  • Observer mode is also called publish/subscribe mode.
  • The observer pattern is mainly used to decouple the observer and the observer, so that there is no dependence or little dependence between them.

3.UML class diagram

Role description:
  • Subject (abstract subject): Also called abstract observee, all references to observer objects are stored in a collection, and each subject can have any number of observers. Abstract topics provide an interface to add and delete observer objects.
  • ConcreteSubject (Specific Subject): Also called Specific Observed, it stores the relevant state in the specific Observer Object, and notifies all registered observers when the internal state of the specific subject changes.
  • Observer (abstract observer): Define an interface for all specific observers to update themselves when they receive subject notifications.
  • ConcrereObserver: Implements an update interface defined by an abstract observer that updates its status when notified of a change in the topic.

4. implementation

Continue to take express delivery as an example. Sometimes the courier just pulls the express downstairs, and then notifies the recipient to go downstairs to pick up the express.

4.1 Create an abstract observer and define a new method of receiving notification, that is, the response of the recipient after receiving notification:

    public interface Observer {//Abstract observer
        public void update(String message);//Update method
    }

4.2 Create concrete observers and implement methods in abstract observers. Here, create two classes, a boy class and a girl class, and define their responses after notification:

    public class Boy implements Observer {

        private String name;//Name
        public Boy(String name) {
            this.name = name;
        }
        @Override
        public void update(String message) {//Boys'specific reactions
            System.out.println(name + ",Information received:" + message+"Get the express in a bumpy way.");
        }
    }

    public class Girl implements Observer {

        private String name;//Name
        public Girl(String name) {
            this.name = name;
        }
        @Override
        public void update(String message) {//Girls'Reactions
            System.out.println(name + ",Information received:" + message+"Let your boyfriend pick up the express~");
        }
    }

4.3 Create Abstract topics, that is, Abstract observers, define add, delete, notify and so on.

    public interface  Observable {//Abstract observee
         void add(Observer observer);//Adding observers

         void remove(Observer observer);//Delete the observer

         void notify(String message);//Notify the observer
    }

4.4 Create a specific theme, that is, the specific observee, that is, the courier, when delivering the courier, inform the recipient according to the courier information to let them pick up the piece:

    public class Postman implements  Observable{//Courier

        private List<Observer> personList = new ArrayList<Observer>();//Preserve the information of the recipient (observer)
        @Override
        public void add(Observer observer) {//add recipient
            personList.add(observer);
        }

        @Override
        public void remove(Observer observer) {//Remove the addressee
            personList.remove(observer);

        }

        @Override
        public void notify(String message) {//Notify the recipient (observer) one by one
            for (Observer observer : personList) {
                observer.update(message);
            }
        }
    }

4.5 Client Testing:

    public void test(){
        Observable postman=new Postman();

        Observer boy1=new Boy("Monkey D Luffy");
        Observer boy2=new Boy("Qiao Ba");
        Observer girl1=new Girl("Nami");

        postman.add(boy1);
        postman.add(boy2);
        postman.add(girl1);

        postman.notify("Express delivery.,Please go downstairs to collect it..");
    }

Output results:

Lufei, received the message: Express arrived, please go downstairs to pick up. Butterfly to pick up the express.
Joba, I got the message: Express arrived, please go downstairs to pick it up. Butterfly to pick up the express.
Na Mei, received the message: Express arrived, please go downstairs to collect it. Let your boyfriend pick up the Express.~

4.6 Notes:
In fact, JDK also has two classes built-in: Observable (abstract observer) and Observer (abstract observer). We can also use them directly. The code is as follows:

public interface Observer {//(abstract observer)
    //Only one update method is defined
    void update(Observable o, Object arg);
}

public class Observable {//Abstract observee
    private boolean changed = false;//Define change status by default false
    private final ArrayList<Observer> observers;//Define an observer list

    public Observable() {//Constructor to initialize an observer list to save the observer
        observers = new ArrayList<>();
    }
    //Adding observers, with synchronization fields, is thread-safe
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!observers.contains(o)) {
            observers.add(o);
        }
    }

    //Delete the observer
    public synchronized void deleteObserver(Observer o) {
        observers.remove(o);
    }

    //Notify so observers, no parameters
    public void notifyObservers() {
        notifyObservers(null);
    }

     //Notify all observers with parameters
    public void notifyObservers(Object arg) {

        Observer[] arrLocal;
        //Add synchronized fields to ensure that there is no problem with multithreading
        synchronized (this) {
            if (!hasChanged())//The purpose of this judgment is to prevent meaningless updates.
                return;

            arrLocal = observers.toArray(new Observer[observers.size()]);//ArrayList is converted into a temporary array, which prevents notifications, additions, and removals from concurrent occurrences of possible exceptions
            clearChanged();/// Clear the change status and set it to false
        }
        //Traverse one by one notification
        for (int i = arrLocal.length-1; i>=0; i--)
            arrLocal[i].update(this, arg);
    }

    //Know all the observers
    public synchronized void deleteObservers() {
        observers.clear();
    }

    //Set the observer to change state and set to true
    protected synchronized void setChanged() {
        changed = true;
    }

    //Clear the change status and set it to false
    protected synchronized void clearChanged() {
        changed = false;
    }

    //Return to the current change status
    public synchronized boolean hasChanged() {
        return changed;
    }

    //Number of observers
    public synchronized int countObservers() {
        return observers.size();
    }
}

5. Application scenarios

  • When an object's change needs to be notified to other objects, and it does not know how many objects need to be changed.
  • When an object must notify other objects, it cannot assume who the other objects are.
  • Cross-system message exchange scenarios, such as message queues, event bus processing mechanisms.

6. advantages

  • Decoupling the observer from the subject. Let both sides of the coupling depend on abstraction rather than on concrete. So that each change will not affect the other side of the change.
  • Easy to expand, no need to modify the original code when adding observers to the same topic.

7. disadvantages

  • Dependency has not been completely relieved, and abstract topics still rely on abstract observers.
  • When using the observer mode, we need to consider the efficiency of development and operation. The program includes an observer, multiple observers, development, debugging and other content will be more complex, and the message notification in Java is usually executed sequentially. Then an observer Katon will affect the overall efficiency of execution. In this case, asynchronous implementation is generally adopted.
  • It may cause redundant data notifications.

8. Source code analysis in Android

8.1 Listener listening mode in control

The most common observer pattern we encounter in Android is the monitoring of various controls, as follows:

        Button button = (Button) findViewById(R.id.button);
        //Registered observer
        button.setOnClickListener(new View.OnClickListener() {
            //Observer Realization
            @Override
            public void onClick(View arg0) {
                Log.d("test", "Click button ");
            }
        });

In the above code, button is the specific subject, that is, the observer; the new View. OnClickListener object is the specific observer; OnClickListener is actually an interface, that is, an abstract observer; and the observer is registered with the observer through setOnClickListener.

Once a button captures a click event, that is, when the state changes, it notifies the observer that the Button state changes by calling back the onClick method of the registered OnClickListener observer.

Relevant source code analysis:

    public interface OnClickListener {//Abstract observer

        void onClick(View v);//Only onClick is the method
    }

    //Registered observer
    public void setOnClickListener(@Nullable View.OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);//Set to clickable
        }
        getListenerInfo().mOnClickListener = l;//Assign the incoming OnClickListener object to getListenerInfo().mOnClickListener, that is, mOnClickListener of mListenerInfo holds a reference to the OnClickListener object
    }

    ListenerInfo getListenerInfo() {//Return the ListenerInfo object, here is a singleton pattern
        if (mListenerInfo != null) {
            return mListenerInfo;
        }
        mListenerInfo = new ListenerInfo();
        return mListenerInfo;
    }

    public boolean performClick() {//Execute click events
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this);//Execute the onClick method, li.mOnClickListener, or OnClickListener object
            result = true;
        } else {
            result = false;
        }

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
        return result;
    }

8.2 Adapter's notifyDataSetChanged() method

When we use ListView, we call the notifyDataSetChanged() method of Adapter when we need to update the data. So let's look at the implementation principle of notifyDataSetChanged(), which is defined in BaseAdaper, and the specific code is as follows:

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
     //Data Set Observed
    private final DataSetObservable mDataSetObservable = new DataSetObservable();

    //Registered observer
    public void registerDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.registerObserver(observer);
    }
    //Cancellation of observers
    public void unregisterDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.unregisterObserver(observer);
    }
    //Notify all observers when data sets change
    public void notifyDataSetChanged() {
        mDataSetObservable.notifyChanged();
    }
}
    //Other code outlines

As can be seen from the above code, BaseAdapter actually uses the observer pattern, and BaseAdapter is the specific observer. Next, look at the implementation of mDataSetObservable.notifyChanged():

//Data Set Observed
public class DataSetObservable extends Observable<DataSetObserver> {

    public void notifyChanged() {
        synchronized(mObservers) {
            //Traverse through all observers and call their onChanged() method
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }

    //Other code outlines
}

Now we see the shadows of observers. Where do these observers come from? In fact, these observers are generated when ListView sets Adaper through setAdaper():

public class ListView extends AbsListView {
    //Other code outlines

    public void setAdapter(ListAdapter adapter) {
        //If an Adapter already exists, cancel the observer for the Adapter first
        if (mAdapter != null && mDataSetObserver != null) {
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        }

        //Other code outlines

        super.setAdapter(adapter);

        if (mAdapter != null) {
            mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
            mOldItemCount = mItemCount;
            mItemCount = mAdapter.getCount();//Get the number of data in Adapter
            checkFocus();

            mDataSetObserver = new AdapterDataSetObserver();//Create a data set observer
            mAdapter.registerDataSetObserver(mDataSetObserver);//Registered observer

           //Other code outlines
        } 
    }
}

As can be seen from the above code, the observer has, so what does the observer mainly do?

class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
        @Override
        public void onChanged() {
            super.onChanged();//Call the onChanged() method of the parent class
            if (mFastScroller != null) {
                mFastScroller.onSectionsChanged();
            }
        }

       //Other code outlines
    }

The onChanged() method in the AdapterDataSetObserver class doesn't see anything. Continue to look at the onChanged() method of his parent class:

class AdapterDataSetObserver extends DataSetObserver {
        private Parcelable mInstanceState = null;
        //Core Realization of Observers
        @Override
        public void onChanged() {
            mDataChanged = true;
            mOldItemCount = mItemCount;
            mItemCount = getAdapter().getCount();//Get the number of data in Adapter
            if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
                    && mOldItemCount == 0 && mItemCount > 0) {
                AdapterView.this.onRestoreInstanceState(mInstanceState);
                mInstanceState = null;
            } else {
                rememberSyncState();
            }
            checkFocus();
            //Re layout
            requestLayout();
        }

       //Other code outlines
    }

Finally, the layout updates are implemented in the onChanged() method in the class AdapterDataSetObserver.

A brief summary:

  • When the data of ListView changes, we call the notifyDataSetChanged() method of Adapter, which in turn calls the onChanged() method of all the observers (Adapter DataSetObserver), and the onChanged() method calls the requestLayout() method to reposition.

8.3 BroadcastReceiver

Broadcast Receiver, as one of the four components of Android, is actually a typical observer mode. When sending broadcasting through sendBroadcast, only Broadcast Receiver object registered with the corresponding IntentFilter will receive the broadcast information, and its onReceive method will be invoked. Broadcast Receiver's code is more complex, so it won't start here. First dig a hole, and then it out. Broadcast Receiver Source Code Analysis.

8.4 other

In addition, some famous third-party event bus libraries, such as RxJava, RxAndroid, EventBus, otto and so on, also use the observer mode. If you are interested, you can see their source code.

Reading Related Articles
Android Design Patterns - Six Principles of Design Patterns
Creative mode:
Android Design Patterns - Singleton Patterns
Android Design Patterns - Builder Patterns
Android Design Patterns - Factory Method Patterns
Android Design Patterns - Simple Factory Patterns
Android Design Patterns - Abstract Factory Patterns
Android Design Patterns - Prototype Patterns
Behavioral patterns:
Android Design Patterns - Policy Patterns
Android Design Patterns - State Patterns
Android Design Patterns - Responsibility Chain Patterns
Android Design Patterns - Observer Patterns

Tags: Android JDK Java

Posted on Sat, 18 May 2019 13:58:03 -0400 by fangorn