An overview of the

In life and the actual project, often involving multiple objects are interested in the change of data in a particular object, and the multiple objects are hoping to keep track of the data in a special object, in this case you can use the observer pattern, such as weather forecast, I believe that a lot of users have often received push from the weather forecast, or a magazine, If you subscribe, you get regular notifications of emails and so on.

The observer pattern is a mature pattern for multiple objects that want to know how the data in one object is changing. In the observer mode, there is one object called “subject (observed)” and several objects called “observers”. There is a one-to-many dependency between “subject” and “observers”. When the state of “subject” changes, all “observers” are notified. The “Weather Prediction center of the Met Office” described earlier is equivalent to a specific “theme” of the observer model; Each user receiving an SMS is a specific “observer” in the observer mode.

The structure of the observer pattern

There are four roles in the structure of the Observer mode:

● Topic (Subject): in general, the topic is an interface, the interface defines the specific topic needs to implement methods, generally have basic methods such as adding, deleting and updating content, of course, can be appropriately expanded, the general topic is also called the observed.

● Observer: The Observer is also an interface. The main method is the interface method of updating content.

● ConcreteSubject: A concrete theme is an instance of an interface class that implements a theme and contains data that can change frequently. A specific topic uses a collection, such as an ArrayList, that holds references to observers so that specific observers are notified of data changes.

● ConcreteObserver: ConcreteObserver is an instance of the ConcreteObserver interface class that implements an observer. A specific observer contains a topic interface variable that can hold a reference to a specific topic so that the specific observer can have the topic add its reference to the collection of the specific topic, make itself an observer of it, or have the topic remove itself from the collection of the specific topic. So that you are no longer an observer of it, so there is a delete interface in the general observer method.

Here is the class diagram for observer mode:

You can see that the structure of the class is exactly the same as the roles described above.

Advantages of the observer model

● The specific subject and the specific observer are loosely coupled. Because the topic interface depends only on the observer interface, a specific topic only knows that its observer is an instance of a class that implements the observer interface, but does not need to know which class. Again, because the observer only depends on the topic interface, the specific observer only knows that the topic it depends on is an instance of a class that implements the topic interface, but does not need to know which class.

● Observer mode meets the “open – close principle” in design mode. The topic interface depends only on the observer interface, so you can make the class that creates the specific topic depend only on the observer interface, so if you add a new class that implements the observer interface, you don’t have to change the code of the class that creates the specific topic. Similarly, the class that creates a concrete observer depends only on the topic interface, and if you add a new class that implements the topic interface, you don’t have to modify the code that creates the concrete observer class.

Usage 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 exactly how many objects need to update their data

Having said a lot of boring theories, let’s look at a simple example:

Start by defining a topic and abstract observer as follows:

public interface Subject {
     void registerObserver(Observer o);
     void removeObserver(Observer o);
     void notifyObservers(floattemprature); } public interface Observer {void update();floattemprature); void remove(Subject mSubject); // delete}Copy the code

We then define classes that implement topics and classes that implement abstract observers:

public class ConcreteSubject implements Subject {
    private static final List<Observer> observers;
    static {
        observers = new ArrayList<>();
        observers.clear();
    }

    @Override
    public void registerObserver(Observer o) {
        observers.add(o);
    }

    @Override
    public void removeObserver(Observer o) {
        if (observers.indexOf(o) >= 0) {
            observers.remove(o);
        }
    }

    @Override
    public void notifyObservers(float temprature) {
        if (observers.size() <= 0) {
            System.out.println("No observer.");
            return;
        }
        for(final Observer o : observers) { o.update(temprature); }}} This implements all principal interfaces for adding, removing, and notifying all observersCopy the code

The specific observers are as follows:

public class ConcreteObserver implements Observer {

