RxJava系列之線程切換實戰

系列文章

RxJava系列之簡介和觀察者設計模式
RxJava系列之上游與下游
RxJava系列之常用創建型操作符
RxJava系列之常用變換操作符
RxJava系列之常用過濾操作符
RxJava系列之常用條件操作符
RxJava系列之常用合併操作符
RxJava系列之常用異常操作符
RxJava系列之線程切換實戰
RxJava系列之背壓模式
RxJava系列之配合Retrofit
RxJava系列之泛型高級
RxJava系列之手寫create操作符
RxJava系列之手寫create操作符增加泛型限定
RxJava系列之手寫just操作符
RxJava系列之手寫map操作符
RxJava系列之手寫切換線程

線程切換

前面的文章都是寫的demo代碼,本篇文章可以實戰使用RxJava的方式加載網絡圖片。RxJava可以方便的設置上游和下游的執行線程,從而達到異步處理的作用。

如何切換線程

RxJava默認執行在主線程。如果需要切換上游執行線程,使用subscribeOn操作符。如果需要切換下游執行線程,使用observeOn操作符。

 /**
     * todo 異步線程區域
     * Schedulers.io() :代表io流操作,網絡操作,文件流,耗時操作
     * Schedulers.newThread()    : 比較常規的,普普通通
     * Schedulers.computation()  : 代表CPU 大量計算 所需要的線程
     *
     * todo main線程 主線程
     * AndroidSchedulers.mainThread()  : 專門爲Android main線程量身定做的
     *
     * todo 給上游分配多次,只會在第一次切換,後面的不切換了(忽略)
     * todo 給下游分配多次,每次都會去切換
     *
     * @param view
     */
    public void r01(View view) {
        // RxJava如果不配置,默認就是主線程main
        // 上游
        Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> e) throws Exception {
                Log.d(TAG, "上游 subscribe: " + Thread.currentThread().getName());

                e.onNext("");
            }
        }).subscribeOn(Schedulers.io()) // todo 給上游配置異步線程    // 給上游分配多次,只會在第一次切換,後面的不切換了
                .subscribeOn(AndroidSchedulers.mainThread()) // 被忽略
                .subscribeOn(AndroidSchedulers.mainThread()) // 被忽略
                .subscribeOn(AndroidSchedulers.mainThread()) // 被忽略
                .subscribeOn(AndroidSchedulers.mainThread()) // 被忽略
                // result: io 異步線程

          .observeOn(AndroidSchedulers.mainThread()) // todo 給下游配置 安卓主線程    // 給下游分配多次,每次都會去切換
                .observeOn(AndroidSchedulers.mainThread()) // 切換一次線程
                .observeOn(AndroidSchedulers.mainThread()) // 切換一次線程
                .observeOn(AndroidSchedulers.mainThread()) // 切換一次線程
                .observeOn(Schedulers.io()) // 切換一次線程
                // result: io 異步線程

                .subscribe(new Consumer<String>() { // 下游簡化版
            @Override
            public void accept(String s) throws Exception {
                Log.d(TAG, "下游 subscribe: " + Thread.currentThread().getName());
            }
        });
    }

是否指定異步線程的影響

如果未指定異步線程,則上游發一次,下游接收一次。上游發一次,下游接收一次。
如果指定了異步線程,則符合多線程的執行順序,具有不確定性。

