RxJava使用總結

weixin_34290000發表於2018-01-30

原部落格地址

前言

RxJava出來已經很久了, 但我至今對它仍然不是很熟悉, 今天就係統的總結一下RxJava的常見用法和操作符.
首先開啟RxJava在github的主頁
https://github.com/ReactiveX/RxJava
點進去檢視介紹, 可以看到都是英文的.說點題外話, 以前我也是比較害怕英文文件的, 但是最近有點變化, 大概閱讀原始碼的時候發現把原始碼上的英文註釋看懂之後再去看原始碼遠遠比直接看原始碼去理解容易得多.英文一遍讀不懂可以多讀幾遍, 就當做閱讀理解一樣, 只要備好工具(網易有道詞典什麼的), 理解句子中所有單詞的意思, 最終理解出來的意思一般和原本表達的意思差不到哪裡去的.如果像大段的英文文件, 就先用google翻譯把整個頁面翻譯一下, 通讀一遍找到你最感興趣的資訊(因為一般我們都是在一大段文章中找一個點而已), 找到之後恢復成英文頁面再去閱讀那個點.
比如我想找到rxjava最新的版本號, 我就找到了這裡

1626396-d2a391b544d832b3.png

文件說關於maven, gradle等依賴資訊可以在超連結中找到.然後以我就點選那個超連結


1626396-9bd57ea8ee89970d.png

可以看到RxJava1最新的版本號是1.3.4(本篇只討論RxJava1)
以下是我的參考資料:
給 Android 開發者的 RxJava 詳解
可能是東半球最全的RxJava使用場景小結

一些基礎操作

關於rxjava的觀察者模式, 舉個例子, 就像電燈和開關.檯燈作為觀察者, 始終觀察著開關的一舉一動, 而開關就作為被觀察者.開關一旦開啟, 檯燈就亮起;開關一旦關閉, 檯燈就熄滅.

建立被觀察者Observable

基於上面的例子, 我們把Observable(被觀察者)取名為switcher(開關)

        // 開關作為被觀察者
        Observable<String> switcher = Observable.create(new Observable.OnSubscribe<String>() {
            @Override
            public void call(Subscriber<? super String> subscriber) {
                subscriber.onNext("on");
                subscriber.onNext("off");
                subscriber.onNext("on");
                subscriber.onNext("on");
                subscriber.onCompleted();
            }
        });

對於建立Observable還有兩種更"偷懶"的方式

        // 建立被觀察者的偷懶模式1
        Observable<String> switcher = Observable.just("on", "off", "on", "on");
        // 建立被觀察者的偷懶模式2
        String[] k = {"on", "off", "on", "on"};
        Observable<String> switcher2 = Observable.from(k);

建立觀察者Observer

