Android Observer Pattern, Introduction to Rxjava Observer Pattern

1. Observer pattern

Definition: Observer mode: It is the behavior mode of an object, also called Publish/Subscribe mode, Model/View mode, and Source/Listener mode. The observer pattern defines a one-to-many dependency relationship that allows multiple observer objects to monitor an object at the same time. When this object changes state, all observer objects will be notified so that they can automatically update themselves.

The following UML diagram:
Insert image description here

1.1 Analyze relationship diagram

Let’s first analyze this relationship diagram

  1. We must first abstract an Observer interface, which has an abstract method update(). The function of this method is to notify the observer to update itself.
  2. ConcrereObserver is its specific implementation subclass. This update() method will be implemented in the subclass, and then our own status will be updated in the current subclass.
  3. Subject is the object we want to monitor. It can be understood this way. The attach() method binds the observer, detach() removes the observer, and notify() notifies the observer of the current changes in the object, and then the observer responds accordingly. For these changes, do the corresponding things.
  4. ConcreteSubject is a subclass of Subject. We can customize some unique methods in the subclass. For example, we add a change() method unique to the subclass to update its own status. As long as the subclass changes, it can be called in the parent class. The notify() method notifies the Observer for processing.
  5. Observer Observer, generally we establish the corresponding dependency relationship in the Subject object. The relationship is a one-to-many relationship. One Subject can correspond to multiple observers.

To summarize the above content:

  • Subject: This class is to rely on the references of observers, such as placing observers in a collection or array. Each object can have any number of observers. This observed object can be called an Observable. , the observed can add or delete observer objects, such as the attach() and detach() we mentioned above.
  • ConcreteSubject: This is a subclass of the observed object. When the specific object, that is, some state of the current subclass object changes, a notification is sent to the observer bound to the object by calling the notify() method. It can also be regarded as an observer, because we have implemented the parent abstract class Subject, and some specific behaviors are implemented in the subclass.
  • Observer: Provides a unified abstract interface for all concrete observers. A concrete observer means its corresponding concrete implementation subclass. When we are notified by the observer, which is the subclass of Subject, we update ourselves.
  • ConcrereObserver: It is a subclass of observer. Each subclass will have its own corresponding implementation. For the update() method, that is to say, each subclass updates its own status after receiving the notification and is not affected by other subclasses.

1. 2 Code implementation

 /**
 * 观察者抽象接口
 */
public interface Observer {
    
    
    //将状态的更新通知给观察者具体的实现类
    void update(String state);
}

/**
 * 被观察者
 */
public abstract class Subject {
    
    

    //存放观察者对象引用
    private List<Observer> observers = new ArrayList<>();

    public void attach(Observer observer) {
    
    
        observers.add(observer);
        System.out.println("绑定观察者对象");
    }

    public void detach(Observer observer) {
    
    
        observers.remove(observer);
        System.out.println("解除绑定观察者");
    }

    /**
     * 通知观察者更新状态
     */
    public void notifyObserver(String state) {
    
    
        for (Observer observer : observers) {
    
    
            observer.update(state);
        }
    }
}

/**
 * 观察者具体的实现子类1
 */
public class SubOneObserverImpl implements Observer {
    
    
    @Override
    public void update(String state) {
    
    
        System.out.println("SubOneObserverImpl观察者接收到状态改变的消息:" + state);
    }
}
/**
 * 观察者具体的实现子类2
 */
public class SubTwoObserverImpl implements Observer {
    
    
    @Override
    public void update(String state) {
    
    
        System.out.println("SubTwoObserverImpl观察者接收到状态改变的消息:" + state);
    }
}
/**
 * 具体实现的被观察者子类
 */
public class SubOneSubject extends Subject {
    
    
    private String state = "subOneSubject";
    //发通知给绑定的观察者对象
    public void change(String state) {
    
    
        notifyObserver(state);
    }
}
public class Test {
    
    
    public static void main(String[] args) {
    
    
        Observer observer1 = new SubOneObserverImpl();
        Observer observer2 = new SubTwoObserverImpl();
        SubOneSubject subject = new SubOneSubject();
        subject.attach(observer1);
        subject.attach(observer2);

        subject.change("SubOneSubject");
    }
}

