RxJava 與 Retrofit 結(jié)合的最佳實(shí)踐

前言

RxJava和Retrofit也火了一段時(shí)間了瘫絮,不過最近一直在學(xué)習(xí)ReactNative和Node相關(guān)的姿勢,一直沒有時(shí)間研究這些新東西,最近有個(gè)項(xiàng)目準(zhǔn)備寫顷级,打算先用Android寫一個(gè)Demo出來,卻發(fā)現(xiàn)Android的世界發(fā)生了天翻地覆的變化确垫,EventBus和OKHttp啥的都不見了弓颈,RxJava和Retrofit是什么鬼?

好吧删掀,到Github上耐著性子看過了RxJava和Retrofit的介紹和幾個(gè)Demo翔冀,原來Android的大神Jake Wharton為Retrofit這個(gè)項(xiàng)目貢獻(xiàn)了這么多的代碼,沒有道理不用了披泪。

如果你對RxJava不熟悉請先看給 Android 開發(fā)者的 RxJava 詳解這篇文章纤子。

如果你對Retrofit不熟悉就先看Retrofit官網(wǎng)

當(dāng)然也有很多RxJava與Retrofit的文章款票,但是我覺得很多大家都很糾結(jié)的功能都沒有被總結(jié)出來控硼,所以才有了此篇文章。

歡迎大家拍磚艾少。

接下來進(jìn)入正文卡乾,我是從下面幾個(gè)角度去思考RxJava與Retrofit結(jié)合的。

RxJava如何與Retrofit結(jié)合

相同格式的Http請求數(shù)據(jù)該如何封裝

相同格式的Http請求數(shù)據(jù)統(tǒng)一進(jìn)行預(yù)處理

如何取消一個(gè)Http請求 -- 觀察者之間的對決缚够,Oberver VS Subscriber

一個(gè)需要ProgressDialog的Subscriber該有的樣子

1.RxJava如何與Retrofit結(jié)合

1.1 基本頁面

先扔出build.gradle文件的內(nèi)容

dependencies{

compile fileTree(dir:'libs',include:['*.jar'])testCompile'junit:junit:4.12'

compile'com.android.support:appcompat-v7:23.2.0'

compile'io.reactivex:rxjava:1.1.0'

compile'io.reactivex:rxandroid:1.1.0'

compile'com.squareup.retrofit2:retrofit:2.0.0-beta4'

compile'com.squareup.retrofit2:converter-gson:2.0.0-beta4'

compile'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4'

compile'com.google.code.gson:gson:2.6.2'

compile'com.jakewharton:butterknife:7.0.1'}

也就是說本文是基于RxJava1.1.0和Retrofit 2.0.0-beta4來進(jìn)行的幔妨。 添加rxandroid是因?yàn)閞xjava中的線程問題。

下面先搭建一個(gè)基本的頁面谍椅,頁面很簡單陶冷,先來看文件目錄結(jié)構(gòu)

activity_main.xml的代碼如下:

MainActivity.java的代碼如下:

packagecom.queen.rxjavaretrofitdemo.activity;importandroid.os.Bundle;importandroid.support.v7.app.AppCompatActivity;importandroid.widget.Button;importandroid.widget.TextView;importcom.queen.rxjavaretrofitdemo.R;importbutterknife.Bind;importbutterknife.ButterKnife;importbutterknife.OnClick;publicclassMainActivityextendsAppCompatActivity{@Bind(R.id.click_me_BN)ButtonclickMeBN;@Bind(R.id.result_TV)TextViewresultTV;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ButterKnife.bind(this);}@OnClick(R.id.click_me_BN)publicvoidonClick(){getMovie();}//進(jìn)行網(wǎng)絡(luò)請求privatevoidgetMovie(){}}

注意不要忘記加網(wǎng)絡(luò)權(quán)限

1.2 只用Retrofit

我們準(zhǔn)備在getMovie方法中進(jìn)行網(wǎng)絡(luò)請求,我們先來看看只使用Retrofit是如何進(jìn)行的毯辅。

我們使用豆瓣電影的Top250做測試連接埂伦,目標(biāo)地址為

https://api.douban.com/v2/movie/top250?start=0&count=10

至于返回的數(shù)據(jù)格式,大家自己訪問下鏈接就看到了思恐,太長就不放進(jìn)來了沾谜。

