你真的會 "用" RxJava & Retrofit 嗎?

今天要來說說兩個困擾了自己很久的框架:RxJava & Retrofit,本篇文章主要介紹的是如何通過這兩個框架請求數(shù)據(jù),一些常用的操作符,接下來就讓我們開始吧。

1) 要使用這兩個框架當然要先把這兩個框架的包導(dǎo)進來:這里可以看到還導(dǎo)入了RxBinding 的包,這是為了使用防抖功能而導(dǎo)入的,同時因為是開發(fā) Android,因此除了導(dǎo)入 RxJava 還需導(dǎo)入 RxAndroid,否則所需的 API 是不完整的,RxJava占所需代碼的 80%,RxAndroid 占20%,只有把這兩部分一起導(dǎo)入,才是我們完整的API,例如我們在切換線程時,最經(jīng)常用到的 AndroidSchedulers.mainThread(),就是 RxAndroid包里的。

    // 依賴RxAndroid 2X 的依賴庫
    // 增加RxJava 2X 的依賴庫
    implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
    implementation 'io.reactivex.rxjava2:rxjava:2.0.7'

    // OkHttp相關(guān)
    implementation 'com.facebook.stetho:stetho:1.4.2'
    implementation 'com.facebook.stetho:stetho-okhttp3:1.4.2'

   // 網(wǎng)絡(luò)請求相關(guān)
    implementation "com.squareup.retrofit2:retrofit:2.4.0"
    implementation "com.squareup.retrofit2:retrofit-mock:2.4.0"
    implementation "com.squareup.retrofit2:converter-gson:2.8.2"
    implementation 'com.squareup.okhttp3:logging-interceptor:3.5.0'
    implementation "com.squareup.retrofit2:converter-scalars:2.4.0"
    implementation "com.squareup.retrofit2:adapter-rxjava2:2.4.0"
    implementation "com.squareup.retrofit2:converter-gson:2.8.2"

    implementation "com.google.code.gson:gson:2.8.2"
    implementation 'com.jakewharton.rxbinding2:rxbinding:2.1.1' // 操作功能防抖

2)使用 Retrofit 請求網(wǎng)絡(luò)首先要寫網(wǎng)絡(luò)的請求接口,以及給這個接口打上注解(包括請求方式,地址,以及傳參等)注意這里返回的是一個 Observable(被觀察者)而我之前手寫 Retrofit 的文章里返回的是一個 okHttp 的 Call。

public interface WanAndroid {
    //總數(shù)據(jù)
    @GET("project/tree/json")
    Observable<ProjectBean> getProject();
    //Item數(shù)據(jù)
    @GET("project/list/{pageIndex}/json")
    Observable<ProjectItem>getProjectItem(@Path("pageIndex") int pageIndex, @Query("cid") int cid);
}

3)接下來就是對 Retrofit對象 構(gòu)建的封裝,因為 Retrofit 是通過 okHttp 請求網(wǎng)絡(luò)的,因此,這里其實也是對 okHttp 的封裝。最后還封裝了一個 RxJava 的線程切換方法,通過這個方法結(jié)合 compose 操作符就可以給上游分配到 io 異步線程,下游分配到 Android 主線程:

    //設(shè)置BaseUrl,這部發(fā)將拼接到上一步注解的前半部分
    public static String BASE_URL = "https://www.wanandroid.com/";

    public static void setBaseUrl(String baseUrl) {
        BASE_URL = baseUrl;
    }

     /**
     * 根據(jù)各種配置創(chuàng)建出Retrofit
     *
     * @return 返回創(chuàng)建好的Retrofit
     */
    public static Retrofit getOnlineCookieRetrofit() {
        // OKHttp客戶端
        OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder();
        // 各種參數(shù)配置
        OkHttpClient okHttpClient = httpBuilder
                .addNetworkInterceptor(new StethoInterceptor())
                .readTimeout(10000, TimeUnit.SECONDS)
                .connectTimeout(10000, TimeUnit.SECONDS)
                .writeTimeout(10000, TimeUnit.SECONDS)
                .build();


        return new Retrofit.Builder().baseUrl(BASE_URL)
                // TODO 請求用 OKhttp
                .client(okHttpClient)
                // TODO 響應(yīng)RxJava
                // 添加一個json解析的工具
                .addConverterFactory(GsonConverterFactory.create(new Gson()))
                // 添加rxjava處理工具
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
    }

    //線程切換封裝
    public static <UD> ObservableTransformer<UD,UD> rxud(){
        return new ObservableTransformer<UD, UD>() {
            @Override
            public ObservableSource<UD> apply(Observable<UD> upstream) {
                           //上游分配異步線程
                return upstream.subscribeOn(Schedulers.io())
                          //下游分配主線程
                        .observeOn(AndroidSchedulers.mainThread())
                        .map(new Function<UD, UD>() {
                            @Override
                            public UD apply(UD ud) throws Exception {
                                Log.d("---", "apply: 我監(jiān)聽到你了,居然再執(zhí)行");
                                return ud;
                            }
                        });
            }
        };
    }