Take a look at the results of running the code:
Insert image description here

2. RxJava

2.1 Introduction

  • Observable: Observable (Subject).
  • Observer/Subscriber: Observer.
  • Subscribe: Subscribe.
  • Observable and Observer realize the subscription relationship through the subscribe0) method, which can be understood as binding and association.

2.2 Usage steps

Prerequisites for use: Add dependent libraries first

implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
implementation 'io.reactivex.rxjava3:rxjava:3.0.0'
  1. Create Observable (observable)
Observable<String> observable = Observable.create(new ObservableOnSubscribe<String>() {
    
    
    @Override
    public void subscribe(@NonNull ObservableEmitter<String> emitter) throws Throwable {
    
    
        emitter.onNext("hello");
        emitter.onNext("world");
        emitter.onComplete();
    }
});
  1. Create Observer
Observer<String> observer = new Observer<String>() {
    
    
    @Override
    public void onSubscribe(@NonNull Disposable d) {
    
    
        Log.e(TAG, "onSubscribe: " + d.isDisposed());
    }

    @Override
    public void onNext(@NonNull String s) {
    
    
        Log.e(TAG, "onNext: " + s);
    }

    @Override
    public void onError(@NonNull Throwable e) {
    
    
        Log.e(TAG, "onError: " + e.getLocalizedMessage());
    }

    @Override
    public void onComplete() {
    
    
        Log.e(TAG, "onComplete: ");
    }
};
  1. subscription
observable.subscribe(observer);

Let’s take a look at the effect of running a Demo

First write an xml layout file

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="test" />

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        android:textSize="40sp" />
</LinearLayout>

test code

public class MainActivity extends AppCompatActivity {
    
    
    private static final String TAG = MainActivity.class.getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = findViewById(R.id.btn);
        TextView textView = findViewById(R.id.tv);

        Observable<String> observable = Observable.create(new ObservableOnSubscribe<String>() {
    
    
            @Override
            public void subscribe(@NonNull ObservableEmitter<String> emitter) throws Throwable {
    
    
                emitter.onNext("hello");
                emitter.onNext("world");
                emitter.onComplete();
            }
        });

        Observer<String> observer = new Observer<String>() {
    
    
            @Override
            public void onSubscribe(@NonNull Disposable d) {
    
    
                Log.e(TAG, "onSubscribe: " + d.isDisposed());
            }

            @Override
            public void onNext(@NonNull String s) {
    
    
                Log.e(TAG, "onNext: " + s);
            }

            @Override
            public void onError(@NonNull Throwable e) {
    
    
                Log.e(TAG, "onError: " + e.getLocalizedMessage());
            }

            @Override
            public void onComplete() {
    
    
                Log.e(TAG, "onComplete: ");
            }
        };
        //设置一个按钮监听事件,点击之后就进行订阅。
        button.setOnClickListener(v -> {
    
    
            observable.subscribe(observer);
        });
    }
}

See what the results are like
Insert image description here

Okay, the above is the basic entry-level writing method of rxjava.

3. Schedulers thread control

Android cannot block the UI thread, so we should put some time-consuming operations into a worker thread that is not the UI thread.

Commonly used functions:

  • Schedulers.immediate(): Run directly on the current thread, which is equivalent to not specifying a thread. This is the default Scheduler.
  • Schedulers.newThread(): Always start a new thread and perform operations on the new thread.
  • Schedulers.io(): Scheduler used for I/O operations (reading and writing files, reading and writing databases, network information interaction, etc.). It is similar to newThread(), but the internal implementation of io() uses an unlimited thread pool to reuse idle threads, so in most cases io() is more efficient than newThread(). Don't put calculation work in io() to avoid creating unnecessary threads.
  • Schedulers.computation(): Scheduler used for calculation. This calculation refers to CPU-intensive calculations, that is, operations that will not limit performance by operations such as I/O. This Scheduler uses a fixed thread pool with a size equal to the number of CPU cores. Don't put I/O operations in computation(), otherwise the waiting time for I/O operations will waste CPU.
  • AndroidSchedulers.mainThread(): The operation it specifies will be run on the Android main thread.