首先我們要根據(jù)返回的結(jié)果封裝一個(gè)Entity膊毁,暫命名為MovieEntity,代碼就不貼了基跑。

接下來我們要?jiǎng)?chuàng)建一個(gè)接口取名為MovieService婚温,代碼如下:

publicinterfaceMovieService{@GET("top250")CallgetTopMovie(@Query("start")intstart,@Query("count")intcount);}

回到MainActivity之中,我們來寫getMovie方法的代碼

//進(jìn)行網(wǎng)絡(luò)請求privatevoidgetMovie(){StringbaseUrl="https://api.douban.com/v2/movie/";Retrofitretrofit=newRetrofit.Builder().baseUrl(baseUrl).addConverterFactory(GsonConverterFactory.create()).build();MovieServicemovieService=retrofit.create(MovieService.class);Callcall=movieService.getTopMovie(0,10);call.enqueue(newCallback(){@OverridepublicvoidonResponse(Callcall,Responseresponse){resultTV.setText(response.body().toString());}@OverridepublicvoidonFailure(Callcall,Throwablet){resultTV.setText(t.getMessage());}});}

以上為沒有經(jīng)過封裝的媳否、原生態(tài)的Retrofit寫網(wǎng)絡(luò)請求的代碼栅螟。 我們可以封裝創(chuàng)建Retrofit和service部分的代碼,然后Activity用創(chuàng)建一個(gè)Callback作為參數(shù)給Call篱竭,這樣Activity中只關(guān)注請求的結(jié)果力图,而且Call有cancel方法可以取消一個(gè)請求,好像沒Rxjava什么事了掺逼,我覺得可以寫到這就下班了~

接下來我們要面對的問題是這樣的 如果我的Http返回?cái)?shù)據(jù)是一個(gè)統(tǒng)一的格式吃媒,例如

{"resultCode":0,"resultMessage":"成功","data":{}}

我們?nèi)绾螌Ψ祷亟Y(jié)果進(jìn)行一個(gè)統(tǒng)一的處理呢?

另外吕喘,我的ProgressDialog的show方法應(yīng)該在哪調(diào)用呢赘那?看樣子只能在getMovie()這個(gè)方法里面調(diào)用了,換個(gè)地方發(fā)出請求就要在對應(yīng)的Listener里面寫一遍show()的代碼氯质,其實(shí)挺鬧心募舟。

而且錯(cuò)誤請求我也想集中處理掉不要貼重復(fù)的代碼。

我們先來看結(jié)合了Rxjava之后闻察,事情有沒有變化的可能胃珍。當(dāng)然即便是不用Rxjava,依舊能夠做很多的封裝蜓陌,只是比較麻煩觅彰。

如需查看項(xiàng)目代碼 --> 代碼地址:

https://github.com/tough1985/RxjavaRetrofitDemo

選擇Tag -> step1

1.3 添加Rxjava

Retrofit本身對Rxjava提供了支持。

添加Retrofit對Rxjava的支持需要在Gradle文件中添加

compile'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4'

當(dāng)然我們已經(jīng)添加過了钮热。

然后在創(chuàng)建Retrofit的過程中添加如下代碼:

Retrofitretrofit=newRetrofit.Builder().baseUrl(baseUrl).addConverterFactory(GsonConverterFactory.create()).addCallAdapterFactory(RxJavaCallAdapterFactory.create()).build();

這樣一來我們定義的service返回值就不在是一個(gè)Call了填抬,而是一個(gè)Observable

重新定義MovieService

publicinterfaceMovieService{@GET("top250")ObservablegetTopMovie(@Query("start")intstart,@Query("count")intcount);}

getMovie方法改為:

//進(jìn)行網(wǎng)絡(luò)請求privatevoidgetMovie(){StringbaseUrl="https://api.douban.com/v2/movie/";Retrofitretrofit=newRetrofit.Builder().baseUrl(baseUrl).addConverterFactory(GsonConverterFactory.create()).addCallAdapterFactory(RxJavaCallAdapterFactory.create()).build();MovieServicemovieService=retrofit.create(MovieService.class);movieService.getTopMovie(0,10).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(newSubscriber(){@OverridepublicvoidonCompleted(){Toast.makeText(MainActivity.this,"Get Top Movie Completed",Toast.LENGTH_SHORT).show();}@OverridepublicvoidonError(Throwablee){resultTV.setText(e.getMessage());}@OverridepublicvoidonNext(MovieEntitymovieEntity){resultTV.setText(movieEntity.toString());}});}

