Rxjava3文檔級教程三: 實戰演練

商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

Rxjava3文檔級教程一: 介紹和基本使用

Rxjava3文檔級教程二: 操作符全解

Rxjava3文檔級教程三: 實戰演練

目錄

一 結合RxBinding

1.1 按鈕防抖功能:

1.2 輸入框輸入實時網絡請求步長控制:

1.3 聯合/表單判斷

1.4 定時器任務

二 

2.1 嵌套網絡請求

三 防泄漏

3.1 Observable.unsubscribe

3.2 disposable.dispose

3.3 CompositeDisposable

3.4 Rxlifecycle

參考文章:


一 結合RxBinding

  RxBinding 的 GitHub 地址

RxBinding 能夠把 Android 平臺的兼容包內的 UI 控件變爲 Observaber 對象. 可以把 UI 控件的事件當作 RxJava 中的數據流來使用。

依賴如下:

Platform bindings:

implementation 'com.jakewharton.rxbinding3:rxbinding:3.1.0'

AndroidX library bindings:

implementation 'com.jakewharton.rxbinding3:rxbinding-core:3.1.0'
implementation 'com.jakewharton.rxbinding3:rxbinding-appcompat:3.1.0'
implementation 'com.jakewharton.rxbinding3:rxbinding-drawerlayout:3.1.0'
implementation 'com.jakewharton.rxbinding3:rxbinding-leanback:3.1.0'
implementation 'com.jakewharton.rxbinding3:rxbinding-recyclerview:3.1.0'
implementation 'com.jakewharton.rxbinding3:rxbinding-slidingpanelayout:3.1.0'
implementation 'com.jakewharton.rxbinding3:rxbinding-swiperefreshlayout:3.1.0'
implementation 'com.jakewharton.rxbinding3:rxbinding-viewpager:3.1.0'
implementation 'com.jakewharton.rxbinding3:rxbinding-viewpager2:3.1.0'

Google 'material' library bindings:

implementation 'com.jakewharton.rxbinding3:rxbinding-material:3.1.0'

RxBinding 的優點:

  • 它是對 Android View 事件的擴展, 它使得開發者可以對 View 事件使用 RxJava 的各種操作。
  • 提供了與 Rxjava 一致的回調, 使得代碼簡潔明瞭。
  • 幾乎支持所有的常用控件及事件, 還對 Kotlin 支持。
  • 可以應用於整個 App 的所有 UI 事件。

進階案例:

1.1 按鈕防抖功能:

相比之前的定時器或者標誌位,來實現的快速點擊下的防抖功能,採用RxView可以大大簡化代碼:

         /**
         * 按鈕點擊防抖
         */
        RxView.clicks(mBt)
                .throttleFirst(1, TimeUnit.SECONDS)
                .subscribeOn(AndroidSchedulers.mainThread())
                .subscribe(v -> Log.i(TAG, "點擊按鈕"));
         /**
         * 按鈕長按
         */
        RxView.longClicks(mBt)...

1.2 輸入框輸入實時網絡請求步長控制:

很多App都有這種頂部的搜索框,而現有的實現,一般都會在用戶進行輸入時,用EditText裏自帶的addTextChangedListener(new TextWatcher()來監聽文本變化,進行實時搜索。

 etPriceBegin.addTextChangedListener(new TextWatcher() {
 
            @Override
            public void onTextChanged(CharSequence s, int start, int before,
                                      int count) {
                //s:變化後的所有字符
                Toast.makeText(getContext(), "變化:"+s+";"+start+";"+before+";"+count, Toast.LENGTH_SHORT).show();
                Log.i("Seachal:","變化:"+s+";"+start+";"+before+";"+count);
            }
 
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count,
                                          int after) {
                //s:變化前的所有字符; start:字符開始的位置; count:變化前的總字節數;after:變化後的字節數
                Toast.makeText(getContext(), "變化前:"+s+";"+start+";"+count+";"+after, Toast.LENGTH_SHORT).show();
                Log.i("Seachal:","變化前:"+s+";"+start+";"+count+";"+after);
            }
 
            @Override
            public void afterTextChanged(Editable s) {
                //S:變化後的所有字符;start:字符起始的位置;before: 變化之前的總字節數;count:變化後的字節數
                Toast.makeText(getContext(), "變化後:"+s+";", Toast.LENGTH_SHORT).show();
                Log.i("Seachal:","變化後:"+s+";");
            }
        });

但有了RxView後,一切更加方便了起來。下面這個例子裏,可以跳過用戶的第一次輸入不做處理, debounce(1, TimeUnit.SECONDS)又實現了用戶在快速刪除或者輸入時,不會因爲每次的實時變化都去進行網絡請求或者相關操作,而是在間隔1s後纔去進行這些操作。

        ed = findViewById(R.id.ed);


        RxTextView.textChanges(ed)
                .debounce(1, TimeUnit.SECONDS)
                //跳過第1次請求 因爲初始輸入框的空字符狀態
                .skip(1)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<CharSequence>() {
                    @Override
                    public void onSubscribe(Disposable d) {

                    }
                    @Override
                    public void onNext(CharSequence charSequence) {
                        Log.i(TAG,"收到的字符: " + charSequence.toString());
                    }

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

                    }

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