Let’s use code to implement switching of network request threads and see what it looks like.

First, we use the Retrofit framework to access network request data. The Retrofit framework will not be analyzed in detail in this article. You can first understand how to use it. Here I will briefly introduce the usage in the Demo.

First add the corresponding dependencies

implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.0.2'

Define an interface. The function of this interface can be understood as storing the specific Url path corresponding to the access server and passing the corresponding parameters.

public interface TestApi {
    
    
    //定义登录的方法,并通过@Filed注解配置请求体参数

    @FormUrlEncoded//会对请求体的数据进行url编码
	//请求登录所需要的两个参数,用户名和密码
    @POST("login")
    Call<ResponseBody> login(@Field("username") String param1, @Field("password") String params2);
}

Write test code in MainActivity

public class MainActivity extends AppCompatActivity {
    
    
    private static final String TAG = MainActivity.class.getSimpleName();
    private Retrofit retrofit;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = findViewById(R.id.btn);
        TextView textView = findViewById(R.id.tv);
        retrofit = new Retrofit.Builder()
            //我们使用鸿洋大神的wanAndroid的开放api进行网络请求测试
                .baseUrl("https://www.wanandroid.com/user/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        TestApi testApi = retrofit.create(TestApi.class);
        //这是我的账号,大伙儿先整一个账号来使用。
        Call<ResponseBody> login = testApi.login("qfh", "qfhqfh1741");
        //按钮点击监听事件
       button.setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View v) {
    
    
                Observable.create(new ObservableOnSubscribe<ResponseBody>() {
    
    
                            @Override
                            public void subscribe(@NonNull ObservableEmitter<ResponseBody> emitter) {
    
    
                                Response<ResponseBody> execute = null;
                                try {
    
    
                                    execute = login.execute();
                                } catch (Exception e) {
    
    
                                    throw new RuntimeException(e);
                                }
                                emitter.onNext(execute.body());
                                emitter.onComplete();
                            }
                        }).subscribeOn(Schedulers.io())
                          .observeOn(AndroidSchedulers.mainThread())
                        .subscribe(new Observer<ResponseBody>() {
    
    
                            @Override
                            public void onSubscribe(@NonNull Disposable d) {
    
    

                            }

                            @Override
                            public void onNext(@NonNull ResponseBody responseBody) {
    
    
                                String responseData = null; // 使用保存的结果
                                try {
    
    
                                    responseData = responseBody.string();
                                } catch (Exception e) {
    
    
                                    throw new RuntimeException(e);
                                }
                                Log.e(TAG, "onNextqqfh: " + responseData);
                                textView.setText(responseData);
                            }

                            @Override
                            public void onError(@NonNull Throwable e) {
    
    
                                Log.e(TAG, "An error occurred: " + e.getMessage());
                            }

                            @Override
                            public void onComplete() {
    
    
                                Log.e(TAG, "onComplete: ");
                            }
                        });
            }
        });
    }
}

Corresponding xml file

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="test" />

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        android:textSize="20sp" />
</LinearLayout>

operation result:

Insert image description here

From the results of the screenshot, we can see that after requesting the network, the string returned to us is displayed on the UI, indicating that our network request this time was successful.

==========================================

3.1 Switch time-consuming operations to sub-threads

Next, let's look at an incorrect example. Let's slightly change the code:

