RxJava 與 Retrofit 結合的最佳實踐

前言

RxJava和Retrofit也火了一段時間了,不過最近一直在學習ReactNative和Node相關的姿式,一直沒有時間研究這些新東西,最近有個項目準備寫,打算先用Android寫一個Demo出來,卻發現Android的世界發生了天翻地覆的變化,EventBus和OKHttp啥的都不見了,RxJava和Retrofit是什麼鬼?java

好吧,到Github上耐着性子看過了RxJava和Retrofit的介紹和幾個Demo,原來Android的大神Jake Wharton爲Retrofit這個項目貢獻了這麼多的代碼,沒有道理不用了。react

若是你對RxJava不熟悉請先看給 Android 開發者的 RxJava 詳解這篇文章。android

若是你對Retrofit不熟悉就先看Retrofit官網git

固然也有不少RxJava與Retrofit的文章,可是我以爲不少你們都很糾結的功能都沒有被總結出來,因此纔有了此篇文章。github

歡迎你們拍磚。api

接下來進入正文,我是從下面幾個角度去思考RxJava與Retrofit結合的。服務器

  1. RxJava如何與Retrofit結合
  2. 相同格式的Http請求數據該如何封裝
  3. 相同格式的Http請求數據統一進行預處理
  4. 如何取消一個Http請求 -- 觀察者之間的對決,Oberver VS Subscriber
  5. 一個須要ProgressDialog的Subscriber該有的樣子

1.RxJava如何與Retrofit結合

1.1 基本頁面

先扔出build.gradle文件的內容網絡

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來進行的。 添加rxandroid是由於rxjava中的線程問題。app

下面先搭建一個基本的頁面,頁面很簡單,先來看文件目錄結構目錄結構框架

activity_main.xml的代碼以下:

<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".activity.MainActivity"> <Button android:id="@+id/click_me_BN" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:padding="5dp" android:text="點我" android:textSize="16sp"/> <TextView android:id="@+id/result_TV" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@id/click_me_BN" android:text="Hello World!" android:textSize="16sp"/> </RelativeLayout>

MainActivity.java的代碼以下:

package com.queen.rxjavaretrofitdemo.activity; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.widget.Button; import android.widget.TextView; import com.queen.rxjavaretrofitdemo.R; import butterknife.Bind; import butterknife.ButterKnife; import butterknife.OnClick; public class MainActivity extends AppCompatActivity { @Bind(R.id.click_me_BN) Button clickMeBN; @Bind(R.id.result_TV) TextView resultTV; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); } @OnClick(R.id.click_me_BN) public void onClick() { getMovie(); } //進行網絡請求 private void getMovie(){ } }

注意不要忘記加網絡權限

<uses-permission android:name="android.permission.INTERNET"/>

1.2 只用Retrofit

咱們準備在getMovie方法中進行網絡請求,咱們先來看看只使用Retrofit是如何進行的。

咱們使用豆瓣電影的Top250作測試鏈接,目標地址爲

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

至於返回的數據格式,你們本身訪問下連接就看到了,太長就不放進來了。

首先咱們要根據返回的結果封裝一個Entity,暫命名爲MovieEntity,代碼就不貼了。

接下來咱們要建立一個接口取名爲MovieService,代碼以下:

public interface MovieService { @GET("top250") Call<MovieEntity> getTopMovie(@Query("start") int start, @Query("count") int count); }

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

//進行網絡請求 private void getMovie(){ String baseUrl = "https://api.douban.com/v2/movie/"; Retrofit retrofit = new Retrofit.Builder() .baseUrl(baseUrl) .addConverterFactory(GsonConverterFactory.create()) .build(); MovieService movieService = retrofit.create(MovieService.class); Call<MovieEntity> call = movieService.getTopMovie(0, 10); call.enqueue(new Callback<MovieEntity>() { @Override public void onResponse(Call<MovieEntity> call, Response<MovieEntity> response) { resultTV.setText(response.body().toString()); } @Override public void onFailure(Call<MovieEntity> call, Throwable t) { resultTV.setText(t.getMessage()); } }); }

以上爲沒有通過封裝的、原生態的Retrofit寫網絡請求的代碼。 咱們能夠封裝建立Retrofit和service部分的代碼,而後Activity用建立一個Callback做爲參數給Call,這樣Activity中只關注請求的結果,並且Call有cancel方法能夠取消一個請求,好像沒Rxjava什麼事了,我以爲能夠寫到這就下班了~