這樣基本上就完成了Retrofit和Rxjava的結(jié)合,但是我知道你們當(dāng)然不會(huì)滿意的隧期。

接下來我們把創(chuàng)建Retrofit的過程封裝一下飒责,然后希望Activity創(chuàng)建Subscriber對象傳進(jìn)來。

如需查看項(xiàng)目代碼 --> 代碼地址:

https://github.com/tough1985/RxjavaRetrofitDemo

選擇Tag -> step2

1.4 將請求過程進(jìn)行封裝

創(chuàng)建一個(gè)對象HttpMethods

publicclassHttpMethods{publicstaticfinalStringBASE_URL="https://api.douban.com/v2/movie/";privatestaticfinalintDEFAULT_TIMEOUT=5;privateRetrofitretrofit;privateMovieServicemovieService;//構(gòu)造方法私有privateHttpMethods(){//手動(dòng)創(chuàng)建一個(gè)OkHttpClient并設(shè)置超時(shí)時(shí)間OkHttpClient.BuilderhttpClientBuilder=newOkHttpClient.Builder();httpClientBuilder.connectTimeout(DEFAULT_TIMEOUT,TimeUnit.SECONDS);retrofit=newRetrofit.Builder().client(httpClientBuilder.build()).addConverterFactory(GsonConverterFactory.create()).addCallAdapterFactory(RxJavaCallAdapterFactory.create()).baseUrl(BASE_URL).build();movieService=retrofit.create(MovieService.class);}//在訪問HttpMethods時(shí)創(chuàng)建單例privatestaticclassSingletonHolder{privatestaticfinalHttpMethodsINSTANCE=newHttpMethods();}//獲取單例publicstaticHttpMethodsgetInstance(){returnSingletonHolder.INSTANCE;}/**

* 用于獲取豆瓣電影Top250的數(shù)據(jù)

* @param subscriber 由調(diào)用者傳過來的觀察者對象

* @param start 起始位置

* @param count 獲取長度

*/publicvoidgetTopMovie(Subscribersubscriber,intstart,intcount){movieService.getTopMovie(start,count).subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(subscriber);}}

用一個(gè)單例來封裝該對象仆潮,在構(gòu)造方法中創(chuàng)建Retrofit和對應(yīng)的Service宏蛉。 如果需要訪問不同的基地址,那么你可能需要?jiǎng)?chuàng)建多個(gè)Retrofit對象性置,或者干脆根據(jù)不同的基地址封裝不同的HttpMethod類拾并。

我們回頭再來看MainActivity中的getMovie方法:

privatevoidgetMovie(){subscriber=newSubscriber(){@OverridepublicvoidonCompleted(){Toast.makeText(MainActivity.this,"Get Top Movie Completed",Toast.LENGTH_SHORT).show();}@OverridepublicvoidonError(Throwablee){resultTV.setText(e.getMessage());}@OverridepublicvoidonNext(MovieEntitymovieEntity){resultTV.setText(movieEntity.toString());}};HttpMethods.getInstance().getTopMovie(subscriber,0,10);}

其中subscriber是MainActivity的成員變量。

如需查看項(xiàng)目代碼 --> 代碼地址:

https://github.com/tough1985/RxjavaRetrofitDemo

選擇Tag -> step3

2.相同格式的Http請求數(shù)據(jù)該如何封裝

第二部分和第三部分我參考了知乎上的一個(gè)問答:RxJava+Retrofit,在聯(lián)網(wǎng)返回后如何先進(jìn)行統(tǒng)一的判斷嗅义?不過沒有完整的示例屏歹,所以在這寫一個(gè)完整的示例出來。

這個(gè)段落我們來聊一下有些Http服務(wù)返回一個(gè)固定格式的數(shù)據(jù)的問題之碗。 例如:

{"resultCode":0,"resultMessage":"成功","data":{}}

大部分的Http服務(wù)可能都是這樣設(shè)置蝙眶,resultCode和resultMessage的內(nèi)容相對比較穩(wěn)定,而data的內(nèi)容變化多端褪那,72變都不一定夠變的幽纷,有可能是個(gè)User對象,也有可能是個(gè)訂單對象博敬,還有可能是個(gè)訂單列表友浸。 按照我們之前的用法,使用Gson轉(zhuǎn)型需要我們在創(chuàng)建subscriber對象是指定返回值類型冶忱,如果我們對不同的返回值進(jìn)行封裝的話尾菇,那可能就要有上百個(gè)Entity了境析,看著明明是很清晰的結(jié)構(gòu)囚枪,卻因?yàn)閐ata的不確定性無奈了起來。