1.3 聯合/表單判斷

        Observable<CharSequence> ObservableName = RxTextView.textChanges(mEtPhone);
        Observable<CharSequence> ObservablePassword = RxTextView.textChanges(mEtPassword);


        Observable.combineLatest(
                ObservableName, ObservablePassword,
                (phone, password) -> {
                    return isPhoneValid(phone.toString()) && isPasswordValid(password.toString());
                })

                .subscribe(aBoolean -> {
                    mBtLogin.setEnabled(aBoolean);
//                    Toast.makeText(getApplicationContext(), "默認的Toast", Toast.LENGTH_SHORT).show();
                });

        //Lambda寫法
        Observable.combineLatest(ObservableName, ObservablePassword
                , (phone, password) -> isPhoneValid(phone.toString()) && isPasswordValid(password.toString()))
                .subscribe(mBtLogin::setEnabled);

1.4 定時器任務

在我們的登錄註冊頁,少不了一個獲得驗證碼的倒計時定時器,一般開發者都會選擇繼承CountDownTimer類,神說:太麻煩了!於是有了Rx。下面的代碼中,mBt在第一次點擊後就處於setEnabled(false)的狀態,不可點擊。

    private Observable<Boolean> verifyCodeObservable;

    private static int SECOND = 20;
       


    verifyCodeObservable = RxView.clicks(mBt)
                .throttleFirst(SECOND, TimeUnit.SECONDS) //防止20秒內連續點擊,或者只使用doOnNext部分
                .subscribeOn(AndroidSchedulers.mainThread())
                .map(o -> false)
                .doOnNext(mBt::setEnabled);
        verifyCodeObservable.subscribe(s -> {
            Observable.interval(1, TimeUnit.SECONDS, AndroidSchedulers.mainThread())
                    .take(SECOND)
                    .subscribe(aLong -> {
                                RxTextView.text(mBt).accept("剩餘" + (SECOND - aLong) + "秒");
                            }
                            , Throwable::printStackTrace
                            , () -> {
                                RxTextView.text(mBt).accept("獲取驗證碼");
                                RxView.enabled(mBt).accept(true);
                            });
        });

二 

2.1 嵌套網絡請求

        // flatMap
        MyRxView.clicks(btn3)
                .throttleFirst(1000, TimeUnit.MILLISECONDS)
                .observeOn(Schedulers.io())
                .flatMap(new Function<Object, ObservableSource<ProjectBean>>() {
                    @Override
                    public ObservableSource<ProjectBean> apply(Object o) throws Exception {
                        return wanAndroidApi.getProject();
                    }
                })
                .flatMap(new Function<ProjectBean, ObservableSource<ProjectBean.DataBean>>() {
                    @Override
                    public ObservableSource<ProjectBean.DataBean> apply(ProjectBean projectBean) throws Exception {
                        return Observable.fromIterable(projectBean.getData());
                    }
                })
                .flatMap(new Function<ProjectBean.DataBean, ObservableSource<ProjectItem>>() {
                    @Override
                    public ObservableSource<ProjectItem> apply(ProjectBean.DataBean dataBean) throws Exception {
                        return wanAndroidApi.getProjectItem(1,dataBean.getId());
                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<ProjectItem>() {
                    @Override
                    public void accept(ProjectItem projectItem) throws Exception {
                        Log.i(TAG, "accept: " + projectItem);
                    }
                });

        //lambda方式
//        RxView.clicks(btn3)
//                .throttleFirst(1000, TimeUnit.MILLISECONDS)
//                .observeOn(Schedulers.io())
//                .flatMap( o -> wanAndroidApi.getProject())
//                .flatMap(projectBean -> Observable.fromIterable(projectBean.getData()))
//                .flatMap(dataBean -> wanAndroidApi.getProjectItem(1,dataBean.getId()))
//                .observeOn(AndroidSchedulers.mainThread())
//                .subscribe(projectItem -> Log.i(TAG, "projectItem: " + projectItem));

 

三 防泄漏

3.1 Observable.unsubscribe

@Override
    protected void onDestroy() {
        super.onDestroy();
        Observable.unsubscribeOn(AndroidSchedulers.mainThread()); //防止泄露
    }

3.2 disposable.dispose

@Override
    protected void onDestroy() {
        super.onDestroy();
        if(disposable!=null && !disposable.isDisposed()){
            disposable.dispose();
        }
    }

3.3 CompositeDisposable

如果在請求過程中,UI層destroy了怎麼辦,不及時取消訂閱,可能會造成內存泄漏。因此,CompositeDisposable就上場了,它可以對我們訂閱的請求進行統一管理。CompositeDisposable的clear方法內部,實際上就會調用Disposable的dispose方法。
大致三步走:
1、在UI層創建的時候(比如onCreate之類的),實例化CompositeDisposable;
2、把subscribe訂閱返回的Disposable對象加入管理器;
3、UI銷燬時清空訂閱的對象。

private CompositeDisposable mCompositeDisposable;
// when create UI
mCompositeDisposable = new CompositeDisposable();
// when request data
if (mCompositeDisposable != null && !mCompositeDisposable.isDisposed()) {
    mCompositeDisposable.add(disposable);
}
// when destroy UI
if (mCompositeDisposable != null) {
    mCompositeDisposable.clear(); // clear時網絡請求會隨即cancel
    mCompositeDisposable = null;
}

這樣我們就可以管理脫繮的網絡請求了,相當於將它與UI的生命週期綁定。只要稍稍將上述模板封裝一哈,就比較方便了。比如add操作可以封裝一個方法,每次網絡請求時add一發就好。
對於MVP架構的項目,CompositeDisposable完全可以封裝到Presenter當中。這裏就不展開了。

3.4 Rxlifecycle

github官網

我們可以利用Rxlifecycle來解決內存泄漏問題。

依賴:

implementation 'com.trello.rxlifecycle3:rxlifecycle:3.1.0'

// If you want to bind to Android-specific lifecycles
implementation 'com.trello.rxlifecycle3:rxlifecycle-android:3.1.0'

// If you want pre-written Activities and Fragments you can subclass as providers
implementation 'com.trello.rxlifecycle3:rxlifecycle-components:3.1.0'

// If you want pre-written support preference Fragments you can subclass as providers
implementation 'com.trello.rxlifecycle3:rxlifecycle-components-preference:3.1.0'

// If you want to use Android Lifecycle for providers
implementation 'com.trello.rxlifecycle3:rxlifecycle-android-lifecycle:3.1.0'

// If you want to use Kotlin syntax
implementation 'com.trello.rxlifecycle3:rxlifecycle-kotlin:3.1.0'

// If you want to use Kotlin syntax with Android Lifecycle
implementation 'com.trello.rxlifecycle3:rxlifecycle-android-lifecycle-kotlin:3.1.0'

// If you want to use Navi for providers
// DEPRECATED: Use rxlifecycle-android-lifecycle instead. This will be removed in a future release.
implementation 'com.trello.rxlifecycle3:rxlifecycle-navi:3.1.0'

You can bind when the lifecycle emits anything:

myObservable
    .compose(RxLifecycle.bind(lifecycle))
    .subscribe();

Or you can bind to when a specific lifecyle event occurs:

myObservable
    .compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY))
    .subscribe();

