RxJava2 learning tutorial (2) thread scheduling

Thread scheduling is also called thread switching. In Rxjava, the default rule is that the issue (completed by the observer) and consumption (completed by the observer) are in the same thread, but in actual development, we often will Encountered a similar situation: time-consuming operations in the sub-thread, and then back to the main thread for UI operations, in this case obviously need to switch the thread, then how do we complete it? This section will explain.

Article clues:

  • Observable (observed) and Observer () default worker thread
  • How to switch between different threads for observer and observer
  • What do we know about ObserverOn () and SubscribeOn ()?
  • What threads are built into Rxjava? What is the difference?
  • Understand and learn the scheduling of threads in Rxjava2 through actual combat

How to switch threads

Observable and Observer are executed in the main thread by default
  Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
                emitter.onNext(1);
                emitter.onNext(2);
                emitter.onNext(3);
                emitter.onNext(4);
                Log.d(TAG, "Observable thread is : " + Thread.currentThread().getName());
                emitter.onComplete();
            }
        }).subscribe(new Observer<Integer>() {

            @Override
            public void onSubscribe(Disposable d) {
                Log.d(TAG, "onSubscribe");
            }

            @Override
            public void onNext(Integer integer) {
                Log.d(TAG, "Observer thread is :" + Thread.currentThread().getName());
                Log.d(TAG, "onNext: " + integer);

            }

            @Override
            public void onError(Throwable e) {

            }

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

The results are as follows:

GfW9Pg.png

It can be seen from the code that when the thread where Observable and Observer are located is not specified, they all work on the main thread by default. The main thread receives in turn until the event is completed.

How to switch between observer and observer

In the above example, by default, the observer and the observer both work on the main thread, that is, the main thread sends data and the main thread receives. If we need to implement the observation to send data in the child thread, then the observer Receive in the thread, then it can be easily achieved through the built-in thread scheduler of RxJava, the implementation code is as follows:

   Observable.create(new ObservableOnSubscribe<Integer>() {
            @SuppressLint("LongLogTag")
            @Override
            public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
                for (int i = 0; i < 4; i++) {
                    Log.e(TAG, "上游被观察者Observable在子线程为" + Thread.currentThread().getName() + "生产一个事件: " + i );
                    emitter.onNext(i);
                    SystemClock.sleep(3000);
                }
                emitter.onComplete();
            }
        }).subscribeOn(Schedulers.newThread())
          .observeOn(AndroidSchedulers.mainThread())
          .subscribe(new Observer<Integer>() {
              @Override
              public void onSubscribe(Disposable d) {

              }

              @SuppressLint("LongLogTag")
              @Override
              public void onNext(Integer integer) {
                  Log.d(TAG, "下游观察者Observer在主线程:" + Thread.currentThread().getName()+"onNext事件中接收了:"+integer);

              }

              @Override
              public void onError(Throwable e) {

              }

              @SuppressLint("LongLogTag")
              @Override
              public void onComplete() {
                  Log.d(TAG, "onComplete: 下游观察者接收完毕");
              }
          });

The results are as follows:

The upstream observer sends an event in the IO thread every 3 seconds, and the downstream observer receives it in the main thread. In the process, the following two lines of code are added:

.subscribeOn(Schedulers.newThread())   
.observeOn(AndroidSchedulers.mainThread())

The first line of code specifies the thread of the observer through subscribeOn, and the second line of code specifies the thread of the observer. So the question is, what thread scheduler is built into RxJava?

Types of thread schedulers
Type of scheduler effect
AndroidSchedulers.mainThread( ) The main thread, UI thread, can be used to update the interface
Schedulers.newThread( ) Create a new thread for each task
Schedulers.io( ) Used for IO-intensive operations, such as reading and writing SD card files, querying databases, accessing the network, etc., with a thread caching mechanism. After the scheduler receives a task, it first checks whether there are idle threads in the thread cache pool. If yes, then reuse. If not, create a new thread and add it to the thread pool. If no idle thread is used every time, you can create a new thread without an upper limit.
Schedulers.computation( ) Used for CPU-intensive computing tasks, that is, time-consuming operations that will not be limited by I / O and other operations, such as analysis of xml and json files, compression sampling of Bitmap images, etc., with a fixed thread pool and a CPU core size number. It cannot be used for I / O operations because the waiting time for I / O operations wastes CPU.
Schedulers.from(executor) Use the specified Executor as the scheduler
Schedulers.immediate( ) Start the task immediately in the current thread (deprecated in Rxjava2), the function is equivalent to Schedulers.trampoline () in RxJava2
Schedulers.trampoline( ) The task is executed immediately in the current thread. If there is a task in the current thread, it will be suspended. After the inserted task is executed, the unfinished task will be executed. (Cut in line)
subscribeOn()和ObserveOn()
  • Rxjava provides subscribeOn () method for each observable object's operator on which thread; ObserveOn () method for each Subscriber (Observer) object's operator on which thread
  • SubscribeOn () is only executed once when the thread is switched. If there are multiple occurrences, the thread used for the first occurrence shall prevail. observeOn () can be called multiple times and switch to a different thread specified
Call subscribeOn () twice and ObserverOn () once

When we use subscribeOn () twice, only the thread specified for the first time will be the main one. The code is as follows:

  //使用两次subscribeOn() 和 一次observeOn()的线程调度
        //通过两次设置subscribeOn()发射和处理数据在不同的线程,但是最终起作用的是第一次设置的工作线程
        //由此可以得出多次设置subscribeOn()设置被观察者Observable工作的线程最终起作用的是第一次设置的线程
        Observable.create(new ObservableOnSubscribe<Integer>() {
            @SuppressLint("LongLogTag")
            @Override
            public void subscribe(ObservableEmitter<Integer> e) throws Exception {
                for (int i = 0; i < 4; i++) {
                    Log.d(TAG, "上游被观察者Observable在" + Thread.currentThread().getName() + "线程, 生产一个事件: " + i );
                    SystemClock.sleep(1000);
                    e.onNext(i);
                }
                e.onComplete();
            }
        }).subscribeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.newThread())
                .map(new Function<Integer, Integer>() {
                    @SuppressLint("LongLogTag")
                    @Override
                    public Integer apply(@NonNull Integer integer) throws Exception {
                        Log.d(TAG, "map操作符在" + Thread.currentThread().getName() + "线程处理了事件: " + integer);
                        return integer * 2;
                    }
                })
                .observeOn(Schedulers.io())
                .subscribe(new Consumer<Integer>() {
                    @SuppressLint("LongLogTag")
                    @Override
                    public void accept(@NonNull Integer integer) throws Exception {
                        Log.d(TAG, "下游观察者Observer在IO线程" + Thread.currentThread().getName() + "接收响应到了事件: " + integer);
                    }
                });

