RxJava2学習チュートリアル(2)スレッドのスケジューリング

スレッドスケジューリングはスレッドスイッチングとも呼ばれます。Rxjavaでは、デフォルトのルールでは、問題(オブザーバーによって完了)と消費(オブザーバーによって完了)が同じスレッドにありますが、実際の開発では、同様の状況が発生しました。サブスレッドで時間のかかる操作を行ってから、メインスレッドに戻ってUI操作を行います。この場合、明らかにスレッドを切り替える必要があり、それをどのように完了するのでしょうか。このセクションで説明します。

記事の手がかり:

  • Observable(observed)およびObserver()default worker thread
  • オブザーバーとオブザーバーの異なるスレッドを切り替える方法
  • ObserverOn()とSubscribeOn()について何を知っていますか?
  • Rxjavaにはどのスレッドが組み込まれていますか?違いは何ですか?
  • 実際の戦闘を通じてRxjava2でのスレッドのスケジューリングを理解して学習する

スレッドを切り替える方法

ObservableとObserverはデフォルトでメインスレッドで実行されます
  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");
            }
        });

結果は次のとおりです。

GfW9Pg.png

コードからわかるように、ObservableおよびObserverが配置されているスレッドが指定されていない場合、それらはすべてデフォルトでメインスレッドで動作し、オブザーバーはそれぞれメインスレッドで整数データの1、2、3、および4イベントを発行します。メインスレッドは、イベントが完了するまで順番に受け取ります。

オブザーバーとオブザーバーを切り替える方法

上記の例では、デフォルトで、オブザーバーとオブザーバーの両方がメインスレッドで動作します。つまり、メインスレッドがデータを送信し、メインスレッドが受信します。オブザーバーが子スレッドでデータを送信し、メインスレッドのオブザーバーが送信することを認識する必要がある場合スレッドで受信すると、RxJavaの組み込みスレッドスケジューラを介して簡単に実現できます。実装コードは次のとおりです。

   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: 下游观察者接收完毕");
              }
          });

結果は次のとおりです。

上流のオブザーバーは3秒ごとにIOスレッドでイベントを送信し、下流のオブザーバーはメインスレッドでイベントを受信します。このプロセスでは、次の2行のコードが追加されます。

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

コードの最初の行はsubscribeOnを介してオブザーバーのスレッドを指定し、コードの2行目はオブザーバーのスレッドを指定します。問題は、RxJavaに組み込まれているスレッドスケジューラは何ですか?

スレッドスケジューラの種類
スケジューラーのタイプ 効果
AndroidSchedulers.mainThread() メインスレッド、UIスレッドは、インターフェイスを更新するために使用できます。
Schedulers.newThread() タスクごとに新しいスレッドを作成する
Schedulers.io() スレッドキャッシュメカニズムを使用して、SDカードファイルの読み取りと書き込み、データベースのクエリ、ネットワークへのアクセスなど、IO集中型の操作に使用されます。スケジューラがタスクを受信した後、まずスレッドキャッシュプールにアイドルスレッドがあるかどうかを確認します使用する場合は再利用し、使用しない場合は新しいスレッドを作成してスレッドプールに追加します。アイドルスレッドが毎回使用されない場合は、上限なしで新しいスレッドを作成できます。
Schedulers.computation() CPUを集中的に使用するコンピューティングタスク、つまりI / Oや他の操作(xmlおよびjsonファイルの分析、ビットマップイメージの圧縮サンプリングなど)によって制限されない時間がかかる操作、および固定スレッドプールとCPUコアで使用されます。番号。I / O操作の待機時間がCPUを浪費するため、I / O操作には使用できません。
Schedulers.from(executor) 指定したエグゼキュータをスケジューラとして使用します
Schedulers.immediate() 現在のスレッドでタスクをすぐに開始します(Rxjava2では非推奨)。この関数はRxJava2のSchedulers.trampoline()と同等です。
Schedulers.trampoline() タスクは現在のスレッドですぐに実行されます。現在のスレッドにタスクがある場合、タスクは中断されます。挿入されたタスクが実行された後、未完了のタスクが実行されます。(カットイン)
subscribeOn()和ObserveOn()
  • Rxjavaは、どのスレッド上の各オブザーバブルオブジェクトのオペレーターにsubscribeOn()メソッドを提供し、どのスレッド上の各サブスクライバー(Observer)オブジェクトのオペレーターにObserveOn()メソッドを提供します
  • SubscribeOn()は、スレッドが切り替えられたときに1回だけ実行されます。複数のオカレンスがある場合、最初のオカレンスに使用されたスレッドが優先されます。observeOn()を複数回呼び出して、指定した別のスレッドに切り替えることができます
subscribeOn()を2回呼び出し、ObserverOn()を1回呼び出します

subscribeOn()を2回使用すると、最初に指定されたスレッドのみがメインスレッドになります。コードは次のとおりです。

  //使用两次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);
                    }
                });

結果は次のとおりです。

ご覧のとおり、監視対象の人物が2度いるスレッドを指定しましたが、最終的には主にメインスレッドにマップオペレーターが初めて印刷されます。

.subscribeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
subscribeOn()を1回呼び出し、observeOn()を2回呼び出します
 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() {

                    }
                });

コードを実行した結果は次のとおりです。

ダウンストリームオブザーバーが以前にIOスレッドにあり、その後メインスレッドに切り替えられたことがわかります。メインキーコードは次のとおりです。

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

したがって、observeOn()が2回呼び出されると、ダウンストリームオブザーバーのスレッドが2回切り替えられます。

練習

要件:登録およびログインの場合、ユーザーが正常に登録された後に自動ログインの操作を実装する必要があります。
素材:
  • 背景:https://wanandroid.com/ウェブサイトの登録とログインを例にとると、登録後の自動ログイン機能を実現する必要があります
  • インターフェース:
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);
}
オリジナルの処理方法:
 //登录逻辑
 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("注册失败");
                      }
                  });
   }

スレッドスケジューリングを学習する前に、登録とログインを別々に実装する必要があり、実行効率を考慮しないでください。コードの処理は非常に面倒です。2つのメソッドを記述し、2つのモジュールを処理する必要があります。効果に影響を与えずに上記のコードを最適化する方法は何ですか?次のコードを見てください:

現在の治療法(改善)
  //使用接口请求对象创建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() {

             }
         });

コードからわかるように、最初に登録結果をIOスレッドで処理します。登録が成功した場合、既存のIOスレッドが直接使用されてログイン操作要求が実行されます。登録が失敗した場合、結果はメインスレッドに返されて処理されます。コードは簡単です、ロジックはシンプルです。

最後

ここではRxjava2のスレッドスケジューリングについて学び、引き続き関連する知識を共有します。次の記事では、Rxjava2の演算子について説明します。はい、はい、これは上記のコードのcreate()、flatMap()などに似ています。等

オリジナルの記事を8件公開 Like9 Visit 6200

おすすめ

転載: blog.csdn.net/DolphKong/article/details/105688517