接下來咱們要面對的問題是這樣的 若是個人Http返回數據是一個統一的格式,例如

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

咱們如何對返回結果進行一個統一的處理呢?

另外,個人ProgressDialog的show方法應該在哪調用呢?看樣子只能在getMovie()這個方法裏面調用了,換個地方發出請求就要在對應的Listener裏面寫一遍show()的代碼,其實挺鬧心。

並且錯誤請求我也想集中處理掉不要貼重複的代碼。

咱們先來看結合了Rxjava以後,事情有沒有變化的可能。固然即使是不用Rxjava,依舊可以作不少的封裝,只是比較麻煩。

如需查看項目代碼 --> 代碼地址:

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'

固然咱們已經添加過了。

而後在建立Retrofit的過程當中添加以下代碼:

Retrofit retrofit = new Retrofit.Builder() .baseUrl(baseUrl) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build();

這樣一來咱們定義的service返回值就不在是一個Call了,而是一個Observable

從新定義MovieService

public interface MovieService { @GET("top250") Observable<MovieEntity> getTopMovie(@Query("start") int start, @Query("count") int count); }

getMovie方法改成:

//進行網絡請求 private void getMovie(){ String baseUrl = "https://api.douban.com/v2/movie/"; Retrofit retrofit = new Retrofit.Builder() .baseUrl(baseUrl) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build(); MovieService movieService = retrofit.create(MovieService.class); movieService.getTopMovie(0, 10) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<MovieEntity>() { @Override public void onCompleted() { Toast.makeText(MainActivity.this, "Get Top Movie Completed", Toast.LENGTH_SHORT).show(); } @Override public void onError(Throwable e) { resultTV.setText(e.getMessage()); } @Override public void onNext(MovieEntity movieEntity) { resultTV.setText(movieEntity.toString()); } }); }

這樣基本上就完成了Retrofit和Rxjava的結合,可是我知道大家固然不會滿意的。

接下來咱們把建立Retrofit的過程封裝一下,而後但願Activity建立Subscriber對象傳進來。

如需查看項目代碼 --> 代碼地址:

https://github.com/tough1985/RxjavaRetrofitDemo

選擇Tag -> step2

1.4 將請求過程進行封裝

建立一個對象HttpMethods