少年劳淆,不必?zé)懒凑樱瑏韥韥韣 老衲賜你寶典葵花,老衲就是練了這個(gè)才出家沛鸵。括勺。。

我們可以創(chuàng)建一個(gè)HttpResult類

publicclassHttpResult{privateintresultCode;privateStringresultMessage;privateT data;}

如果data是一個(gè)User對象的話曲掰。那么在定義Service方法的返回值就可以寫為

Observable>

這樣一來HttpResult就相當(dāng)于一個(gè)包裝類疾捍,將結(jié)果包裝了起來,但是在使用的時(shí)候要給出一個(gè)明確的類型栏妖。

在上面的示例中乱豆,我也創(chuàng)建了一個(gè)HttpResult類,用來模仿這個(gè)形式吊趾,將其中的Subject單獨(dú)封裝了起來。

publicclassHttpResult{//用來模仿resultCode和resultMessageprivateintcount;privateintstart;privateinttotal;privateStringtitle;//用來模仿DataprivateT subjects;}

這樣泛型的時(shí)候就要寫為:

Observable>>

如需查看項(xiàng)目代碼 --> 代碼地址:

https://github.com/tough1985/RxjavaRetrofitDemo

選擇Tag -> step4

3.相同格式的Http請求數(shù)據(jù)統(tǒng)一進(jìn)行預(yù)處理

既然我們有了相同的返回格式屁奏,那么我們可能就需要在獲得數(shù)據(jù)之后進(jìn)行一個(gè)統(tǒng)一的預(yù)處理湿颅。

當(dāng)接收到了一個(gè)Http請求結(jié)果之后怀浆,由于返回的結(jié)構(gòu)統(tǒng)一為

{"resultCode":0,"resultMessage":"成功","data":{}}

我們想要對resultCoderesultMessage先做一個(gè)判斷沙合,因?yàn)槿绻鹯esultCode == 0代表success,那么resultCode != 0時(shí)data一般都是null

Activity或Fragment對resultCoderesultMessage基本沒有興趣,他們只對請求狀態(tài)data數(shù)據(jù)感興趣。

基于這種考慮,我們在resultCode != 0的時(shí)候,拋出個(gè)自定義的ApiException。這樣就會(huì)進(jìn)入到subscriber的onError中乓梨,我們可以在onError中處理錯(cuò)誤信息臭觉。

另外什乙,請求成功時(shí),需要將data數(shù)據(jù)轉(zhuǎn)換為目標(biāo)數(shù)據(jù)類型傳遞給subscriber亦鳞,因?yàn)橥吆簦珹ctivity和Fragment只想拿到和他們真正相關(guān)的數(shù)據(jù)。

使用Observable的map方法可以完成這一功能饲宿。

HttpMethods中創(chuàng)建一個(gè)內(nèi)部類HttpResultFunc厦酬,代碼如下:

/**

* 用來統(tǒng)一處理Http的resultCode,并將HttpResult的Data部分剝離出來返回給subscriber

*

* @param Subscriber真正需要的數(shù)據(jù)類型仗阅,也就是Data部分的數(shù)據(jù)類型

*/privateclassHttpResultFuncimplementsFunc1,T>{@OverridepublicT call(HttpResulthttpResult){if(httpResult.getResultCode()!=0){thrownewApiException(httpResult.getResultCode());}returnhttpResult.getData();}}

然后我們的getTopMovie方法改為:

publicvoidgetTopMovie(Subscriber>subscriber,intstart,intcount){movieService.getTopMovie(start,count).map(newHttpResultFunc>()).subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(subscriber);}

由于HttpResult中的泛型T就是我們希望傳遞給subscriber的數(shù)據(jù)類型,而數(shù)據(jù)可以通過httpResult的getData方法獲得扎运,這樣我們就處理了泛型問題扯罐,錯(cuò)誤處理問題负拟,還有將請求數(shù)據(jù)部分剝離出來給subscriber