Alternatively, you can let RxLifecycle determine the appropriate time to end the sequence:

myObservable
    .compose(RxLifecycleAndroid.bindActivity(lifecycle))
    .subscribe();

例子:

public class RxLifeActivity extends RxAppCompatActivity {
 
    /**
     * RxLifecycle使用:在當前activity中繼承RxAppCompatActivity
     * <p>
     * ActivityEvent:手動設置指定在什麼時候取消訂閱,下列枚舉
     * CREATE,
     * START,
     * RESUME,
     * PAUSE,
     * STOP,
     * DESTROY
     */
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_rx_life);
 
        Flowable.interval(3, 2, TimeUnit.SECONDS)
                .subscribeOn(Schedulers.io())
                .compose(this.bindUntilEvent(ActivityEvent.PAUSE)) //手動設置在activity onPause的時候取消訂閱
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(aLong -> Log.e("liuqiang", "RxLifeActivityOnCreate:" + aLong));
 
    }
 
    @Override
    protected void onStart() {
        super.onStart();
        Flowable.interval(6, 3, TimeUnit.SECONDS)
                //bindToLifecycle的自動取消訂閱示例,因爲是在onStart的時候調用,所以在onStop的時候自動取消訂閱
                .compose(this.bindToLifecycle()) //設置在默認的生命週期中取消訂閱
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(aLong -> Log.e("liuqiang", "RxLifeActivityOnStart:" + aLong));
 
    }
}

 

 

參考文章:

因爲寫RxJava系列的文章時進行了很多閱讀和參考,因此不分一二三等,將全系列的參考引用統一如下:

RxJava3 Wiki:https://github.com/ReactiveX/RxJava/wiki

RxJava3官方github:https://github.com/ReactiveX/RxJava/wiki/What's-different-in-3.0

ReactiveX文檔中文翻譯:https://mcxiaoke.gitbooks.io/rxdocs/content/operators/Creating-Observables.html

single:http://reactivex.io/documentation/single.html

操作符系列講的很好的文章:https://blog.csdn.net/weixin_42046829/article/details/104836592

基礎介紹:https://blog.csdn.net/weixin_42046829/article/details/104833751

RxJava3的一些簡介:https://juejin.im/post/5d1eeffe6fb9a07f0870b4e8

觀察者被觀察者入門RxJava的一篇好文章:https://juejin.im/post/580103f20e3dd90057fc3e6d

關於背壓一個很好的介紹:https://juejin.im/post/582d413c8ac24700619cceed

RxLifecycle:https://github.com/trello/RxLifecycle

剛哥平臺的挺好很全:RxJava2 只看這一篇文章就夠了https://juejin.im/post/5b17560e6fb9a01e2862246f

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