public class HttpMethods { public static final String BASE_URL = "https://api.douban.com/v2/movie/"; private static final int DEFAULT_TIMEOUT = 5; private Retrofit retrofit; private MovieService movieService; //構造方法私有 private HttpMethods() { //手動建立一個OkHttpClient並設置超時時間 OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(); httpClientBuilder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS); retrofit = new Retrofit.Builder() .client(httpClientBuilder.build()) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .baseUrl(BASE_URL) .build(); movieService = retrofit.create(MovieService.class); } //在訪問HttpMethods時建立單例 private static class SingletonHolder{ private static final HttpMethods INSTANCE = new HttpMethods(); } //獲取單例 public static HttpMethods getInstance(){ return SingletonHolder.INSTANCE; } /** * 用於獲取豆瓣電影Top250的數據 * @param subscriber 由調用者傳過來的觀察者對象 * @param start 起始位置 * @param count 獲取長度 */ public void getTopMovie(Subscriber<MovieEntity> subscriber, int start, int count){ movieService.getTopMovie(start, count) .subscribeOn(Schedulers.io()) .unsubscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(subscriber); } }

用一個單例來封裝該對象,在構造方法中建立Retrofit和對應的Service。 若是須要訪問不一樣的基地址,那麼你可能須要建立多個Retrofit對象,或者乾脆根據不一樣的基地址封裝不一樣的HttpMethod類。

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

private void getMovie(){ subscriber = new Subscriber<MovieEntity>() { @Override public void onCompleted() { Toast.makeText(MainActivity.this, "Get Top Movie Completed", Toast.LENGTH_SHORT).show(); } @Override public void onError(Throwable e) { resultTV.setText(e.getMessage()); } @Override public void onNext(MovieEntity movieEntity) { resultTV.setText(movieEntity.toString()); } }; HttpMethods.getInstance().getTopMovie(subscriber, 0, 10); }

其中subscriber是MainActivity的成員變量。

如需查看項目代碼 --> 代碼地址:

https://github.com/tough1985/RxjavaRetrofitDemo

選擇Tag -> step3

2.相同格式的Http請求數據該如何封裝

第二部分和第三部分我參考了知乎上的一個問答: RxJava+Retrofit,在聯網返回後如何先進行統一的判斷? 不過沒有完整的示例,因此在這寫一個完整的示例出來。

這個段落咱們來聊一下有些Http服務返回一個固定格式的數據的問題。 例如:

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

大部分的Http服務可能都是這樣設置,resultCode和resultMessage的內容相對比較穩定,而data的內容變化無窮,72變都不必定夠變的,有多是個User對象,也有多是個訂單對象,還有多是個訂單列表。 按照咱們以前的用法,使用Gson轉型須要咱們在建立subscriber對象是指定返回值類型,若是咱們對不一樣的返回值進行封裝的話,那可能就要有上百個Entity了,看着明明是很清晰的結構,卻由於data的不肯定性無奈了起來。

少年,沒必要煩惱,來來來~ 老衲賜你寶典葵花,老衲就是練了這個纔出家。。。

咱們能夠建立一個HttpResult類

public class HttpResult<T> { private int resultCode; private String resultMessage; private T data; }

若是data是一個User對象的話。那麼在定義Service方法的返回值就能夠寫爲

Observable<HttpResult<User>>

這樣一來HttpResult就至關於一個包裝類,將結果包裝了起來,可是在使用的時候要給出一個明確的類型。

在上面的示例中,我也建立了一個HttpResult類,用來模仿這個形式,將其中的Subject單獨封裝了起來。

public class HttpResult<T> { //用來模仿resultCode和resultMessage private int count; private int start; private int total; private String title; //用來模仿Data private T subjects; }

這樣泛型的時候就要寫爲:

Observable<HttpResult<List<Subject>>>

如需查看項目代碼 --> 代碼地址:

https://github.com/tough1985/RxjavaRetrofitDemo

選擇Tag -> step4

3.相同格式的Http請求數據統一進行預處理

既然咱們有了相同的返回格式,那麼咱們可能就須要在得到數據以後進行一個統一的預處理。

當接收到了一個Http請求結果以後,因爲返回的結構統一爲

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

咱們想要對resultCoderesultMessage先作一個判斷,由於若是resultCode == 0表明success,那麼resultCode != 0data通常都是null

Activity或Fragment對resultCoderesultMessage基本沒有興趣,他們只對請求狀態data數據感興趣。

基於這種考慮,咱們在resultCode != 0的時候,拋出個自定義的ApiException。這樣就會進入到subscriber的onError中,咱們能夠在onError中處理錯誤信息。

另外,請求成功時,須要將data數據轉換爲目標數據類型傳遞給subscriber,由於,Activity和Fragment只想拿到和他們真正相關的數據。

使用Observable的map方法能夠完成這一功能。

HttpMethods中建立一個內部類HttpResultFunc,代碼以下:

/** * 用來統一處理Http的resultCode,並將HttpResult的Data部分剝離出來返回給subscriber * * @param <T> Subscriber真正須要的數據類型,也就是Data部分的數據類型 */ private class HttpResultFunc<T> implements Func1<HttpResult<T>, T>{ @Override public T call(HttpResult<T> httpResult) { if (httpResult.getResultCode() != 0) { throw new ApiException(httpResult.getResultCode()); } return httpResult.getData(); } }

而後咱們的getTopMovie方法改成:

public void getTopMovie(Subscriber<List<Subject>> subscriber, int start, int count){ movieService.getTopMovie(start, count) .map(new HttpResultFunc<List<Subject>>()) .subscribeOn(Schedulers.io()) .unsubscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(subscriber); }

因爲HttpResult中的泛型T就是咱們但願傳遞給subscriber的數據類型,而數據能夠經過httpResult的getData方法得到,這樣咱們就處理了泛型問題,錯誤處理問題,還有將請求數據部分剝離出來給subscriber

這樣咱們只須要關注Data數據的類型,而沒必要在關心整個過程了。

須要注意一點,就是在定義Service的時候,泛型是

HttpResult<User> //or HttpResult<List<Subject>>

而在定義Subscriber的時候泛型是 java User //or List<Subject>

否則你會獲得一個轉型錯誤。

如需查看項目代碼 --> 代碼地址:

https://github.com/tough1985/RxjavaRetrofitDemo

選擇Tag -> step5

代碼中我是用豆瓣數據模擬了HttpResult中的resultCode和resultMessage,與文檔中的代碼略有出入。

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

4.1 取消一個Http請求

這一部分咱們來聊一下關於取消Http請求的事情,已經Oberver和Subscriber這兩個體位咱們哪一個更容易給咱們G點。

若是沒有使用Rxjava,那麼Service返回的是一個Call,而這個Call對象有一個cancel方法能夠用來取消Http請求。那麼用了Rxjava以後,如何來取消一個請求呢?由於返回值是一個Observable。咱們能作的彷佛只有解除對Observable對象的訂閱,其餘的什麼也作不了。

好在Retrofit已經幫咱們考慮到了這一點。 答案在RxJavaCallAdapterFactory這個類的源碼中能夠找到

static final class CallOnSubscribe<T> implements Observable.OnSubscribe<Response<T>> { private final Call<T> originalCall; CallOnSubscribe(Call<T> originalCall) { this.originalCall = originalCall; } @Override public void call(final Subscriber<? super Response<T>> subscriber) { // Since Call is a one-shot type, clone it for each new subscriber. final Call<T> call = originalCall.clone(); // Attempt to cancel the call if it is still in-flight on unsubscription. subscriber.add(Subscriptions.create(new Action0() { @Override public void call() { call.cancel(); } })); try { Response<T> response = call.execute(); if (!subscriber.isUnsubscribed()) { subscriber.onNext(response); } } catch (Throwable t) { Exceptions.throwIfFatal(t); if (!subscriber.isUnsubscribed()) { subscriber.onError(t); } return; } if (!subscriber.isUnsubscribed()) { subscriber.onCompleted(); } } }

咱們看到call方法中,給subscriber添加了一個Subscription對象,Subscription對象很簡單,主要就是取消訂閱用的,若是你查看Subscriptions.create的源碼,發現是這樣的

public static Subscription create(final Action0 unsubscribe) { return BooleanSubscription.create(unsubscribe); }

利用了一個BooleanSubscription類來建立一個Subscription,若是你點進去看BooleanSubscription.create方法一切就清晰了,當接觸綁定的時候,subscriber會調用Subscription的unsubscribe方法,而後觸發建立Subscription時候的傳遞進來的Action0的call方法。RxJavaCallAdapterFactory幫咱們給subscriber添加的是call.cancel(),

總結起來就是說,咱們在Activity或者Fragment中建立subscriber對象,想要取消請求的時候調用subscriber的unsubscribe方法就能夠了。

對不起這一節有太多的SubscriberSubscription以及ObserverObservable,老衲當時看的時候也是不知道吐了多少次了,習慣了就行了。

4.2 爲何會提到Oberver

提到Observer的過程是這樣的。因爲Subscriber一旦調用了unsubscribe方法以後,就沒有用了。且當事件傳遞到onError或者onCompleted以後,也會自動的解綁。這樣出現的一個問題就是每次發送請求都要建立新的Subscriber對象。

這樣咱們就把注意力放到了Observer,Observer自己是一個接口,他的特性是無論你怎麼用,都不會解綁,爲何呢?由於他沒有解綁的方法。因此就達到了複用的效果,一開始我一直美滋滋的用Observer。事實上,若是你用的是Observer,在調用Observable對象的subscribe方法的時候,會自動的將Observer對象轉換成Subscriber對象。

下面是源碼:

public final Subscription subscribe(final Observer<? super T> observer) { if (observer instanceof Subscriber) { return subscribe((Subscriber<? super T>)observer); } return subscribe(new Subscriber<T>() { @Override public void onCompleted() { observer.onCompleted(); } @Override public void onError(Throwable e) { observer.onError(e); } @Override public void onNext(T t) { observer.onNext(t); } }); }

後來發現了問題,

問題1 沒法取消,由於Observer沒有unsubscribe方法 問題2 沒有onStart方法 這個一會聊

這兩個問題是很痛苦的。因此,爲了後面更好的高潮,咱們仍是選擇用Subscriber。

5.一個須要ProgressDialog的Subscriber該有的樣子

咱們但願有一個Subscriber在咱們每次發送請求的時候可以彈出一個ProgressDialog,而後在請求接受的時候讓這個ProgressDialog消失,同時在咱們取消這個ProgressDialog的同時可以取消當前的請求,而咱們只須要處理裏面的數據就能夠了。

咱們先來建立一個類,就叫ProgressSubscriber,讓他繼承Subscriber

Subscriber給咱們提供了onStart、onNext、onError、onCompleted四個方法。

其中只有onNext方法返回了數據,那咱們天然但願可以在onNext裏面處理數據相關的邏輯。

onStart方法咱們用來啓動一個ProgressDialog。 onError方法咱們集中處理錯誤,同時也中止ProgressDialog onComplated方法裏面中止ProgressDialog

其中咱們須要解決兩個問題

問題1 onNext的處理 問題2 cancel掉一個ProgressDialog的時候取消請求

咱們先來解決問題1

5.1處理onNext

咱們但願這裏可以讓Activity或者Fragment本身處理onNext以後的邏輯,很天然的咱們想到了用接口。問題仍是泛型的問題,這裏面咱們必須指定明確的類型。因此接口仍是須要泛型。

咱們先來定義一個接口,命名SubscriberOnNextListener

public interface SubscriberOnNextListener<T> { void onNext(T t); }

代碼很簡單。再來看一下ProgressSubscriber如今的代碼

public class ProgressSubscriber<T> extends Subscriber<T> { private SubscriberOnNextListener mSubscriberOnNextListener; private Context context; public ProgressSubscriber(SubscriberOnNextListener mSubscriberOnNextListener, Context context) { this.mSubscriberOnNextListener = mSubscriberOnNextListener; this.context = context; } @Override public void onStart() { } @Override public void onCompleted() { Toast.makeText(context, "Get Top Movie Completed", Toast.LENGTH_SHORT).show(); } @Override public void onError(Throwable e) { Toast.makeText(context, "error:" + e.getMessage(), Toast.LENGTH_SHORT).show(); } @Override public void onNext(T t) { mSubscriberOnNextListener.onNext(t); } }

我知道傳Context很差,不過爲了演示而已,你們能夠本身封裝一下Toast。

MainActivity使用是這樣的:

先來定義一個SubscriberOnNextListener對象,能夠在onCreate裏面建立這個對象

private SubscriberOnNextListener getTopMovieOnNext; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); getTopMovieOnNext = new SubscriberOnNextListener<List<Subject>>() { @Override public void onNext(List<Subject> subjects) { resultTV.setText(subjects.toString()); } }; }

getMovie方法這麼寫:

private void getMovie(){ HttpMethods.getInstance().getTopMovie( new ProgressSubscriber(getTopMovieOnNext, MainActivity.this), 0, 10); }

這樣Activity或Fragment就只須要關注拿到結果以後的邏輯了,其餘的徹底不用操心。

如需查看項目代碼 --> 代碼地址:

https://github.com/tough1985/RxjavaRetrofitDemo

選擇Tag -> step6

5.2處理ProgressDialog

咱們但願當cancel掉ProgressDialog的時候,可以取消訂閱,也就取消了當前的Http請求。 因此咱們先來建立個接口來處理這件事情。

public interface ProgressCancelListener { void onCancelProgress(); }

而後咱們用ProgressSubscriber來實現這個接口,這樣ProgressSubscriber就有了一個onCancelProgress方法,在這裏面取消訂閱。

@Override public void onCancelProgress() { if (!this.isUnsubscribed()) { this.unsubscribe(); } }

而後我用了一個Handler來封裝了ProgressDialog。

public class ProgressDialogHandler extends Handler { public static final int SHOW_PROGRESS_DIALOG = 1; public static final int DISMISS_PROGRESS_DIALOG = 2; private ProgressDialog pd; private Context context; private boolean cancelable; private ProgressCancelListener mProgressCancelListener; public ProgressDialogHandler(Context context, ProgressCancelListener mProgressCancelListener, boolean cancelable) { super(); this.context = context; this.mProgressCancelListener = mProgressCancelListener; this.cancelable = cancelable; } private void initProgressDialog(){ if (pd == null) { pd = new ProgressDialog(context); pd.setCancelable(cancelable); if (cancelable) { pd.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialogInterface) { mProgressCancelListener.onCancelProgress(); } }); } if (!pd.isShowing()) { pd.show(); } } } private void dismissProgressDialog(){ if (pd != null) { pd.dismiss(); pd = null; } } @Override public void handleMessage(Message msg) { switch (msg.what) { case SHOW_PROGRESS_DIALOG: initProgressDialog(); break; case DISMISS_PROGRESS_DIALOG: dismissProgressDialog(); break; } } }