這樣我們只需要關(guān)注Data數(shù)據(jù)的類型,而不必在關(guān)心整個(gè)過程了秸歧。

需要注意一點(diǎn)厨姚,就是在定義Service的時(shí)候,泛型是

HttpResult//orHttpResult>

而在定義Subscriber的時(shí)候泛型是java User //or List

不然你會(huì)得到一個(gè)轉(zhuǎn)型錯(cuò)誤键菱。

如需查看項(xiàng)目代碼 --> 代碼地址:

https://github.com/tough1985/RxjavaRetrofitDemo

選擇Tag -> step5

代碼中我是用豆瓣數(shù)據(jù)模擬了HttpResult中的resultCode和resultMessage谬墙,與文檔中的代碼略有出入。

4.如何取消一個(gè)Http請求 -- 觀察者之間的對決经备,Observer VS Subscriber

4.1 取消一個(gè)Http請求

這一部分我們來聊一下關(guān)于取消Http請求的事情拭抬,已經(jīng)Oberver和Subscriber這兩個(gè)體位我們哪個(gè)更容易給我們G點(diǎn)。

如果沒有使用Rxjava侵蒙,那么Service返回的是一個(gè)Call造虎,而這個(gè)Call對象有一個(gè)cancel方法可以用來取消Http請求。那么用了Rxjava之后蘑志,如何來取消一個(gè)請求呢累奈?因?yàn)榉祷刂凳且粋€(gè)Observable贬派。我們能做的似乎只有解除對Observable對象的訂閱,其他的什么也做不了澎媒。

好在Retrofit已經(jīng)幫我們考慮到了這一點(diǎn)搞乏。 答案在RxJavaCallAdapterFactory這個(gè)類的源碼中可以找到

staticfinalclassCallOnSubscribeimplementsObservable.OnSubscribe>{privatefinalCalloriginalCall;CallOnSubscribe(CalloriginalCall){this.originalCall=originalCall;}@Overridepublicvoidcall(finalSubscriber>subscriber){// Since Call is a one-shot type, clone it for each new subscriber.finalCallcall=originalCall.clone();// Attempt to cancel the call if it is still in-flight on unsubscription.subscriber.add(Subscriptions.create(newAction0(){@Overridepublicvoidcall(){call.cancel();}}));try{Responseresponse=call.execute();if(!subscriber.isUnsubscribed()){subscriber.onNext(response);}}catch(Throwablet){Exceptions.throwIfFatal(t);if(!subscriber.isUnsubscribed()){subscriber.onError(t);}return;}if(!subscriber.isUnsubscribed()){subscriber.onCompleted();}}}

我們看到call方法中,給subscriber添加了一個(gè)Subscription對象戒努,Subscription對象很簡單请敦,主要就是取消訂閱用的,如果你查看Subscriptions.create的源碼储玫,發(fā)現(xiàn)是這樣的

publicstaticSubscriptioncreate(finalAction0unsubscribe){returnBooleanSubscription.create(unsubscribe);}

利用了一個(gè)BooleanSubscription類來創(chuàng)建一個(gè)Subscription侍筛,如果你點(diǎn)進(jìn)去看BooleanSubscription.create方法一切就清晰了,當(dāng)接觸綁定的時(shí)候撒穷,subscriber會(huì)調(diào)用Subscription的unsubscribe方法匣椰,然后觸發(fā)創(chuàng)建Subscription時(shí)候的傳遞進(jìn)來的Action0的call方法。RxJavaCallAdapterFactory幫我們給subscriber添加的是call.cancel()端礼,

總結(jié)起來就是說禽笑,我們在Activity或者Fragment中創(chuàng)建subscriber對象,想要取消請求的時(shí)候調(diào)用subscriber的unsubscribe方法就可以了蛤奥。

對不起這一節(jié)有太多的SubscriberSubscription以及ObserverObservable佳镜,老衲當(dāng)時(shí)看的時(shí)候也是不知道吐了多少次了,習(xí)慣了就好了凡桥。

4.2 為什么會(huì)提到Oberver

提到Observer的過程是這樣的蟀伸。由于Subscriber一旦調(diào)用了unsubscribe方法之后,就沒有用了缅刽。且當(dāng)事件傳遞到onError或者onCompleted之后啊掏,也會(huì)自動(dòng)的解綁。這樣出現(xiàn)的一個(gè)問題就是每次發(fā)送請求都要?jiǎng)?chuàng)建新的Subscriber對象拷恨。