我們把Observer(觀察者)取名為light(電燈)

        // 電燈作為觀察者, 對始終在觀察者開關的動作, 對開關的動作而做出相應的反應
        Subscriber<String> light = new Subscriber<String>() {
            @Override
            public void onCompleted() {
                Log.d(TAG, "onCompleted: ");
            }

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

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

然後是被觀察者訂閱了觀察者(這個邏輯和我們通常認為的不符, 但是方便了API的設計), 被觀察者會持有觀察者的引用

switcher.subscribe(light);

錯誤處理

        Observable<Integer> observable = Observable.create(new Observable.OnSubscribe<Integer>() {
            @Override
            public void call(Subscriber<? super Integer> subscriber) {
                subscriber.onNext(1);
                subscriber.onNext(2);
                subscriber.onNext(0);
                subscriber.onNext(3);
                subscriber.onNext(5/0);
                subscriber.onCompleted();
            }
        });

        Subscriber<Integer> subscriber = new Subscriber<Integer>() {
            @Override
            public void onCompleted() {
                Log.d(TAG, "onCompleted: ");
            }

            @Override
            public void onError(Throwable e) {
                // subscriber的onNext中出現的異常和observable和call方法中的給onNext()傳引數出現的異常都會出現在這裡
                // 總之就是call()方法中發生的異常都會出現在這裡
                Log.e(TAG, "onError: " + e.getMessage());
            }

            @Override
            public void onNext(Integer integer) {
                Log.d(TAG, "onNext: " + 10 / integer);
            }
        };

        observable.subscribe(subscriber);

過濾(filter)

        Observable.just("on", "off", "on", "on")
                .filter(new Func1<String, Boolean>() {
                    @Override
                    public Boolean call(String s) {
                        // 如果這裡返回了true資料就會被回撥到onNext, 否則返回了false就會被過濾掉
                        return TextUtils.equals("on", s);
                    }
                })
                .subscribe(new Subscriber<String>() {
                    @Override
                    public void onCompleted() {
                        Log.d(TAG, "onCompleted: ");
                    }

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

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

scheduler(執行緒切換)

        Observable.create(new Observable.OnSubscribe<Drawable>() {
            @Override
            public void call(Subscriber<? super Drawable> subscriber) {
                Log.d(TAG, "call: thread = " + Thread.currentThread().getName());
                Drawable drawable = getResources().getDrawable(R.mipmap.avatar);
                subscriber.onNext(drawable);
                subscriber.onCompleted();
            }
        })
                .subscribeOn(Schedulers.io()) // 指定 subscribe()發生在IO執行緒, 即在訂閱這個過程發生在IO執行緒
                .observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber的回撥發生在主執行緒
                .subscribe(new Observer<Drawable>() {
                    @Override
                    public void onCompleted() {
                    }

                    @Override
                    public void onError(Throwable e) {
                    }

                    @Override
                    public void onNext(Drawable drawable) {
                        Log.d(TAG, "onNext: thread = " + Thread.currentThread().getName());
                        mIv.setImageDrawable(drawable);
                    }
                });

這裡需要說明一下了, subscribeOn()指定 subscribe() 所發生的執行緒,即 Observable.OnSubscribe 被啟用時所處的執行緒。或者叫做事件產生的執行緒(資料發出的執行緒)。 observeOn(): 指定 Subscriber 所執行在的執行緒。或者叫做事件消費的執行緒。
Subscriber 並不是(嚴格說應該為『不一定是』,但這裡不妨理解為『不是』)subscribe() 引數中的Subscriber,而是 observeOn()執行時的當前 Observable 所對應的 Subscriber ,即它的直接下級 Subscriber 。換句話說,observeOn() 指定的是它之後的操作所在的執行緒。因此如果有多次切換執行緒的需求,只要在每個想要切換執行緒的位置呼叫一次 observeOn() 即可

Observable.just(1, 2, 3, 4) // IO 執行緒,由 subscribeOn() 指定
    .subscribeOn(Schedulers.io())
    .observeOn(Schedulers.newThread())
    .map(mapOperator) // 新執行緒,由 observeOn() 指定
    .observeOn(Schedulers.io())
    .map(mapOperator2) // IO 執行緒,由 observeOn() 指定
    .observeOn(AndroidSchedulers.mainThread) 
    .subscribe(subscriber);  // Android 主執行緒,由 observeOn() 指定

不同於 observeOn(),subscribeOn() 的位置放在哪裡都可以,但它是隻能呼叫一次的。


1626396-f147a10c2c94e2b6.jpg

圖中共有5處含有對事件的操作。由圖中可以看出,①和②兩處受第一個 subscribeOn()影響,執行在紅色執行緒;③和④處受第一個observeOn()的影響,執行在綠色執行緒;⑤處受第二個observeOn()影響,執行在紫色執行緒;而第二個subscribeOn(),由於在通知過程中執行緒就被第一個 subscribeOn()截斷,因此對整個流程並沒有任何影響。這裡也就回答了前面的問題:當使用了多個subscribeOn()的時候,只有第一個 subscribeOn()起作用。

doOnSubscribe()

預設情況下,doOnSubscribe()執行在subscribe()發生的執行緒(其實這一點我持懷疑態度);而如果在doOnSubscribe()之後有subscribeOn()的話,它將執行在離它最近的 subscribeOn()所指定的執行緒。

        Observable.create(new Observable.OnSubscribe<Integer>() {
            @Override
            public void call(Subscriber<? super Integer> subscriber) {
                Log.d(TAG, "call: thread = " + Thread.currentThread().getName());
                subscriber.onNext(1);
                subscriber.onNext(2);
                subscriber.onCompleted();
            }
        })
                .subscribeOn(Schedulers.io()) // 指定事件發生在io執行緒
                .doOnSubscribe(new Action0() { // doOnSubscribe在subscribe()呼叫後而且在事件傳送前執行
                    @Override
                    public void call() {
                        Log.d(TAG, "call: thread = " + Thread.currentThread().getName());
                        Log.d(TAG, "call: 資料傳送之前顯示progressbar");
                    }
                })
                .subscribeOn(AndroidSchedulers.mainThread()) // 指定doOnSubscribe()發生在主執行緒
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<Integer>() {
                    @Override
                    public void call(Integer integer) {
                        Log.d(TAG, "call: thread = " + Thread.currentThread().getName());
                        Log.d(TAG, "call: 輸出最終的資料" + integer);
                    }
                });

map(變換)

        Observable.just(R.mipmap.avatar)
                .map(new Func1<Integer, Drawable>() {
                    @Override
                    public Drawable call(Integer integer) {
                        Drawable drawable = getResources().getDrawable(integer);
                        return drawable;
                    }
                })
                .subscribe(new Subscriber<Drawable>() {
                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onNext(Drawable drawable) {
                        mIv.setImageDrawable(drawable);
                    }
                });

compose(對Observable整體的變換)

lift()的區別在於,lift()是針對事件項和事件序列的,而compose()是針對Observable自身進行變換。compose()方法其實挺有用的.

        Observable.Transformer<Integer, Drawable> transformer = new Observable.Transformer<Integer, Drawable>() {
            @Override
            public Observable<Drawable> call(Observable<Integer> observable) {
                return observable.map(new Func1<Integer, Drawable>() {
                    @Override
                    public Drawable call(Integer integer) {
                        Drawable drawable = getResources().getDrawable(integer);
                        return drawable;
                    }
                }).map(new Func1<Drawable, Drawable>() {
                    @Override
                    public Drawable call(Drawable drawable) {
                        return drawable;
                    }
                }); // observable.map後面可以繼續.lift
            }
        };
        Observable.just(R.mipmap.avatar)
                // compose是為了將一系列的變換方法封裝起來
                .compose(transformer)
                .subscribe(new Action1<Drawable>() {
                    @Override
                    public void call(Drawable drawable) {
                        mIv.setImageDrawable(drawable);
                    }
                });

flatmap

        Student student1 = new Student();
        student1.setStudentName("小明");
        List<Student.Course> courseList1 = new ArrayList<>();
        courseList1.add(new Student.Course("語文"));
        courseList1.add(new Student.Course("數學"));
        student1.setCourses(courseList1);

        Student student2 = new Student();
        student2.setStudentName("小紅");
        List<Student.Course> courseList2 = new ArrayList<>();
        courseList2.add(new Student.Course("英語"));
        courseList2.add(new Student.Course("化學"));
        student2.setCourses(courseList2);

        Student[] students = {student1, student2};

        Observable.from(students)
                .flatMap(new Func1<Student, Observable<Student.Course>>() {
                    @Override
                    public Observable<Student.Course> call(Student student) {
                        return Observable.from(student.getCourses());
                    }
                })
                .subscribe(new Subscriber<Student.Course>() {
                    @Override
                    public void onCompleted() {
                    }

                    @Override
                    public void onError(Throwable e) {
                    }

                    @Override
                    public void onNext(Student.Course course) {
                        Log.d(TAG, "onNext: course = " + course.courseName);
                    }
                });

doOnNext

        Observable.just(1, 2, 3)
                .doOnNext(new Action1<Integer>() {
                    @Override
                    public void call(Integer integer) {
                        Log.d(TAG, "doOnNext: integer = " + integer);
                    }
                })
                .subscribe(new Action1<Integer>() {
                    @Override
                    public void call(Integer integer) {
                        Log.d(TAG, "call: 最終輸出 " + integer);
                    }
                });

一些進階的操作

timer(定時操作)

        Observable.timer(3, TimeUnit.SECONDS)
                .subscribe(new Subscriber<Long>() {
                    @Override
                    public void onCompleted() {
                        Log.d(TAG, "onCompleted: ");
                    }

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

                    @Override
                    public void onNext(Long aLong) {
                        Log.d(TAG, "onNext: " + aLong);
                    }
                });

interval(週期性操作)

        Observable.interval(2, TimeUnit.SECONDS)
                .subscribe(new Subscriber<Long>() {
                    @Override
                    public void onCompleted() {
                        Log.d(TAG, "onCompleted: ");
                    }

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

                    @Override
                    public void onNext(Long aLong) {
                        Log.d(TAG, "onNext: " + aLong);
                    }
                });

throttleFirst(在每次事件觸發後的一定時間間隔內丟棄新的事件)

注: 這裡引入了rxbinding

        // 只返回一秒內的第一個, 後續發射出來的全部丟棄
        RxView.clicks(button)
                .throttleFirst(1, TimeUnit.SECONDS)
                .subscribe(new Observer<Object>() {
                    @Override
                    public void onCompleted() {
                        Log.d(TAG, "onCompleted: ");
                    }

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

                    @Override
                    public void onNext(Object o) {
                        Log.d(TAG, "onNext: button clicked");
                    }
                });

schedulePeriodically(輪詢請求)

        Observable.create(new Observable.OnSubscribe<String>() {
            @Override
            public void call(final Subscriber<? super String> observer) {
                Schedulers.newThread()
                        .createWorker()
                        .schedulePeriodically(new Action0() {
                            @Override
                            public void call() {
                                observer.onNext("呵呵");
                            }
                        }, 0, 3, TimeUnit.SECONDS);
            }
        }).subscribe(new Action1<String>() {
            @Override
            public void call(String s) {
                Log.d(TAG, "call: " + s);
            }
        });

concat(將若干個Observable串聯起來)

        Observable.concat(Observable.just(1, 2, 3), Observable.just(4, 5, 6))
                .subscribe(new Action1<Integer>() {
                    @Override
                    public void call(Integer integer) {
                        Log.d(TAG, "concat: " + integer);
                    }
                });

zip(將若干個Observable發射的資料組合)

        Observable.zip(Observable.just(1, 2, 3), Observable.just("A", "B", "C"), new Func2<Integer, String, String>() {
            @Override
            public String call(Integer integer, String s) {
                return integer + s;
            }
        })
                .subscribe(new Action1<String>() {
                    @Override
                    public void call(String s) {
                        Log.d(TAG, "zip: " + s);
                    }
                });

merge

        // 拼接兩個Observable的輸出,不保證順序,按照事件產生的順序傳送給訂閱者
        // 與concat的區別在於不保證順序, 按照事件產生的順序
        Observable.merge(Observable.just("1", "2", "3"), Observable.just("A", "B", "C"))
                .subscribe(new Action1<String>() {
                    @Override
                    public void call(String s) {
                        Log.d(TAG, "merge: " + s);
                    }
                });

combineLatest

注: 可以用來處理表單驗證

        mFirstObservable = Observable.create(new Observable.OnSubscribe<String>() {
            @Override
            public void call(final Subscriber<? super String> subscriber) {
                mEtFirst.addTextChangedListener(new TextWatcher() {
                    @Override
                    public void beforeTextChanged(CharSequence s, int start, int count, int after) {

                    }

                    @Override
                    public void onTextChanged(CharSequence s, int start, int before, int count) {
                        subscriber.onNext(s.toString());
                    }

                    @Override
                    public void afterTextChanged(Editable s) {

                    }
                });
            }
        });

        mSecondObservable = Observable.create(new Observable.OnSubscribe<String>() {
            @Override
            public void call(final Subscriber<? super String> subscriber) {
                mEtSecond.addTextChangedListener(new TextWatcher() {
                    @Override
                    public void beforeTextChanged(CharSequence s, int start, int count, int after) {

                    }

                    @Override
                    public void onTextChanged(CharSequence s, int start, int before, int count) {
                        subscriber.onNext(s.toString());
                    }

                    @Override
                    public void afterTextChanged(Editable s) {

                    }
                });
            }
        });
        
        Observable<Data> combineLatest = Observable.combineLatest(mFirstObservable, mSecondObservable, new Func2<String, String, Data>() {
            @Override
            public Data call(String s, String s2) {
                return new Data(s, s2);
            }
        });
        
        combineLatest.subscribe(new Action1<Data>() {
            @Override
            public void call(Data data) {
                mTv.setText("first = " + data.first +", seoncd = " + data.second);
            }
        });

上面的例子可以用rxbinding簡化

Observable<CharSequence> ObservableEmail = RxTextView.textChanges(mEmailView);
Observable<CharSequence> ObservablePassword = RxTextView.textChanges(mPasswordView);

Observable.combineLatest(ObservableEmail, ObservablePassword, new Func2<CharSequence, CharSequence, Boolean>() {
    @Override
    public Boolean call(CharSequence email, CharSequence password) {
        return isEmailValid(email.toString()) && isPasswordValid(password.toString());
    }
}).subscribe(new Subscriber<Boolean>() {
    @Override
    public void onCompleted() {

    }

    @Override
    public void onError(Throwable e) {

    }

    @Override
    public void onNext(Boolean verify) {
        if (verify) {
            mEmailSignInButton.setEnabled(true);
        } else {
            mEmailSignInButton.setEnabled(false);
        }
    }
});

combineLatest與zip的區別在於, zip發射的資料是一一對應的, 如果一個一直髮射資料而另一個不發射資料, 下游是獲取不到資料的.而combineLates則是: 只要有一個資料來源發射了資料, 就會呼叫call方法

與retrofit結合

public interface GankAPI {
    // http://gank.io/api/data/福利/10/1
    @GET("福利/{pagesize}/{page}")
    Observable<GankMeiziResult> getMeiziData(@Path("pagesize") int pageSize, @Path("page") int page);
}
    private RetrofitManager() {
        mRetrofit = new Retrofit.Builder()
                .client(genericClient())
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();
        // 所有的請求方法都放在同一個interface裡, 所以這裡建立出來的class不會很多, 所以可以用這一個
        mGankAPI = mRetrofit.create(GankAPI.class);
    }
    
    public GankAPI getGankAPI() {
        return mGankAPI;
    }
        GankAPI gankAPI = APIFactory.createGankAPI();
        // 這裡的gankAPI.getMeiziData(10, 1)代替之前的call
        gankAPI.getMeiziData(10, 1)
            .subscribeOn(Schedulers.io())
            .doOnSubscribe(new Action0() {
                @Override
                public void call() {
                    showProgressDialog();
                }
            })
            .subscribeOn(AndroidSchedulers.mainThread())  // 指定doOnSubscribe()所發生的執行緒, 其實這句程式碼可以不加的
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Subscriber<GankMeiziResult>() {
                @Override
                public void onCompleted() {
                    hideProgressDialog();
                }
            
                @Override
                public void onError(Throwable e) {
                    hideProgressDialog();
                }
            
                @Override
                public void onNext(GankMeiziResult gankMeizhiResult) {
                    mMeiZiAdapter = new MeiZiAdapter(gankMeizhiResult.beauties);
                    mRv.setAdapter(mMeiZiAdapter);
                }
            });

使用compose簡化執行緒指定

        gankAPI.getMeiziData(10, 1)
                .subscribeOn(Schedulers.io())
                .doOnSubscribe(new Action0() {
                    @Override
                    public void call() {
                        // 貌似doOnSubsribe的執行緒不受前面subscribeOn()指定執行緒的影響, 預設為主執行緒
                        Log.d(TAG, "currentThread = " + Thread.currentThread().getName());
                        showProgressDialog();
                    }
                })
                .compose(new Observable.Transformer<GankMeiziResult, GankMeiziResult>() {
                    @Override
                    public Observable<GankMeiziResult> call(Observable<GankMeiziResult> observable) {
                        return observable.observeOn(AndroidSchedulers.mainThread());
                    }
                })
                .subscribe(new Subscriber<GankMeiziResult>() {
                    @Override
                    public void onCompleted() {
                        hideProgressDialog();
                    }

                    @Override
                    public void onError(Throwable e) {
                        hideProgressDialog();
                    }

                    @Override
                    public void onNext(GankMeiziResult gankMeizhiResult) {
                        mMeiZiAdapter = new MeiZiAdapter(gankMeizhiResult.beauties);
                        mRv.setAdapter(mMeiZiAdapter);
                    }
                });

進一步簡化

public class RxSchedulersHelper {
    public static <T> Observable.Transformer<T, T> io2main() {
        return new Observable.Transformer<T, T>() {
            @Override
            public Observable<T> call(Observable<T> observable) {
                return observable
                        .subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread());
            }
        };
    }
}
        gankAPI.getMeiziData(10, 1)
                .compose(RxSchedulersHelper.<GankMeiziResult>io2main())
                .doOnSubscribe(new Action0() {
                    @Override
                    public void call() {
                        // 貌似doOnSubsribe的執行緒不受前面subscribeOn()指定執行緒的影響, 預設為主執行緒
                        Log.d(TAG, "currentThread = " + Thread.currentThread().getName());
                        showProgressDialog();
                    }
                })
                .subscribe(new Subscriber<GankMeiziResult>() {
                    @Override
                    public void onCompleted() {
                        hideProgressDialog();
                    }

                    @Override
                    public void onError(Throwable e) {
                        hideProgressDialog();
                    }

                    @Override
                    public void onNext(GankMeiziResult gankMeizhiResult) {
                        mMeiZiAdapter = new MeiZiAdapter(gankMeizhiResult.beauties);
                        mRv.setAdapter(mMeiZiAdapter);
                    }
                });