    @Override
    public void update(float temprature) {
        System.out.println("Temperature is :"+ temprature); } @Override public void remove(Subject mSubject) { mSubject.removeObserver(this); // Delete it if you don't need to observe it}}Copy the code

Let’s test the code;

public class ObserverClient { public static void main(String[] args){ final Subject sb = new ConcreteSubject(); final Observer observer = new ConcreteObserver(); sb.registerObserver(observer); Sb. NotifyObservers (21.0 f); observer.remove(sb); Sb. NotifyObservers (23.0 f); }}Copy the code

Test code is very simple, first to register a specific observer, then subject to release a temperature, and then the exact observer is notified, and when the specific observer after deleted theme, will no longer receive the notification, just as you cancel magazine subscriptions, will no longer receive the magazine, as it is good to understand, test results are as follows:

It’s just a topic and an abstract observer, and it doesn’t matter how many topics or abstract observers there are, the basic principle is subscription-publish-update.

Observer mode in Android source code application

In fact, in the Android system source code, the observer mode can be said to be widely used, with the Adapter we are familiar with the update mechanism of the data to a simple analysis, in general, when there is data change, most people like a sentence to solve the problem:

mAdapter.notifyDataSetChanged(); Let's look at the call: public final voidnotifyDataSetChanged() {
            mObservable.notifyChanged();
        }
Copy the code

This mObservable is an AdapterDataObservable with the following code;

private final AdapterDataObservable mObservable = new AdapterDataObservable();
Copy the code

The mObservable update method is called as follows:

public void notifyChanged() {
            // since onChanged() is implemented by the app, it could do anything, including
            // removing itself from {@link mObservers} - and that could cause problems if
            // an iterator is used on the ArrayList {@link mObservers}.
            // to avoid such problems, just march thru the list in the reverse order.
            for(int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onChanged(); }}Copy the code

This is the point, fetching all the observers and then calling onChanged(), the question is, when did those specific observers get added? In the setAdapter() method, the code looks like this:

public void setAdapter(Adapter adapter) {
        // bail out if layout is frozen
        setLayoutFrozen(false);
        setAdapterInternal(adapter, false.true);
        requestLayout();
    }
Copy the code

And then it’s called again

 private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
            boolean removeAndRecycleViews) {
        if(mAdapter ! = null) {/ / first remove the old observer mAdapter unregisterAdapterDataObserver (mObserver); mAdapter.onDetachedFromRecyclerView(this); }if(! compatibleWithPrevious || removeAndRecycleViews) { removeAndRecycleViews(); } mAdapterHelper.reset(); final Adapter oldAdapter = mAdapter; mAdapter = adapter;if(adapter ! = null) {/ / here is the key, register adapter. RegisterAdapterDataObserver (mObserver); adapter.onAttachedToRecyclerView(this); }if(mLayout ! = null) { mLayout.onAdapterChanged(oldAdapter, mAdapter); } mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious); mState.mStructureChanged =true;
        markKnownViewsInvalid();
    }
Copy the code

We can see that the registration is called when mAdapter registerAdapterDataObserver method, the code is as follows:

public void registerAdapterDataObserver(AdapterDataObserver observer) { mObservable.registerObserver(observer); } public void registerObserver(T observer) {if (observer == null) {
            throw new IllegalArgumentException("The observer is null.");
        }
        synchronized(mObservers) {
            if (mObservers.contains(observer)) {
                throw new IllegalStateException("Observer " + observer + " is already registered."); } mObservers.add(observer); }} You can see that this is where the registration actually takes placeCopy the code

Here’s where the refresh actually happens; The code is as follows:

static class AdapterDataObservable extends Observable<AdapterDataObserver> {
        public boolean hasObservers() {
            return! mObservers.isEmpty(); } public voidnotifyChanged() {
            // since onChanged() is implemented by the app, it could do anything, including
            // removing itself from {@link mObservers} - and that could cause problems if
            // an iterator is used on the ArrayList {@link mObservers}.
            // to avoid such problems, just march thru the list in the reverse order.
            for(int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onChanged(); }} // Omit some codeCopy the code

Is take out, in turn, update call onChanged () method, AdapterDataObserver is an abstract class, so the real implementation class is RecyclerViewDataObserver, code is as follows:

private class RecyclerViewDataObserver extends AdapterDataObserver {
        RecyclerViewDataObserver() {
        }