這樣我們就把注意力放到了Observer脖律,Observer本身是一個(gè)接口谢肾,他的特性是不管你怎么用腕侄,都不會(huì)解綁,為什么呢芦疏?因?yàn)樗麤]有解綁的方法冕杠。所以就達(dá)到了復(fù)用的效果,一開始我一直美滋滋的用Observer酸茴。事實(shí)上分预,如果你用的是Observer,在調(diào)用Observable對象的subscribe方法的時(shí)候薪捍,會(huì)自動(dòng)的將Observer對象轉(zhuǎn)換成Subscriber對象笼痹。

下面是源碼:

publicfinalSubscriptionsubscribe(finalObserverobserver){if(observer instanceofSubscriber){returnsubscribe((Subscriber)observer);}returnsubscribe(newSubscriber(){@OverridepublicvoidonCompleted(){observer.onCompleted();}@OverridepublicvoidonError(Throwablee){observer.onError(e);}@OverridepublicvoidonNext(T t){observer.onNext(t);}});}

后來發(fā)現(xiàn)了問題配喳,

問題1 無法取消,因?yàn)镺bserver沒有unsubscribe方法 問題2 沒有onStart方法 這個(gè)一會(huì)聊

這兩個(gè)問題是很痛苦的凳干。所以晴裹,為了后面更好的高潮,我們還是選擇用Subscriber救赐。

5.一個(gè)需要ProgressDialog的Subscriber該有的樣子

我們希望有一個(gè)Subscriber在我們每次發(fā)送請求的時(shí)候能夠彈出一個(gè)ProgressDialog涧团,然后在請求接受的時(shí)候讓這個(gè)ProgressDialog消失,同時(shí)在我們?nèi)∠@個(gè)ProgressDialog的同時(shí)能夠取消當(dāng)前的請求经磅,而我們只需要處理里面的數(shù)據(jù)就可以了泌绣。

我們先來創(chuàng)建一個(gè)類,就叫ProgressSubscriber预厌,讓他繼承Subscriber阿迈。

Subscriber給我們提供了onStart、onNext轧叽、onError仿滔、onCompleted四個(gè)方法。

其中只有onNext方法返回了數(shù)據(jù)犹芹,那我們自然希望能夠在onNext里面處理數(shù)據(jù)相關(guān)的邏輯崎页。

onStart方法我們用來啟動(dòng)一個(gè)ProgressDialog。onError方法我們集中處理錯(cuò)誤腰埂,同時(shí)也停止ProgressDialogonComplated方法里面停止ProgressDialog

其中我們需要解決兩個(gè)問題

問題1 onNext的處理 問題2 cancel掉一個(gè)ProgressDialog的時(shí)候取消請求

我們先來解決問題1

5.1處理onNext

我們希望這里能夠讓Activity或者Fragment自己處理onNext之后的邏輯飒焦,很自然的我們想到了用接口。問題還是泛型的問題屿笼,這里面我們必須指定明確的類型牺荠。所以接口還是需要泛型。

我們先來定義一個(gè)接口驴一,命名SubscriberOnNextListener

publicinterfaceSubscriberOnNextListener{voidonNext(T t);}

代碼很簡單休雌。再來看一下ProgressSubscriber現(xiàn)在的代碼

publicclassProgressSubscriberextendsSubscriber{privateSubscriberOnNextListenermSubscriberOnNextListener;privateContextcontext;publicProgressSubscriber(SubscriberOnNextListenermSubscriberOnNextListener,Contextcontext){this.mSubscriberOnNextListener=mSubscriberOnNextListener;this.context=context;}@OverridepublicvoidonStart(){}@OverridepublicvoidonCompleted(){Toast.makeText(context,"Get Top Movie Completed",Toast.LENGTH_SHORT).show();}@OverridepublicvoidonError(Throwablee){Toast.makeText(context,"error:"+e.getMessage(),Toast.LENGTH_SHORT).show();}@OverridepublicvoidonNext(T t){mSubscriberOnNextListener.onNext(t);}}

我知道傳Context不好,不過為了演示而已肝断,大家可以自己封裝一下Toast杈曲。

MainActivity使用是這樣的:

先來定義一個(gè)SubscriberOnNextListener對象,可以在onCreate里面創(chuàng)建這個(gè)對象