button.setOnClickListener(new View.OnClickListener() {
    
    
    @Override
    public void onClick(View v) {
    
    
        Observable.create(new ObservableOnSubscribe<ResponseBody>() {
    
    
            @Override
            public void subscribe(@NonNull ObservableEmitter<ResponseBody> emitter) {
    
    
                Response<ResponseBody> execute = null;
                try {
    
    
                    execute = login.execute();
                } catch (Exception e) {
    
    
                    throw new RuntimeException(e);
                }
                emitter.onNext(execute.body());
                emitter.onComplete();
            }
        }).subscribe(new Observer<ResponseBody>() {
    
    
            @Override
            public void onSubscribe(@NonNull Disposable d) {
    
    

            }

            @Override
            public void onNext(@NonNull ResponseBody responseBody) {
    
    
                String responseData = null; // 使用保存的结果
                try {
    
    
                    responseData = responseBody.string();
                } catch (Exception e) {
    
    
                    throw new RuntimeException(e);
                }
                Log.e(TAG, "onNextqqfh: " + responseData);
                textView.setText(responseData);
            }

            @Override
            public void onError(@NonNull Throwable e) {
    
    
                Log.e(TAG, "An error occurred: " + e.getMessage());
            }

            @Override
            public void onComplete() {
    
    
                Log.e(TAG, "onComplete: ");
            }
        });
    }
});

Insert image description here

When we look at the results, it is obvious that our UI has not updated the corresponding data information of the request network, and if you carefully observe the log, you will find that after we subscribe, the onError method is executed. As we mentioned earlier, the onError and onComplete methods are mutually exclusive, and only one of them will be executed. Here we output this log Log.e(TAG, "An error occurred: " + e.getMessage());, indicating that an error occurred during the execution of the code, and it was thrown An exception occurred, but we caught the exception, so the program did not crash, and the log did not execute the onComplete() method.

Why is this? Let's study it

The exception information is also very obvious, which means that time-consuming operations such as network requests cannot be called on the main thread. From this exception, we can also deduce that the code in subscribe() is executed in the main thread of Android, then we should add this line of code.

//将网络请求切换至IO线程处理
.subscribeOn(Schedulers.io())

Schedulers.io(): will open a thread pool and call our network request code in the child thread.

After adding this line of code, let's run it again and see.

button.setOnClickListener(new View.OnClickListener() {
    
    
    @Override
    public void onClick(View v) {
    
    
        Observable.create(new ObservableOnSubscribe<ResponseBody>() {
    
    
                    @Override
                    public void subscribe(@NonNull ObservableEmitter<ResponseBody> emitter) {
    
    
                        Response<ResponseBody> execute = null;
                        try {
    
    
                            execute = login.execute();
                        } catch (Exception e) {
    
    
                            throw new RuntimeException(e);
                        }
                        emitter.onNext(execute.body());
                        emitter.onComplete();
                    }
                }).subscribeOn(Schedulers.io())
                .subscribe(new Observer<ResponseBody>() {
    
    
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {
    
    

                    }

                    @Override
                    public void onNext(@NonNull ResponseBody responseBody) {
    
    
                        String responseData = null; // 使用保存的结果
                        try {
    
    
                            responseData = responseBody.string();
                        } catch (Exception e) {
    
    
                            throw new RuntimeException(e);
                        }
                        textView.setText(responseData);
                    }

                    @Override
                    public void onError(@NonNull Throwable e) {
    
    
                        Log.e(TAG, "An error occurred: " + e.getMessage());
                    }

                    @Override
                    public void onComplete() {
    
    
                        Log.e(TAG, "onComplete: ");
                    }
                });
    }
});

Take a look at the result after the change:

Insert image description here

We found that exception information was still thrown, and our UI was indeed not updated. This exception means that the UI must be updated on the main thread of Android, so we need to add this code to update the UI.

3.2 Update ui and switch back to the main thread

.observeOn(AndroidSchedulers.mainThread())

After you add this line of code, you can run it yourself and see.

summary:

  • When we switch threads without adding the subscribeOn code, our code actually runs in the main thread of Android.
  • After adding subscribeOn, our code will be switched to the sub-thread for execution. Then when updating the UI, we can only switch the observer back to the main thread to update the UI. This line of code observeOn means that the observer and the observed cannot be in the same thread.
  • observeOn() specifies the observer thread, and subscribeOn() specifies the observed thread.

Guess you like

Origin blog.csdn.net/weixin_46039528/article/details/130041265