你真的會 "用" 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)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市甚疟,隨后出現(xiàn)的幾起案子仗岖,更是在濱河造成了極大的恐慌,老刑警劉巖览妖,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件轧拄,死亡現(xiàn)場離奇詭異,居然都是意外死亡讽膏,警方通過查閱死者的電腦和手機檩电,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來府树,“玉大人俐末,你說我怎么就攤上這事⊙傧溃” “怎么了卓箫?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長垄潮。 經(jīng)常有香客問我烹卒,道長恢准,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任甫题,我火速辦了婚禮馁筐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘坠非。我一直安慰自己敏沉,他們只是感情好,可當我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布炎码。 她就那樣靜靜地躺著盟迟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪潦闲。 梳的紋絲不亂的頭發(fā)上攒菠,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天,我揣著相機與錄音歉闰,去河邊找鬼辖众。 笑死,一個胖子當著我的面吹牛和敬,可吹牛的內(nèi)容都是我干的凹炸。 我是一名探鬼主播,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼昼弟,長吁一口氣:“原來是場噩夢啊……” “哼啤它!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起舱痘,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤变骡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后芭逝,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體塌碌,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年铝耻,在試婚紗的時候發(fā)現(xiàn)自己被綠了誊爹。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡瓢捉,死狀恐怖频丘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情泡态,我是刑警寧澤搂漠,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站某弦,受9級特大地震影響桐汤,放射性物質(zhì)發(fā)生泄漏而克。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一怔毛、第九天 我趴在偏房一處隱蔽的房頂上張望员萍。 院中可真熱鬧,春花似錦拣度、人聲如沸碎绎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽筋帖。三九已至,卻和暖如春冤馏,著一層夾襖步出監(jiān)牢的瞬間日麸,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工逮光, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留代箭,地道東北人。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓睦霎,卻偏偏與公主長得像梢卸,于是被迫代替她去往敵國和親走诞。 傳聞我的和親對象是個殘疾皇子副女,可洞房花燭夜當晚...
    茶點故事閱讀 44,955評論 2 355