privateSubscriberOnNextListenergetTopMovieOnNext;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ButterKnife.bind(this);getTopMovieOnNext=newSubscriberOnNextListener>(){@OverridepublicvoidonNext(Listsubjects){resultTV.setText(subjects.toString());}};}

getMovie方法這么寫:

privatevoidgetMovie(){HttpMethods.getInstance().getTopMovie(newProgressSubscriber(getTopMovieOnNext,MainActivity.this),0,10);}

這樣Activity或Fragment就只需要關(guān)注拿到結(jié)果之后的邏輯了胸懈,其他的完全不用操心担扑。

如需查看項(xiàng)目代碼 --> 代碼地址:

https://github.com/tough1985/RxjavaRetrofitDemo

選擇Tag -> step6

5.2處理ProgressDialog

我們希望當(dāng)cancel掉ProgressDialog的時(shí)候,能夠取消訂閱趣钱,也就取消了當(dāng)前的Http請求涌献。 所以我們先來創(chuàng)建個(gè)接口來處理這件事情。

publicinterfaceProgressCancelListener{voidonCancelProgress();}

然后我們用ProgressSubscriber來實(shí)現(xiàn)這個(gè)接口首有,這樣ProgressSubscriber就有了一個(gè)onCancelProgress方法燕垃,在這里面取消訂閱枢劝。

@OverridepublicvoidonCancelProgress(){if(!this.isUnsubscribed()){this.unsubscribe();}}

然后我用了一個(gè)Handler來封裝了ProgressDialog。

publicclassProgressDialogHandlerextendsHandler{publicstaticfinalintSHOW_PROGRESS_DIALOG=1;publicstaticfinalintDISMISS_PROGRESS_DIALOG=2;privateProgressDialogpd;privateContextcontext;privateboolean cancelable;privateProgressCancelListenermProgressCancelListener;publicProgressDialogHandler(Contextcontext,ProgressCancelListenermProgressCancelListener,boolean cancelable){super();this.context=context;this.mProgressCancelListener=mProgressCancelListener;this.cancelable=cancelable;}privatevoidinitProgressDialog(){if(pd==null){pd=newProgressDialog(context);pd.setCancelable(cancelable);if(cancelable){pd.setOnCancelListener(newDialogInterface.OnCancelListener(){@OverridepublicvoidonCancel(DialogInterfacedialogInterface){mProgressCancelListener.onCancelProgress();}});}if(!pd.isShowing()){pd.show();}}}privatevoiddismissProgressDialog(){if(pd!=null){pd.dismiss();pd=null;}}@OverridepublicvoidhandleMessage(Messagemsg){switch(msg.what){caseSHOW_PROGRESS_DIALOG:initProgressDialog();break;caseDISMISS_PROGRESS_DIALOG:dismissProgressDialog();break;}}}

Handler接收兩個(gè)消息來控制顯示Dialog還是關(guān)閉Dialog卜壕。 創(chuàng)建Handler的時(shí)候我們需要傳入ProgressCancelListener的對象實(shí)例呈野。

最后貼出ProgressSubscriber的完整代碼:

publicclassProgressSubscriberextendsSubscriberimplementsProgressCancelListener{privateSubscriberOnNextListenermSubscriberOnNextListener;privateProgressDialogHandlermProgressDialogHandler;privateContextcontext;publicProgressSubscriber(SubscriberOnNextListenermSubscriberOnNextListener,Contextcontext){this.mSubscriberOnNextListener=mSubscriberOnNextListener;this.context=context;mProgressDialogHandler=newProgressDialogHandler(context,this,true);}privatevoidshowProgressDialog(){if(mProgressDialogHandler!=null){mProgressDialogHandler.obtainMessage(ProgressDialogHandler.SHOW_PROGRESS_DIALOG).sendToTarget();}}privatevoiddismissProgressDialog(){if(mProgressDialogHandler!=null){mProgressDialogHandler.obtainMessage(ProgressDialogHandler.DISMISS_PROGRESS_DIALOG).sendToTarget();mProgressDialogHandler=null;}}@OverridepublicvoidonStart(){showProgressDialog();}@OverridepublicvoidonCompleted(){dismissProgressDialog();Toast.makeText(context,"Get Top Movie Completed",Toast.LENGTH_SHORT).show();}@OverridepublicvoidonError(Throwablee){dismissProgressDialog();Toast.makeText(context,"error:"+e.getMessage(),Toast.LENGTH_SHORT).show();}@OverridepublicvoidonNext(T t){mSubscriberOnNextListener.onNext(t);}@OverridepublicvoidonCancelProgress(){if(!this.isUnsubscribed()){this.unsubscribe();}}}

