添加請求頭 retrofit_RxJava 與 Retrofit 結合的最佳實踐

前言

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

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

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

如果你對Retrofit不熟悉就先看Retrofit官網。

當然也有很多RxJava與Retrofit的文章,但是我覺得很多大家都很糾結的功能都沒有被總結出來,所以才有了此篇文章。

歡迎大家拍磚。

接下來進入正文,我是從下面幾個角度去思考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中的線程問題。

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

36bae9b4c44d6a77b1a4457f39e2de68.png

activity_main.xml的代碼如下:

<?xml version="1.0" encoding="utf-8"?>

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()      {      }}

注意不要忘記加網絡權限

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 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 call = movieService.getTopMovie(0, 10);      call.enqueue(new Callback()       {              @Override                public void onResponse(Call call, Response response)                 {                  resultTV.setText(response.body().toString());                }                @Override                public void onFailure(Call 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 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()                         {                              @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 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()       {            @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 {      private int resultCode;      private String resultMessage;      private T data;}

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

Observable>

這樣一來HttpResult就相當于一個包裝類,將結果包裝了起來,但是在使用的時候要給出一個明確的類型。

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

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

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

Observable>>

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

https://github.com/tough1985/RxjavaRetrofitDemo

選擇Tag -> step4

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

既然我們有了相同的返回格式,那么我們可能就需要在獲得數據之后進行一個統一的預處理。

當接收到了一個Http請求結果之后,由于返回的結構統一為

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

我們想要對resultCoderesultMessage先做一個判斷,因為如果resultCode == 0代表success,那么resultCode != 0時data一般都是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  Subscriber真正需要的數據類型,也就是Data部分的數據類型*/private class HttpResultFunc implements Func1, T>{      @Override      public T call(HttpResult httpResult)       {            if (httpResult.getResultCode() != 0)             {              throw new ApiException(httpResult.getResultCode());            }            return httpResult.getData();      }}

然后我們的getTopMovie方法改為:

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

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

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

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

HttpResult//orHttpResult>

而在定義Subscriber的時候泛型是javaUser//orList

不然你會得到一個轉型錯誤。

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

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 implements Observable.OnSubscribe> {      private final Call originalCall;      CallOnSubscribe(Call originalCall)       {        this.originalCall = originalCall;      }@Override       public void call(final Subscriber super Response> subscriber)       {            // Since Call is a one-shot type, clone it for each new subscriber.            final Call 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 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(){                   @Overridepublic void onCompleted() {observer.onCompleted();}@Overridepublic void onError(Throwable e) {observer.onError(e);}@Overridepublic 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方法我們集中處理錯誤,同時也停止ProgressDialogonComplated方法里面停止ProgressDialog

其中我們需要解決兩個問題

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

我們先來解決問題1

5.1處理onNext

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

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

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

代碼很簡單。再來看一下ProgressSubscriber現在的代碼

public class ProgressSubscriber extends Subscriber {      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;@Overrideprotected void onCreate(Bundle savedInstanceState) {      super.onCreate(savedInstanceState);      setContentView(R.layout.activity_main);      ButterKnife.bind(this);      getTopMovieOnNext = new SubscriberOnNextListener>()       {            @Override            public void onNext(List 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方法,在這里面取消訂閱。

@Overridepublic 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()                              {@Overridepublic void onCancel(DialogInterface dialogInterface) {mProgressCancelListener.onCancelProgress();}});}if (!pd.isShowing()) {pd.show();}}}private void dismissProgressDialog(){if (pd != null) {pd.dismiss();pd = null;}}@Overridepublic 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 extends Subscriber 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> subscriber, int start, int count){  //原來的樣子  // movieService.getTopMovie(start, count)  // .map(new HttpResultFunc>())  // .subscribeOn(Schedulers.io())  // .unsubscribeOn(Schedulers.io())  // .observeOn(AndroidSchedulers.mainThread())  // .subscribe(subscriber);    //修改之后的樣子  Observable observable = movieService.getTopMovie(start, count)    .map(new HttpResultFunc>())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的新體位能夠享受到新的高潮。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/536981.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/536981.shtml
英文地址,請注明出處:http://en.pswp.cn/news/536981.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Mybatis源碼閱讀(二):動態節點解析2.1 —— SqlSource和SqlNode

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

k8s邊緣節點_邊緣計算,如何啃下集群管理這塊硬骨頭?

導讀邊緣計算平臺&#xff0c;旨在將邊緣端靠近數據源的計算單元納入到中心云&#xff0c;實現集中管理&#xff0c;將云服務部署其上&#xff0c;及時響應終端請求。然而&#xff0c;成千上萬的邊緣節點散布于各地&#xff0c;例如銀行網點、車載節點等&#xff0c;節點數量甚…

Mybatis源碼閱讀(二):動態節點解析2.2 —— SqlSourceBuilder與三種SqlSource

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

搞懂toString()與valueOf()的區別

一、toString&#xff08;&#xff09; 作用&#xff1a;toString&#xff08;&#xff09;方法返回一個表示改對象的字符串&#xff0c;如果是對象會返回&#xff0c;toString() 返回 “[object type]”,其中type是對象類型。 二、valueOf( ) 作用&#xff1a;valueOf房啊發返…

oracle入庫的速度能到多少_倒車入庫別練復雜了,其實就這兩點

教練總會讓學員反復練倒車入庫&#xff0c;但不少學員都會有這樣的疑惑&#xff1a;為什么每一次倒庫結果都不一樣&#xff0c;倒車入庫的練習重點是什么&#xff1f;倒車入庫是科二的重點及難點&#xff0c;但只要掌握以下兩個關鍵&#xff0c;順利通過真不難&#xff1a;01方…

Mybatis源碼閱讀(三):結果集映射3.1 —— ResultSetBuilder與簡單映射

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

kdj買賣指標公式源碼_通達信指標公式源碼MACD背離KDJ背離指標

N1:5;N2:10;N3:21;N4:60;牛熊:EMA(CLOSE,N4),COLORGREEN,LINETHICK3;DIFF:EMA(CLOSE,12) - EMA(CLOSE,26);DEA:EMA(DIFF,8);A1:BARSLAST(REF(CROSS(DIFF,DEA),1)); B1:REF(C,A11)>C AND REF(DIFF,A11)DRAWTEXT(IF(B1>0,1,0),L-0.1,MACD底背),COLORGREEN;RSV:(CLOSE-LLV(L…

Mybatis源碼閱讀(三):結果集映射3.2 —— 嵌套映射

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

18.requests

多多的的轉載于:https://www.cnblogs.com/yangyangchunchun/p/10368337.html

gridview獲取選中行數據_Word轉Excel,不想熬夜加班,那就掌握這個數據清洗方法...

私信回復關鍵詞【福利】~獲取豐富辦公資源&#xff0c;助你高效辦公早下班&#xff01;小伙伴們&#xff0c;大家好&#xff0c;我是專治各種疑難雜「數」的農夫~今天&#xff0c;我就為大家介紹一種高效的數據清洗方法&#xff0c;助你告別熬夜加班&#xff0c;擁抱美好的夜晚…

如何深入學習python_菜鳥如何學好python

python在我國發展得如火如荼&#xff0c;因其操作簡單&#xff0c;應用廣泛受到很多人的喜歡。下面小編就來說說菜鳥如何學好python&#xff0c;一起來看看吧!1. 了解編程的基礎知識種是變量、編程規范、基本語法等&#xff0c;這也是開始編寫Python代碼的先決條件。第二種是數…

HTML5中本地儲存概念是什么,什么優點 ,與cookie有什么區別?

html5中的Web Storage 包括了兩種存儲方式&#xff1a; sessionStorage 和 localStorage. seessionStorage 用于本地存儲一個會話&#xff08;session&#xff09;中的數據&#xff0c;這些數據只有在同一個會話中的頁面才能訪問并且當會話結束后數據也隨之銷毀。因此session…

Mybatis源碼閱讀(三):結果集映射3.3 —— 主鍵生成策略

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

list最大容量_Java 基礎(四)集合源碼解析 List

List 接口前面我們學習了Iterator、Collection&#xff0c;為集合的學習打下了基礎&#xff0c;現在我們來學習集合的第一大體系 List。List 是一個接口&#xff0c;定義了一組元素是有序的、可重復的集合。List 繼承自 Collection&#xff0c;較之 Collection&#xff0c;List…

Mybatis源碼閱讀(四):核心接口4.1——StatementHandler

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

Shell學習之結合正則表達式與通配符的使用(五)

Shell學習之結合正則表達式與通配符的使用 目錄 通配符 正則表達式與通配符通配符通配符的使用正則表達式 正則表達式正則表達式的使用通配符 正則表達式與通配符 正則表達式用來在文件中匹配符合條件的字符串&#xff0c;正則是包含匹配。grep、awk、sed等命令可以支持正則表達…

Mybatis源碼閱讀(四):核心接口4.2——Executor(上)

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

接收xml參數_SpringBoot實戰(二):接收xml請求

強烈推薦一個大神的人工智能的教程&#xff1a;http://www.captainbed.net/zhanghan【前言】最近在對接一個第三方系統&#xff0c;需要接收第三方系統的回調&#xff0c;而且格式為XML形式&#xff0c;之前自己一般接收的參數是Json形式&#xff0c;于是乎做個實驗驗證一下使用…

報錯 插入更新_window如何解決mysql數據量過大導致的報錯

window如何解決報錯“The total number of locks exceeds the lock table size”第一大步&#xff0c;查看mysql配置信息在CMD中輸入mysql -hlocalhost -uroot -p #如果設置了密碼直接接在p 后面 show variables like %storage_engine%以下為結果可以看到InnoDB是MySQL的默認引…

148. Sort List

Sort a linked list in O(n log n) time using constant space complexity. Example 1: Input: 4->2->1->3 Output: 1->2->3->4 Example 2: Input: -1->5->3->4->0 Output: -1->0->3->4->5難度&#xff1a;medium 題目&#xff1a;排…