/**
     * todo 同步 和 異步 執行流程
     * @param view
     */
    public void r02(View view) {

        // TODO 默認情況下  上游和下游都是main線程的情況下
        // 上游
        /*Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> e) throws Exception {
                Log.d(TAG, "subscribe: 上游發送了一次 1 ");
                e.onNext(1);

                Log.d(TAG, "subscribe: 上游發送了一次 2 ");
                e.onNext(2);

                Log.d(TAG, "subscribe: 上游發送了一次 3 ");
                e.onNext(3);
            }
        }).subscribe(new Consumer<Integer>() { // 下游簡化版
            @Override
            public void accept(Integer integer) throws Exception {
                Log.d(TAG, "下游 accept: " + integer);
            }
        });*/

        /**
         * 默認情況下 同步的想象
         * 09-05 16:23:46.064 8088-8088/com.netease.rxjavastudy D/MainActivity8: subscribe: 上游發送了一次 1
         * 09-05 16:23:46.064 8088-8088/com.netease.rxjavastudy D/MainActivity8: 下游 accept: 1
         * 09-05 16:23:46.064 8088-8088/com.netease.rxjavastudy D/MainActivity8: subscribe: 上游發送了一次 2
         * 09-05 16:23:46.064 8088-8088/com.netease.rxjavastudy D/MainActivity8: 下游 accept: 2
         * 09-05 16:23:46.064 8088-8088/com.netease.rxjavastudy D/MainActivity8: subscribe: 上游發送了一次 3
         * 09-05 16:23:46.064 8088-8088/com.netease.rxjavastudy D/MainActivity8: 下游 accept: 3
         */






        // TODO 配置好異步線程
        Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> e) throws Exception {
                Log.d(TAG, "subscribe: 上游發送了一次 1 ");
                e.onNext(1);

                Log.d(TAG, "subscribe: 上游發送了一次 2 ");
                e.onNext(2);

                Log.d(TAG, "subscribe: 上游發送了一次 3 ");
                e.onNext(3);
            }
        }).subscribeOn(Schedulers.io()) // 給上游分配 異步線程
          .observeOn(AndroidSchedulers.mainThread()) // 給下游分配 主線程

          .subscribe(new Consumer<Integer>() { // 下游簡化版
            @Override
            public void accept(Integer integer) throws Exception {
                Log.d(TAG, "下游 accept: " + integer);
            }
        });
        /**
         * 配置了 異步後
         * 09-05 16:26:03.574 9547-9690/com.netease.rxjavastudy D/MainActivity8: subscribe: 上游發送了一次 1
         * 09-05 16:26:03.574 9547-9690/com.netease.rxjavastudy D/MainActivity8: subscribe: 上游發送了一次 2
         * 09-05 16:26:03.574 9547-9690/com.netease.rxjavastudy D/MainActivity8: subscribe: 上游發送了一次 3
         * 09-05 16:26:03.574 9547-9547/com.netease.rxjavastudy D/MainActivity8: 下游 accept: 1
         * 09-05 16:26:03.574 9547-9547/com.netease.rxjavastudy D/MainActivity8: 下游 accept: 2
         * 09-05 16:26:03.575 9547-9547/com.netease.rxjavastudy D/MainActivity8: 下游 accept: 3
         */
    }

常規方式加載網絡圖片

使用常規方式加載圖片,代碼四分五裂,沒有串起來。增加或刪減功能維護比較困難。

private final String PATH = "http://pic33.nipic.com/20131007/13639685_123501617185_2.jpg";

    /**
     * todo 不使用RxJava去下載圖片
     * @param view
     */
    public void r03(View view) {

        progressDialog = new ProgressDialog(this);
        progressDialog.setMessage("正在下載中...");
        progressDialog.show();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);

                    URL url = new URL(PATH);
                    URLConnection urlConnection = url.openConnection();
                    HttpURLConnection httpURLConnection = (HttpURLConnection) urlConnection;
                    httpURLConnection.setConnectTimeout(5000);
                    int responseCode = httpURLConnection.getResponseCode();
                    if (HttpURLConnection.HTTP_OK == responseCode) {
                       Bitmap bitmap = BitmapFactory.decodeStream(httpURLConnection.getInputStream());
                       Message message = handler.obtainMessage();
                       message.obj = bitmap;
                       handler.sendMessage(message);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            Bitmap bitmap = (Bitmap) msg.obj;
            imageView.setImageBitmap(bitmap);

            // 隱藏加載框
            if (progressDialog != null)
                progressDialog.dismiss();
            return false;
        }
    });

RxJava的方式加載網絡圖片