Handler接收兩個消息來控制顯示Dialog仍是關閉Dialog。 建立Handler的時候咱們須要傳入ProgressCancelListener的對象實例。

最後貼出ProgressSubscriber的完整代碼:

public class ProgressSubscriber<T> extends Subscriber<T> implements ProgressCancelListener{ private SubscriberOnNextListener mSubscriberOnNextListener; private ProgressDialogHandler mProgressDialogHandler; private Context context; public ProgressSubscriber(SubscriberOnNextListener mSubscriberOnNextListener, Context context) { this.mSubscriberOnNextListener = mSubscriberOnNextListener; this.context = context; mProgressDialogHandler = new ProgressDialogHandler(context, this, true); } private void showProgressDialog(){ if (mProgressDialogHandler != null) { mProgressDialogHandler.obtainMessage(ProgressDialogHandler.SHOW_PROGRESS_DIALOG).sendToTarget(); } } private void dismissProgressDialog(){ if (mProgressDialogHandler != null) { mProgressDialogHandler.obtainMessage(ProgressDialogHandler.DISMISS_PROGRESS_DIALOG).sendToTarget(); mProgressDialogHandler = null; } } @Override public void onStart() { showProgressDialog(); } @Override public void onCompleted() { dismissProgressDialog(); Toast.makeText(context, "Get Top Movie Completed", Toast.LENGTH_SHORT).show(); } @Override public void onError(Throwable e) { dismissProgressDialog(); Toast.makeText(context, "error:" + e.getMessage(), Toast.LENGTH_SHORT).show(); } @Override public void onNext(T t) { mSubscriberOnNextListener.onNext(t); } @Override public void onCancelProgress() { if (!this.isUnsubscribed()) { this.unsubscribe(); } } }