其實這裡一些filter, map等統一操作都可以放在這個helper的compose裡面

retrywhen()錯誤重連機制

        Observable.create(new Observable.OnSubscribe<Integer>() {
            @Override
            public void call(Subscriber<? super Integer> subscriber) {
                subscriber.onNext(1);
                subscriber.onNext(2);
                subscriber.onNext(3 / mDivisor);
                subscriber.onCompleted();
            }
        })
                .retryWhen(new Func1<Observable<? extends Throwable>, Observable<?>>() {
                    @Override
                    public Observable<?> call(final Observable<? extends Throwable> observable) {
                        return observable.flatMap(new Func1<Throwable, Observable<?>>() {
                            @Override
                            public Observable<?> call(Throwable throwable) {

                                if (throwable instanceof ArithmeticException) {
                                    if (++retryCount <= maxRetries) {
                                        Log.d(TAG, "正在重試");
                                        if (retryCount == 3) {
                                            mDivisor = 1;
                                        }
                                        return Observable.timer(2, TimeUnit.SECONDS);
                                    }
                                }
                                return Observable.error(throwable);
                            }
                        });
                    }
                })
                .subscribe(new Subscriber<Integer>() {
                    @Override
                    public void onCompleted() {
                        Log.d(TAG, "onCompleted: ");
                    }

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

                    @Override
                    public void onNext(Integer integer) {
                        Log.d(TAG, "onNext: integer = " + integer);
                    }
                });

github地址

https://github.com/mundane799699/AndroidProjects/tree/master/MyRxJavaSummary
總結的有點累, 寫的有點亂, 畢竟不是太熟, 個人水平也有限.如有疏漏, 請幫助我指出, 感謝您的閱讀.