4)當然還要定義返回數(shù)據(jù)的 bean 類,我這里定義了兩個 ProjectBean ,ProjectItem,就是一些基本的 set & get,這里省略不寫。
5)使用的時候跟使用 Retrofit 時一樣,先獲得 第 2) 步 定義的接口對象,通過接口對象請求網(wǎng)絡(luò),通過 RxJava 操作返回的數(shù)據(jù):

    //獲取全部數(shù)據(jù)
    private void getProjectData() {
        disposable = api.getProject()
                .compose(HttpUtil.rxud())//切換線程
                .subscribe(new Consumer<ProjectBean>() {
                    @Override
                    public void accept(ProjectBean projectBean) throws Exception {
                        Log.e("---", projectBean.toString());
                    }
                });
    }

可以看到這里使用了一個 Disposable 來接收操作完數(shù)據(jù)的結(jié)果,Disposable 的提供一個中斷的作用,可以利用這個變量進行中斷操作,當 APP 在關(guān)閉時,如果后臺仍然持有Context 對象繼續(xù)操作數(shù)據(jù),這就會引發(fā)內(nèi)存泄露,因此需要通過這個變量來防止內(nèi)存泄漏:在onDestory 中斷操作數(shù)據(jù)

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

6)當出現(xiàn)網(wǎng)絡(luò)嵌套的情況,可以使用 flatMap 操作符解決,flatMap 的作用是把數(shù)據(jù)處理完之后又封裝成一個被觀察者繼續(xù)傳給下游.。同時在這里使用了防抖(指的是指定時間內(nèi)允許用戶操作指定次數(shù)某控件):

    //使用防抖+網(wǎng)絡(luò)嵌套問題
    //這里第一次層級嘗試使用map操作符,但是效果不佳,查看map/flatMap源碼:
    //public final <R> Observable<R> flatMap(Function<? super T, ? extends ObservableSource<? extends R>> mapper)
    //public final <R> Observable<R> map(Function<? super T, ? extends R> mapper)
    //可以看到flatMap操作符的Function的第二個入?yún)?下游)是個被觀察者,將這個被觀察者作為下一步操作的上游
    //而map的第二個入?yún)⑹菦]有限制的泛型,
    //總結(jié):fltaMap 與 map 的區(qū)別是fltaMap 讓用戶操作完成之后又封裝成一個被觀察者繼續(xù)向下傳遞
    private void getDataByView() {
        Button btnNet = findViewById(R.id.btn_net);
        disposable = RxView.clicks(btnNet)
                .throttleFirst(2, TimeUnit.SECONDS)//2s-允許點擊一次
                .observeOn(Schedulers.io())
                .flatMap(
                        new Function<Object, ObservableSource<ProjectBean>>() {
                            @Override
                            public ObservableSource<ProjectBean> apply(Object o) throws Exception {
                                return api.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 api.getProjectItem(1, dataBean.getId());
                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<ProjectItem>() {
                    @Override
                    public void accept(ProjectItem projectItem) throws Exception {

                    }
                });
    }

7)map操作符:作用是將上游的數(shù)據(jù)傳再到下游觀察者之前進行一次操作,轉(zhuǎn)換成用戶所需要的類型后再傳給觀察者,因此觀察者接收到的上游就是被 map 轉(zhuǎn)換之后的下游:

     //獲取全部數(shù)據(jù)
    private void getProjectMap() {
        disposable = api.getProject()
                .compose(HttpUtil.rxud())//切換線程
                //注意這里上游是 ProjectBean,提供給下游觀察者的是 String
                .map(new Function<ProjectBean, String>() {
                    @Override //這里返回的 String 提供給下游觀察者
                    public String apply(ProjectBean projectBean) throws Exception {
                        return null;
                    }
                })
                 //觀察者接收到 map 返回的 String
                .subscribe(new Consumer<String>() {
                    @Override
                    public void accept(String str) throws Exception {
                        Log.e("---", str);
                    }
                });
    }

8)**doOnNext操作符:在觀察者的 next 操作之前先執(zhí)行這個方法,一般用在一連續(xù)的操作(比如注冊后顯示UI,緊接著跳轉(zhuǎn)登錄)map 是在上游傳給下游時進行一次攔截,而 doOnNext 則是在下游往上游回傳時進行攔截:

     //doOnNext練習(xí)
    //doOnNext:就是在拿到結(jié)果交給訂閱者之前截斷,處理完成后再交給訂閱者的Next操作
    //map操作是在數(shù)據(jù)發(fā)送數(shù)據(jù)時,上游與下游之間截斷
    //doOnNext 是在拿到數(shù)據(jù)后,下游往上游回傳時截斷
    private void getDataByNext(){
        disposable = api.getProject()
                .compose(HttpUtil.rxud())
                .doOnNext(new Consumer<ProjectBean>() {
                    @Override
                    public void accept(ProjectBean projectBean) throws Exception {
                        Log.e("---","先執(zhí)行這里");
                    }
                })
                .subscribe(new Consumer<ProjectBean>() {
                    @Override
                    public void accept(ProjectBean projectBean) throws Exception {
                        Log.e("---","后執(zhí)行這里");
                    }
                });
    }

本篇主要以使用為主,RxJava 的原理性將在下篇文章講解,希望對你有所幫助~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容