加載網絡圖片這件事被封裝成一個事件流,增加與刪減功能維護起來非常清楚。

   /**
     * todo 使用RxJava去下載圖片
     * @param view
     */
    public void r04(View view) {

        // 起點

        // 上游 被觀察者 Observable
        Observable.just(PATH)  // 內部發射

        // 加載網路圖片,String Path  變換  Bitmap
        .map(new Function<String, Bitmap>() {
            @Override
            public Bitmap apply(String s) throws Exception {

                try {
                    Thread.sleep(2000);

                    URL url = new URL(PATH);
                    URLConnection urlConnection = url.openConnection();
                    HttpURLConnection httpURLConnection = (HttpURLConnection) urlConnection;
                    httpURLConnection.setConnectTimeout(5000);
                    int responseCode = httpURLConnection.getResponseCode();
                    if (HttpURLConnection.HTTP_OK == responseCode) {
                        Bitmap bitmap = BitmapFactory.decodeStream(httpURLConnection.getInputStream());
                        return bitmap;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

                return null;
            }
        })
.		// 給圖片加水印
        .map(new Function<Bitmap, Bitmap>() {
            @Override
            public Bitmap apply(Bitmap bitmap) throws Exception {
                // 給圖片加水印
                Paint paint = new Paint();
                paint.setColor(Color.RED);
                paint.setTextSize(30);
                Bitmap bitmapSuccess = drawTextToBitmap(bitmap, "同學們大家好", paint, 60, 60);
                return bitmapSuccess;
            }
        })

        // 比如:增加一個 日誌紀錄功能,只需要添加要給 變換操作符
        .map(new Function<Bitmap, Bitmap>() {
            @Override
            public Bitmap apply(Bitmap bitmap) throws Exception {
                Log.d(TAG, "apply: 下載的Bitmap 是這個樣子的" + bitmap);
                return bitmap;
            }
        })

        .subscribeOn(Schedulers.io()) // todo  給上游分配 異步線程
        .observeOn(AndroidSchedulers.mainThread()) // todo  給下游分配 主線程

        .subscribe(new Observer<Bitmap>() { // 下游
            @Override
            public void onSubscribe(Disposable d) {
                progressDialog = new ProgressDialog(MainActivity8.this);
                progressDialog.setMessage("RxJava下載圖片中..");
                progressDialog.show();
            }

            @Override
            public void onNext(Bitmap bitmap) {
                if (imageView != null)
                    imageView.setImageBitmap(bitmap);
            }

            @Override
            public void onError(Throwable e) { // 發生異常
               //  if (imageView != null)
                   //  imageView.setImageResource(R.mipmap.ic_launcher); // 下載錯誤的圖片
            }

            @Override
            public void onComplete() { // 終點
                if (progressDialog != null)
                    progressDialog.dismiss();
            }
        });
    }


    //圖片上繪製文字
    private Bitmap drawTextToBitmap(Bitmap bitmap, String text, Paint paint, int paddingLeft, int paddingTop) {
        Bitmap.Config bitmapConfig = bitmap.getConfig();

        paint.setDither(true); // 獲取跟清晰的圖像採樣
        paint.setFilterBitmap(true);// 過濾一些
        if (bitmapConfig == null) {
            bitmapConfig = Bitmap.Config.ARGB_8888;
        }
        bitmap = bitmap.copy(bitmapConfig, true);
        Canvas canvas = new Canvas(bitmap);

        canvas.drawText(text, paddingLeft, paddingTop, paint);
        return bitmap;
    }

總結

1.異步線程區域
Schedulers.io() :代表io流操作,網絡操作,文件流,耗時操作
Schedulers.newThread() : 比較常規的,普普通通
Schedulers.computation() : 代表CPU 大量計算 所需要的線程
2.AndroidSchedulers.mainThread() : 專門爲Android main線程量身定做的
3.給上游分配多次,只會在第一次切換,後面的不切換了(忽略)
4.給下游分配多次,每次都會去切換
5.如果不配置異步線程,上游發一次,下游接收一次,上游發一次,下游接收一次,上游發一次,下游接收一次
6.配置好異步線程,就是異步的表現
7.傳統下載圖片的寫法,容易四分五裂
8.RxJava下載圖片,基於事件流編程,一條鏈子,起點和終點

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章