目前為止,就封裝完畢了印叁。以上是我在用Rxjava和Retrofit過程中踩過的一些坑被冒,最后整合出來的,由于沒有在實(shí)際的項(xiàng)目中跑過轮蜕,有問題的話希望能夠提出來大家討論一下昨悼,拍磚也歡迎。

現(xiàn)在我們再寫一個(gè)新的網(wǎng)絡(luò)請求跃洛,步驟是這樣的: 1. 在Service中定義一個(gè)新的方法率触。 2. 在HttpMethods封裝對應(yīng)的請求(代碼基本可以copy) 3. 創(chuàng)建一個(gè)SubscriberOnNextListener處理請求數(shù)據(jù)并刷新UI。

最后

如果你覺得寫更改線程的代碼覺得也很煩的話汇竭,可以把訂閱這部分也封裝起來:

publicvoidgetTopMovie(Subscriber>subscriber,intstart,intcount){//原來的樣子// movieService.getTopMovie(start, count)// .map(new HttpResultFunc>())// .subscribeOn(Schedulers.io())// .unsubscribeOn(Schedulers.io())// .observeOn(AndroidSchedulers.mainThread())// .subscribe(subscriber);//修改之后的樣子Observableobservable=movieService.getTopMovie(start,count).map(newHttpResultFunc>());toSubscribe(observable,subscriber);}//添加線程管理并訂閱privatevoidtoSubscribe(Observableo,Subscribers){o.subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(s);}

讓你每次寫一個(gè)請求的時(shí)候葱蝗,寫的代碼盡量少,更多的精力放在業(yè)務(wù)邏輯本身细燎。

最后的最后

如果你的httpResult格式本身沒有問題两曼,但是data中的內(nèi)容是這樣的:

{"resultCode":0,"resultMessage":"成功","data":{"user":{},"orderArray":[]}}

這樣的情況還能不能繼續(xù)使用這樣的框架呢? 我的解決方法是封裝一個(gè)類玻驻,把user和orderArray作為類的屬性悼凑。 但是如果你的服務(wù)器一會(huì)data本身是一個(gè)完整的user數(shù)據(jù),一會(huì)又是這樣:"data": {"user": {}, "orderArray": []}那我覺得你有必要跟你的服務(wù)端好好聊聊了璧瞬,請他吃頓飯和頓酒户辫,大不了獻(xiàn)出菊花就是了。

但是如果服務(wù)已經(jīng)上線了`惋薄S婊丁!


來自:

http://gank.io/post/56e80c2c677659311bed9841

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瘟忱,一起剝皮案震驚了整個(gè)濱河市奥额,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌酷誓,老刑警劉巖披坏,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異盐数,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)伞梯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進(jìn)店門玫氢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來帚屉,“玉大人,你說我怎么就攤上這事漾峡」サ” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵生逸,是天一觀的道長牢屋。 經(jīng)常有香客問我,道長槽袄,這世上最難降的妖魔是什么烙无? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮遍尺,結(jié)果婚禮上截酷,老公的妹妹穿的比我還像新娘。我一直安慰自己乾戏,他們只是感情好迂苛,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鼓择,像睡著了一般三幻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上呐能,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天赌髓,我揣著相機(jī)與錄音,去河邊找鬼催跪。 笑死锁蠕,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的懊蒸。 我是一名探鬼主播荣倾,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼骑丸!你這毒婦竟也來了舌仍?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤通危,失蹤者是張志新(化名)和其女友劉穎铸豁,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體菊碟,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡节芥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片头镊。...
    茶點(diǎn)故事閱讀 40,144評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蚣驼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出相艇,到底是詐尸還是另有隱情颖杏,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布坛芽,位于F島的核電站留储,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏咙轩。R本人自食惡果不足惜获讳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望臭墨。 院中可真熱鬧赔嚎,春花似錦、人聲如沸胧弛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽结缚。三九已至损晤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間红竭,已是汗流浹背尤勋。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留茵宪,地道東北人最冰。 一個(gè)月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像稀火,于是被迫代替她去往敵國和親暖哨。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內(nèi)容