The results are as follows:

As you can see, we specified the thread where the observed person is located twice, but in the end, the map operator is printed on the main thread mainly for the first time.

.subscribeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
Call subscribeOn () once and observeOn () twice
 Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
                emitter.onNext(1);
                emitter.onNext(2);
                emitter.onNext(3);
                LogUtil.d("上游被观察者所在IO线程为:"+Thread.currentThread().getName());
            }
        }).subscribeOn(Schedulers.io())
                .observeOn(Schedulers.io())
                .map(new Function<Integer, String>() {
                    @Override
                    public String apply(Integer integer) throws Exception {
                        LogUtil.d("下游观察者线程为"+Thread.currentThread().getName());
                        return 2*integer+"";
                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<String>() {
                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onNext(String s) {
                        LogUtil.d("onNext():下游观察者所在线程为"+Thread.currentThread().getName());
                    }

                    @Override
                    public void onError(Throwable e) {
                    }
                    @Override
                    public void onComplete() {

                    }
                });

The result of running the code is as follows:

It can be seen that the downstream observer was in the IO thread before, and then switched to the main thread. The main key code is:

  .observeOn(Schedulers.io())
  .observeOn(AndroidSchedulers.mainThread())

So when observeOn () is called twice, the thread of the downstream observer will be switched twice.

practice

Requirements: In the case of registration and login, we need to implement the operation of automatic login after the user is successfully registered.
Material:
  • Background: Taking the registration and login of https://wanandroid.com/ website as an example, it is necessary to realize the automatic login function after registration
  • interface:
public interface ApiService {
    /**
    * 注册
    * */

    @FormUrlEncoded
    @POST( "user/register")
    Observable<RegisterResp> register(@Field("username")String username, @Field("password")String password, @Field("repassword")String repassword);

    /**
     * 登录
     */
    @FormUrlEncoded
    @POST("user/login")
    Observable<LoginResponse> login(@Field("username") String name, @Field("password") String password);
}
Original processing method:
 //登录逻辑
 private void originLogin(){
       apiService.login(username, password)
               .subscribeOn(Schedulers.io())
               .observeOn(AndroidSchedulers.mainThread())  //回到主线程去处理请求结果
               .subscribe(new Consumer<LoginResponse>() {
                   @Override
                   public void accept(LoginResponse loginResponse) throws Exception {
                       ToastUtil.showToast("登录成功");

                   }
               },new Consumer<Throwable>() {
                   @Override
                   public void accept(Throwable throwable) throws Exception {
                       ToastUtil.showToast("登录失败");
                   }
               });
   }
   
   // 注册后自动登录逻辑
   private void regisLogin(){
       apiService.register(username,password,repassword)
                  .subscribeOn(Schedulers.io()) //在IO线程进行网络请求
                  .observeOn(AndroidSchedulers.mainThread()) 
                  .subscribe(new Consumer<RegisterResp>() {
                      @Override
                      public void accept(RegisterResp registerResp) throws Exception {
                          LogUtil.d("registCode:"+registerResp.getErrorCode());
                          if (-1==registerResp.getErrorCode()){
                             ToastUtil.showToast("用户名已注册");
                          }else {
                              ToastUtil.showToast("注册成功。开始登录");
                              originLogin();
                          }
                      }
                  }, new Consumer<Throwable>() {
                      @Override
                      public void accept(Throwable throwable) throws Exception {
                          ToastUtil.showToast("注册失败");
                      }
                  });
   }

It can be seen that before learning thread scheduling, we need to implement registration and login separately, and do not consider the execution efficiency. The processing of the code is very cumbersome. Two methods need to be written and two modules are processed. Is there any What is the way to optimize the above code without affecting the effect, and look at the following code:

Current treatment (improved)
  //使用接口请求对象创建call对象
        apiService.register(username,password,repassword)
            .subscribeOn(Schedulers.io()) ////在IO线程进行注册和登录的网络请求
             .flatMap(new Function<RegisterResp, ObservableSource<LoginResponse>>() {
            @Override
            public ObservableSource<LoginResponse> apply(RegisterResp registerResp) throws Exception {
              if (-1==registerResp.getErrorCode()){
                  return Observable.error(new Throwable("用户名已经注册"));
              }else if ( -1001==registerResp.getErrorCode()){
                  return Observable.error(new Throwable("注册失败"));
              }

                return api.login(username, password);
            }
        }).observeOn(AndroidSchedulers.mainThread()) // 回到主线程处理
         .subscribe(new Observer<LoginResponse>() {
             @Override
             public void onSubscribe(Disposable d) {

             }

             @Override
             public void onNext(LoginResponse loginResponse) {
                 Toast.makeText(MainActivity.this, "恭喜你登录成功", Toast.LENGTH_SHORT).show();
             }

             @Override
             public void onError(Throwable e) {
                 if ("注册失败".equals(e.getMessage())){
                     Toast.makeText(MainActivity.this, "注册失败", Toast.LENGTH_SHORT).show();
                 }else if (("用户名已经注册".equals(e.getMessage()))){
                     Toast.makeText(MainActivity.this, "用户名已经注册", Toast.LENGTH_SHORT).show();
                     }
                 }

             @Override
             public void onComplete() {

             }
         });

As you can see from the code, we will first process the registration results in the IO thread. If the registration is successful, the existing IO thread is used directly to perform the login operation request. If the registration fails, the result is returned to the main thread for processing. The code is concise , The logic is simple.

At last

We will learn about thread scheduling in Rxjava2 here, and we will continue to share related knowledge later. The next article will explain the operators in Rxjava2. Yes, yes, it is similar to create (), flatMap (), etc. in the above code. Wait.

Published 8 original articles · Like9 · Visit 6200

Guess you like

Origin blog.csdn.net/DolphKong/article/details/105688517