        @Override
        public void onChanged() {
            assertNotInLayoutOrScroll(null);
            mState.mStructureChanged = true;

            setDataSetChangedAfterLayout();
            if(! mAdapterHelper.hasPendingUpdates()) { requestLayout(); // This is the point, actually trigger the refresh}}Copy the code

RequestLayout () calls the parent requestLayout() class layer by layer by default, and eventually calls the top class refresh. At the end, the famous view wrootimpl performTraversals() method is used to refresh the interface, and the refresh process is complete.

The observer mode in the actual Android application

As we mentioned earlier, the observer mode is particularly useful in one-to-many situations where changes in one object’s data cause changes in other objects’ data. In Android projects, there are a lot of requirements, such as broadcasting, where once a broadcast is sent, whoever registers the broadcast receives the broadcast message. Or EventBus event bus, as long as send news events, no matter where, who registered the news event, will receive event at the same time, through the processing of logic, in the actual project, sometimes encountered in different interface, need to change the condition of the UI at the same time, such as in the B a logic processing interface, Then you need to change some UI in the previous A interface and so on. If you have the following two interfaces, A and B,

Ok, now you need to click the change all text button in TestActivity2, and then you need to change the textViews in both TestActivity2 and TestActivity1 to the pre-set text. This example comes from the actual example, , of course, the actual example involves the red dots or other UI changes, not pulled out analysis, so in this simple example, but the principle remains the same, we first to analyze how to achieve better and EventBus sending messages, of course, can solve the problem, but need to go one by one manually after the new text message, Instead of using EventBus to send events, we use observer mode. We know that clicking “Change all text” triggers a bunch of UI changes. First, we define a method to update the text as follows:

public interface Observer { void updateText(String text); // Update TextView's text void remove(Observable Observable); // Delete yourself as an observer}Copy the code

These TextViews are then custom and implement the observer interface, as shown below

public class AutoUpdatetextView extends android.support.v7.widget.AppCompatTextView implements Observer { public AutoUpdatetextView(Context context) { super(context); } public AutoUpdatetextView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public AutoUpdatetextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @override public void updateText(String text) {if(! TextUtils.isEmpty(text)) {setText(text); } } @Override public void remove(Observable observable) { observable.remove(this); }}Copy the code

So, there you have it: abstract observer and concrete observer. Let’s write the abstract theme and the concrete theme as follows:

Observable public Interface Observable {void addObserver(Observer Observer); void remove(Observer observer); void update(String text); } // Public class ObservableManager implements Observable {private static final List<Observer> observers; private static ObservableManager sInstance; privateObservableManager() {

    }

    public static ObservableManager getInstance() {
        if (sInstance == null) {
            synchronized (ObservableManager.class) {
                if(sInstance == null) { sInstance = new ObservableManager(); }}}return sInstance;
    }

    static {
        observers = new ArrayList<>();
        observers.clear();
    }

    @Override
    public void addObserver(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void remove(Observer observer) {
        if (observers.indexOf(observer) > 0) {
            observers.remove(observer);
        }
    }

    @Override
    public void update(String text) {
        if (observers.size() <= 0) {
            Log.d("[app]"."No specific observer.");
            return;
        }
        for (Observer observer : observers) {
            observer.updateText(text);
        }

    }

    public void removeAll(List<Observer> mList) {
        if (mList == null || mList.size() == 0) {
            return;
        }
        Log.d("[app]"."The collection size removed is :" + mList.size());
        for (Observer observer : mList) {
            remove(observer);
        }
        Log.d("[app]"."The remaining set size is :"+ observers.size()); }}Copy the code

< span style = “box-sizing: inherit! Important;” style = “box-sizing: inherit! Important; If so, add it, if not, leave it at all. To do this, we need to write a BaseActivity. Here, the layout is not pasted.

public abstract class BaseActivity extends AppCompatActivity {

    protected List<Observer> mList = new ArrayList<>();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mList.clear();
        setContentView(getResID()); ViewGroup viewGroup = (ViewGroup) getWindow().getDecorView(); addUpdateTextView(viewGroup); } protected abstract int getResID(); Private void addUpdateTextView(ViewGroup ViewGroup) {for (int i = 0; i < viewGroup.getChildCount(); i++) {
            View childView = viewGroup.getChildAt(i);
            if (childView instanceof ViewGroup) {
                ViewGroup childGroup = (ViewGroup) childView;
                addUpdateTextView(childGroup);
            } else {
                ifObserver Observer = (Observer) childView; ObservableManager.getInstance().addObserver(observer); mList.add(observer); }}}} // Remove the AutoUpdatetextView from the tree after onDestroy to avoid memory leaks from holding the Activity @override protected voidonDestroy() { ObservableManager.getInstance().removeAll(mList); super.onDestroy(); }}Copy the code

Now let’s look at the TestActivity1 code,

public class TestActivity1 extends BaseActivity {
    private TextView text;

    @Override
    protected int getResID() {
        return R.layout.activity_test1;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        text = (TextView) findViewById(R.id.text);
        text.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(TestActivity1.this, TestActivity2.class); startActivity(intent); }}); }} jumps to TestActivity2Copy the code

TestActivity2:

public class TestActivity2 extends BaseActivity {
    private TextView update;
    String[] texts = {"Have fun."."Hehe, new text."."Hello?"."What about Android development?"."What about ios development?"."WP development"."PHP"."Python development"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        update = (TextView) findViewById(R.id.update);
        update.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int len = texts.length;
                int result = new Random().nextInt(len);
                Log.d("[app]"."result="+ texts[result]); / / here is the key, notify the other registered place to change the text, here is the text, in fact. Can do other things ObservableManager getInstance (), update (texts. [result]); }}); } @Override protected intgetResID() {
        returnR.layout.activity_test2; }}Copy the code

We can see:

 ObservableManager.getInstance().update(texts[result]);
Copy the code

AutoUpdatetextView’s interface method is used to update text

@override public void updateText(String text) {if(! TextUtils.isEmpty(text)) {setText(text); }}Copy the code

Run as follows:

This is the result, dynamic display of text, of course, the actual situation can do more logical operations, I don’t have space to show one by one, it is more important to understand the principle, so that’s all for today’s article, thank you for reading.