目前爲止,就封裝完畢了。以上是我在用Rxjava和Retrofit過程當中踩過的一些坑,最後整合出來的,因爲沒有在實際的項目中跑過,有問題的話但願可以提出來你們討論一下,拍磚也歡迎。

如今咱們再寫一個新的網絡請求,步驟是這樣的: 1. 在Service中定義一個新的方法。 2. 在HttpMethods封裝對應的請求(代碼基本能夠copy) 3. 建立一個SubscriberOnNextListener處理請求數據並刷新UI。

最後

若是你以爲寫更改線程的代碼以爲也很煩的話,能夠把訂閱這部分也封裝起來:

public void getTopMovie(Subscriber<List<Subject>> subscriber, int start, int count){ //原來的樣子 // movieService.getTopMovie(start, count) // .map(new HttpResultFunc<List<Subject>>()) // .subscribeOn(Schedulers.io()) // .unsubscribeOn(Schedulers.io()) // .observeOn(AndroidSchedulers.mainThread()) // .subscribe(subscriber); //修改以後的樣子 Observable observable = movieService.getTopMovie(start, count) .map(new HttpResultFunc<List<Subject>>()); toSubscribe(observable, subscriber); } //添加線程管理並訂閱 private void toSubscribe(Observable o, Subscriber s){ o.subscribeOn(Schedulers.io()) .unsubscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(s); }

讓你每次寫一個請求的時候,寫的代碼儘可能少,更多的精力放在業務邏輯自己。

最後的最後

若是你的httpResult格式自己沒有問題,可是data中的內容是這樣的:

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

這樣的狀況還能不能繼續使用這樣的框架呢? 個人解決方法是封裝一個類,把user和orderArray做爲類的屬性。 可是若是你的服務器一會data自己是一個完整的user數據,一會又是這樣: "data": {"user": {}, "orderArray": []} 那我以爲你有必要跟你的服務端好好聊聊了,請他吃頓飯和頓酒,大不了獻出菊花就是了。

可是若是服務已經上線了!!!

對不起,騷年......

老衲會在你墳前念300遍Thinking in java替你超度的~

但願你用Retrofit和Rxjava的新體位可以享受到新的高潮。

相關文章
相關標籤/搜索