【轉載】RxJava2+Retrofit2+OkHttp3的基礎、封裝和項目中的使用

前言:

近些年很火的Retrofit+RxJava+OkHttp網絡請求框架,功能強大,結構合理,使用簡單方便。後面還會給大家發自己整理過的Retrofit和RxJava、RxAndroid和RxBus。希望大家點一下關注,讓我這個懶癌患者有動力繼續寫下去!
本篇分三個部分:基礎篇、封裝篇和自己項目使用篇,項目是自己公司的APP提取的,文章偏長可以分三部分一點點看,當初看了很多優秀的文章然後自己在整理寫在印象筆記中。
感謝大佬們的學習參考文章:
扔物線:http://gank.io/post/560e15be2dca930e00da1083
依然范特西https://www.jianshu.com/p/5bc866b9cbb9
拉丁吳:https://juejin.im/post/580103f20e3dd90057fc3e6d
玉剛說https://juejin.im/post/5b17560e6fb9a01e2862246f
最新更新:7.11,更新內容是升級版本替換成RxJava2,修改了一些語法。
Github地址:https://github.com/bigeyechou/NetWorkFrame

簡單介紹Retrofit、OKHttp和RxJava之間的關係:

  • Retrofit:Retrofit是Square公司開發的一款針對Android 網絡請求的框架(底層默認是基於OkHttp 實現)。
  • OkHttp:也是Square公司的一款開源的網絡請求庫。
  • RxJava :"a library for composing asynchronous and event-based programs using observable sequences for the Java VM"(一個在 Java VM 上使用可觀測的序列來組成異步的、基於事件的程序的庫)。RxJava使異步操作變得非常簡單。

各自職責:Retrofit 負責 請求的數據 和 請求的結果,使用 接口的方式 呈現,OkHttp 負責請求的過程,RxJava 負責異步,各種線程之間的切換。

基礎篇:

一、Retrofit寫一個網絡請求:

1.引入Retrofit的包,在build.gradle文件中添加如下配置:

compile 'com.squareup.retrofit2:retrofit:2.3.0'//導入retrofit
compile 'com.google.code.gson:gson:2.6.2'//Gson 庫
//下面兩個是RxJava 和 RxAndroid
compile 'io.reactivex.rxjava2:rxandroid:2.0.2'
compile 'io.reactivex.rxjava2:rxjava:2.x.y'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'//轉換器,請求結果轉換成Model
compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'//配合Rxjava 使用

2.創建一個Retrofit 實例,並且完成相關的配置:
配置了接口的 BASE_URL 和一個 converter , GsonConverterFactory 是默認提供的 Gson轉換器。

public static final String BASE_URL = "https://api.douban.com/v2/movie/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();

3.創建一個接口:
定義了一個方法 getTop250 ,使用 get請求方式,加上@GET 標籤,標籤後面是這個接口的 尾址top250,完整的地址應該是 baseUrl+尾址 ,參數 使用@Query標籤,如果參數多的話可以用@QueryMap標 籤,接收一個Map。
使用 POST 請求方式時,只需要更改方法定義的標籤,用 @POST 標籤,參數標籤用 @Field 或者 @Body 或者 FieldMap

public interface MovieService {
//獲取豆瓣Top250 榜單
@GET("top250")
Call<MovieSubject> getTop250 (@Query("start") int start , @Query("count") int count);

@FormUrlEncoded
@POST(“top250”)
Call<MovieSubject> getTop250 (@Field(“start”) int start , @Field(“count”) int count);
}

使用 POST 方式時需要注意兩點:

  • 必須加上 @FormUrlEncoded標籤,否則會拋異常。
  • 必須要有參數,否則會拋異常, 源碼拋異常的地方如下:
if (isFormEncoded && !gotField) {
      throw methodError("Form-encoded method must contain at least one @Field.");
}

4.用 Retrofit 創建 接口實例 MoiveService 並且調用接口中的方法進行網絡請求:
異步方式請求:

//獲取接口實例
MovieService movieService = retrofit.create(MovieService.class);
//調用方法得到一個Call
Call<MovieSubject> call = movieService.getTop250(0,20);
 //進行網絡請求
call.enqueue(new Callback<MovieSubject>() {
       @Override
       public void onResponse(Call<MovieSubject> call, Response<MovieSubject> response) {
            mMovieAdapter.setMovies(response.body().subjects);     
            mMovieAdapter.notifyDataSetChanged();
       }
      @Override
      public void onFailure(Call<MovieSubject> call, Throwable t) {
         t.printStackTrace();
      }
});

同步方式請求: 返回一個Response

Response<MovieSubject> response = call.execute();
二,配合RxJava 使用:
  1. 更改定義的接口,返回值不再是一個 Call ,而是返回的一個 Observble:
public interface MovieService {
//獲取豆瓣Top250 榜單
@GET("top250")
Observable<MovieSubject> getTop250(@Query("start") int start, @Query("count")int count);
}

2.創建 Retrofit 的時候添加如下代碼:

addCallAdapterFactory(RxJava2CallAdapterFactory.create())

3.添加轉換器Converter(將 json 轉爲 JavaBean):

addConverterFactory(GsonConverterFactory.create())

舉實際項目中使用的例子:

retrofit = new Retrofit.Builder()
        .client(okHttpBuilder.build())
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .baseUrl(BASE_URL)
        .build();

4.Activity 或者 Fragment 中傳入 DisposableObserver 建立訂閱關係:

Subscription subscription = movieService.getTop250(0,20)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new DisposableObserver<MovieSubject>() {
@Override
public void onComplete() {

}
@Override
public void onError(Throwable e) {

}
@Override
public void onNext(MovieSubject movieSubject) {
mMovieAdapter.setMovies(movieSubject.subjects);
mMovieAdapter.notifyDataSetChanged();
}
});

5.加入RxJava的好處:

  • 加入 RxJava 後的網絡請求,返回不再是一個 Call ,而是一個 Observable。
  • 在Activity / Fragment 中傳入一個Subscriber 建立訂閱關係,就可以在 onNext 中處理結果了。
  • RxJava 的好處是幫我處理 線程之間的切換,我們可以在指定 訂閱的在哪個線程,觀察在哪個線程。
  • 可以 通過操作符 進行數據變換。
  • 整個過程都是鏈式的,簡化邏輯。其中FlatMap 操作符 還可以解除多層嵌套的問題。

RxJava 很強大,能幫我處理很多複雜的場景,如果熟練使用的話,那麼能提升我們的開發效率。

三,加入 OkHttp 配置:

通過OkHttpClient 可以配置很多東西,比如 鏈接超時時間,緩存,攔截器 等等。代碼如下:

OkHttpClient.Builder builder = new OkHttpClient.Builder();
     builder.connectTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS);//連接 超時時間
     builder.writeTimeout(DEFAULT_TIME_OUT,TimeUnit.SECONDS);//寫操作 超時時間
     builder.readTimeout(DEFAULT_TIME_OUT,TimeUnit.SECONDS);//讀操作 超時時間
     builder.retryOnConnectionFailure(true);//錯誤重連

// 添加公共參數攔截器
BasicParamsInterceptor basicParamsInterceptor = new BasicParamsInterceptor.Builder()
.addHeaderParam(“userName”,"")//添加公共參數
.addHeaderParam(“device”,"")
.build();

builder.addInterceptor(basicParamsInterceptor);

// 創建Retrofit
mRetrofit = new Retrofit.Builder()
.client(builder.build())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(ApiConfig.BASE_URL)
.build();

列舉項目中用到的如下:

//項目中設置頭信息
Interceptor headerInterceptor = new Interceptor() {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request originalRequest = chain.request();
        Request.Builder requestBuilder = originalRequest.newBuilder()
                .addHeader("Accept-Encoding", "gzip")
                .addHeader("Accept", "application/json")
                .addHeader("Content-Type", "application/json; charset=utf-8")
                .method(originalRequest.method(), originalRequest.body());
        requestBuilder.addHeader("Authorization", "Bearer " + BaseConstant.TOKEN);//添加請求頭信息,服務器進行token有效性驗證
        Request request = requestBuilder.build();
        return chain.proceed(request);
    }
};
okHttpBuilder.addInterceptor(headerInterceptor);

//項目中創建Retrofit
retrofit = new Retrofit.Builder()
.client(okHttpBuilder.build())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl(BASE_URL)
.build();
httpService = retrofit.create(HttpService.class);

封裝篇

一,創建一個 統一生成接口實例的管理類 RetrofitServiceManager

創建了一個 RetrofitServiceManager 類,該類採用 單例模式,在 私有的 構造方法中,生成了 Retrofit 實例,並配置了OkHttpClient 和一些 公共配置。
提供了一個create()方法,生成 接口實例,接收 Class泛型。
代碼如下:

public class RetrofitServiceManager {
  private static final int DEFAULT_TIME_OUT = 5;//超時時間 5s   
  private static final int DEFAULT_READ_TIME_OUT = 10;   
  private Retrofit mRetrofit;   

private RetrofitServiceManager(){
// 創建 OKHttpClient
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS);//連接超時時間
builder.writeTimeout(DEFAULT_READ_TIME_OUT,TimeUnit.SECONDS);//寫操作 超時時間
builder.readTimeout(DEFAULT_READ_TIME_OUT,TimeUnit.SECONDS);//讀操作超時時間

 <span class="hljs-comment">// 添加公共參數攔截器       </span>
 HttpCommonInterceptor commonInterceptor = <span class="hljs-keyword">new</span> HttpCommonInterceptor.Builder()
           .addHeaderParams(<span class="hljs-string">"paltform"</span>,<span class="hljs-string">"android"</span>)
           .addHeaderParams(<span class="hljs-string">"userToken"</span>,<span class="hljs-string">"1234343434dfdfd3434"</span>)
           .addHeaderParams(<span class="hljs-string">"userId"</span>,<span class="hljs-string">"123445"</span>)     
           .build();       
 builder.addInterceptor(commonInterceptor);  

 <span class="hljs-comment">// 創建Retrofit       </span>
 mRetrofit = <span class="hljs-keyword">new</span> Retrofit.Builder()
           .client(builder.build()) 
           .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
           .addConverterFactory(GsonConverterFactory.create())
           .baseUrl(ApiConfig.BASE_URL)   
           .build();   

}

private static class SingletonHolder{
private static final RetrofitServiceManager INSTANCE = new RetrofitServiceManager();
}

<span class="hljs-comment">/**
 * 獲取RetrofitServiceManager
 * <span class="hljs-doctag">@return</span>
 */</span>   

public static RetrofitServiceManager getInstance(){
return SingletonHolder.INSTANCE;
}

/**
* 獲取對應的Service
* @param service Service 的 class
* @param <T>
* @return
*/

public <T> T create(Class<T> service){
return mRetrofit.create(service);
}

}

接口實例Service都可以用這個來生成,代碼如下:

mMovieService = RetrofitServiceManager.getInstance().create(MovieService.class);
二,創建接口,通過第一步獲取實例

有了可以獲取接口實例的方法,然後創建一個接口,代碼如下:

public interface MovieService{ 
  //獲取豆瓣Top250 榜單 
  @GET("top250")   
  Observable<MovieSubject> getTop250(@Query("start") int start, @Query("count") int count); 

@FormUrlEncoded
@POST("/x3/weather")
Call<String> getWeather(@Field(“cityId”) String cityId, @Field(“key”) String key);
}

三,創建一個業務Loader ,如XXXLoder,獲取Observable並處理相關業務

創建 Loader 的原因:每一個Api 都寫一個接口很麻煩,因此就把 請求邏輯 封裝在 一個業務Loader 裏面,一個 Loader 裏面可以處理多個Api 接口。代碼如下:

public class MovieLoader extends ObjectLoader {
  private MovieService mMovieService;
  public MovieLoader(){ 
      mMovieService = RetrofitServiceManager.getInstance().create(MovieService.class);
    } 
  /**
    * 獲取電影列表
    * @param start 
    * @param count   
    * @return   
    */ 
  public Observable<List<Movie>> getMovie(int start, int count){ 
      return observe(mMovieService.getTop250(start , count)).map(new Func1<MovieSubject, List<Movie>>() { 
        @Override
        public List<Movie> call(MovieSubject movieSubject) { 
        return movieSubject.subjects;   
      } 
    });
  } 

public Observable<String> getWeatherList(String cityId,String key){
return observe(mMovieService.getWeather(cityId , key)).map(new Func1<String , String>() {
@Override
public String call(String s) {
//可以處理對應的邏輯後在返回
return s;
}
});
}

public interface MovieService{
//獲取豆瓣Top250 榜單
@GET(“top250”)
Observable<MovieSubject> getTop250(@Query(“start”) int start, @Query(“count”)int count);

<span class="hljs-meta">@FormUrlEncoded</span> 
<span class="hljs-meta">@POST</span>(<span class="hljs-string">"/x3/weather"</span>)   
<span class="hljs-function">Call&lt;String&gt; <span class="hljs-title">getWeather</span><span class="hljs-params">(@Field(<span class="hljs-string">"cityId"</span>)</span> String cityId, @<span class="hljs-title">Field</span><span class="hljs-params">(<span class="hljs-string">"key"</span>)</span> String key)</span>; 

}
}
創建一個MovieLoader,構造方法中生成了mHttpService,而 Service 中可以定義和業務相關的多個api,比如:例子中的HttpService中,
可以定義和電影相關的多個api,獲取電影列表、獲取電影詳情、搜索電影等api,就不用定義多個接口了。

MovieLoader 是從 ObjectLoader 中繼承下來的,ObjectLoader 提取了一些公共的操作。代碼如下:
/**

  • 將一些重複的操作提出來,放到父類以免Loader 裏每個接口都有重複代碼
    /
    public class ObjectLoader {
    /
    *
  • @param observable
  • @param <T>
  • @return
    */
    protected <T> Observable<T> observe(Observable<T> observable){
    return observable
    .subscribeOn(Schedulers.io())
    .unsubscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread());
    }
    }
四,Activity/Fragment 中的調用

創建Loader實例:

mMovieLoader = new MovieLoader();

通過Loader 調用方法獲取結果,代碼如下:

/**
 * 獲取電影列表
 */
private void getMovieList(){
  mMovieLoader.getMovie(0,10).subscribe(new Action1<List<Movie>>() { 
    @Override 
    public void call(List<Movie> movies) { 
        mMovieAdapter.setMovies(movies);       
        mMovieAdapter.notifyDataSetChanged();     
        }
  }, new Action1<Throwable>() {   
    @Override     
    public void call(Throwable throwable) {   
        Log.e("TAG","error message:"+throwable.getMessage());   
      } 
  });
}
五,統一處理結果和錯誤

1.統一處理請求結果:
現實項目中,所有接口的返回結果都是同一格式,如:

{
"status": 200,
"message": "成功",
"data": {}
}

在請求api 接口的時候,只關心想要的數據,也就上面的 data{ },其他的東西不太關心,請求失敗 的時候可以根據 status 判斷進行 錯誤處理。
包裝返回結果:首先需要根據服務端定義的 JSON 結構創建一個 BaseResponse 類,代碼如下:

/**
 * 網絡請求結果 基類 
 */
public class BaseResponse<T> { 
  public int status; 
  public String message;   
  public T data;   
  public boolean isSuccess(){ 
    return status == 200; 
  }
}

有了統一的格式數據後,我們需要 剝離出data{ }返回給 上層調用者,創建一個 PayLoad 類,代碼如下:

/**
 * 剝離 最終數據
 */
public class PayLoad<T> implements Func1<BaseResponse<T>{   
@Override
  public T call(BaseResponse<T> tBaseResponse) {//獲取數據失敗時,包裝一個Fault 拋給上層處理錯誤
        if(!tBaseResponse.isSuccess()){
          throw new Fault(tBaseResponse.status,tBaseResponse.message); 
      }   
    return tBaseResponse.data; 
  }
}

PayLoad 繼承自 Func1,接收一個BaseResponse<T> , 就是接口返回的 JSON 數據結構,返回的是 T,就是data{ },判斷是否請求成功,請求成功 返回Data,請求失敗 包裝成一個 Fault 返回給上層統一處理錯誤。
在Loader類裏面獲取結果後,通過map 操作符剝離數據。代碼如下:

public Observable<List<Movie>> getMovie(int start, int count){
  return observe(mMovieService.getTop250(start,count))       
    .map(new PayLoad<BaseResponse<List<Movie>>());
}

2.統一處理錯誤:
在PayLoad 類裏面,請求失敗時,拋出了一個Fault 異常給上層,我在Activity/Fragment 中拿到這個異常,然後判斷錯誤碼,進行異常處理。在onError () 中添加。
對應 錯誤碼 處理 相應的錯誤,代碼如下:

public void call(Throwable throwable) { 
  Log.e("TAG","error message:"+throwable.getMessage()); 
  if(throwable instanceof Fault){   
  Fault fault = (Fault) throwable;   
    if(fault.getErrorCode() == 404){   
      //錯誤處理
      }else if(fault.getErrorCode() == 500){ 
      //錯誤處理 
      }else if(fault.getErrorCode() == 501){     
      //錯誤處理 
    } 
  }
}
六,添加公共參數

實際項目中,每個接口都有一些基本的相同的參數,我們稱之爲公共參數,比如:userId、userToken、userName、deviceId等等,我們不必每個接口都去寫,可以寫一個攔截器,在攔截器裏面攔截請求,爲每個請求都添加相同的公共參數。
攔截器代碼如下:

/*
 * 攔截器
 *
 * 向請求頭裏添加公共參數
 */
public class HttpCommonInterceptor implements Interceptor {   
private Map<String,String> mHeaderParamsMap = new HashMap<>(); 
  public HttpCommonInterceptor() { 
  }   
    @Override
    public Response intercept(Chain chain) throws IOException {   
    Log.d("HttpCommonInterceptor","add common params");   
        Request oldRequest = chain.request();   
        // 添加新的參數,添加到url 中 
        /*HttpUrl.Builder authorizedUrlBuilder = oldRequest.url().newBuilder()     
        .scheme(oldRequest.url().scheme()) 
        .host(oldRequest.url().host());*/
  <span class="hljs-comment">// 新的請求 </span>
  Request.Builder requestBuilder =  oldRequest.newBuilder();
  requestBuilder.method(oldRequest.method(), oldRequest.body());

  <span class="hljs-comment">//添加公共參數,添加到header中       </span>
 <span class="hljs-keyword">if</span>(mHeaderParamsMap.size() &gt; <span class="hljs-number">0</span>){     
      <span class="hljs-keyword">for</span>(Map.Entry&lt;String,String&gt; params:mHeaderParamsMap.entrySet()){ 
          requestBuilder.header(params.getKey(),params.getValue());     
    }   

}
Request newRequest = requestBuilder.build();
return chain.proceed(newRequest);
}

public static class Builder{
HttpCommonInterceptor mHttpCommonInterceptor;
public Builder(){
mHttpCommonInterceptor = new HttpCommonInterceptor();
}

public Builder addHeaderParams(String key, String value){
mHttpCommonInterceptor.mHeaderParamsMap.put(key,value);
return this;
}

public Builder addHeaderParams(String key, int value){
return addHeaderParams(key, String.valueOf(value));
}

public Builder addHeaderParams(String key, float value){
return addHeaderParams(key, String.valueOf(value));
}

public Builder addHeaderParams(String key, long value){
return addHeaderParams(key, String.valueOf(value));
}

public Builder addHeaderParams(String key, double value){
return addHeaderParams(key, String.valueOf(value));
}

public HttpCommonInterceptor build(){
return mHttpCommonInterceptor;
}

}
}

以上就是添加公共參數的攔截器,在 RetrofitServiceManager 類裏面加入OkHttpClient 配置就好了。
代碼如下:

// 添加公共參數攔截器
HttpCommonInterceptor commonInterceptor = new HttpCommonInterceptor.Builder()   
      .addHeaderParams("paltform","android") 
      .addHeaderParams("userToken","1234343434dfdfd3434")
      .addHeaderParams("userId","123445")     
      .build();
builder.addInterceptor(commonInterceptor);

項目使用篇 ----->插入廣告!本項目來源於金融研習社App,金融理財類的在線教育

項目是基於RxJava1
1.引入依賴:

    compile 'com.google.code.gson:gson:2.6.2'//導入Gson 庫
    //導入RxJava 和 RxAndroid
    compile 'io.reactivex.rxjava2:rxandroid:2.0.2'
    compile 'io.reactivex.rxjava2:rxjava:2.x.y'
    compile 'com.squareup.retrofit2:retrofit:2.3.0'//導入retrofit
    compile 'com.squareup.retrofit2:converter-gson:2.3.0'//轉換器,請求結果轉換成Model
    compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'//配合Rxjava 使用
    compile 'com.squareup.okhttp3:logging-interceptor:3.8.1'//添加HttpLoggingInterceptor進行調試

2.創建一個HttpService接口:

public interface HttpService {
/**
 * 獲取用戶詳細資料
 */
@POST("api/XXX/GetUserAllDetails")
Observable<ResponseBody> getUserAllDetails(@Body GetUserAllDetailsRequestBean bean);

/**

  • @param apkUrl 下載地址
    */
    @GET()
    @Streaming
    Call<ResponseBody> downloadNewApk(@Url String apkUrl);

/**

  • 獲取推廣大使分享圖片
    */
    @GET(“api/XXX/InvitedImage”)
    Observable<ResponseBody> getInvitedImage(@QueryMap Map<String, Object> map);

}

3.創建http請求類,並在裏面初始化並配置Retrofit和OkHttp:

public class HttpMethods {
    public String TAG = "HttpMethods";
    public static final String CACHE_NAME = "xxx";
    public static String BASE_URL = URLConstant.BASE_URL;
    private static final int DEFAULT_CONNECT_TIMEOUT = 30;
    private static final int DEFAULT_WRITE_TIMEOUT = 30;
    private static final int DEFAULT_READ_TIMEOUT = 30;
    private Retrofit retrofit;
    private HttpService httpService;
    /**
     * 請求失敗重連次數
     */
    private int RETRY_COUNT = 0;
    private OkHttpClient.Builder okHttpBuilder;
<span class="hljs-comment">//構造方法私有</span>
<span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">HttpMethods</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-comment">//手動創建一個OkHttpClient並設置超時時間</span>
    okHttpBuilder = <span class="hljs-keyword">new</span> OkHttpClient.Builder();

    <span class="hljs-comment">/**
     * 設置緩存
     */</span>
    File cacheFile = <span class="hljs-keyword">new</span> File(ApplicationContext.context.getExternalCacheDir(), CACHE_NAME);
    Cache cache = <span class="hljs-keyword">new</span> Cache(cacheFile, <span class="hljs-number">1024</span> * <span class="hljs-number">1024</span> * <span class="hljs-number">50</span>);
    Interceptor cacheInterceptor = <span class="hljs-keyword">new</span> Interceptor() {
        <span class="hljs-meta">@Override</span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> Response <span class="hljs-title">intercept</span><span class="hljs-params">(Chain chain)</span> <span class="hljs-keyword">throws</span> IOException </span>{
            Request request = chain.request();
            <span class="hljs-keyword">if</span> (!NetUtil.isNetworkConnected()) {
                request = request.newBuilder()
                        .cacheControl(CacheControl.FORCE_CACHE)
                        .build();
            }
            Response response = chain.proceed(request);
            <span class="hljs-keyword">if</span> (!NetUtil.isNetworkConnected()) {
                <span class="hljs-keyword">int</span> maxAge = <span class="hljs-number">0</span>;
                <span class="hljs-comment">// 有網絡時 設置緩存超時時間0個小時</span>
                response.newBuilder()
                        .header(<span class="hljs-string">"Cache-Control"</span>, <span class="hljs-string">"public, max-age="</span> + maxAge)
                        .removeHeader(CACHE_NAME)<span class="hljs-comment">// 清除頭信息,因爲服務器如果不支持,會返回一些干擾信息,不清除下面無法生效</span>
                        .build();
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-comment">// 無網絡時,設置超時爲4周</span>
                <span class="hljs-keyword">int</span> maxStale = <span class="hljs-number">60</span> * <span class="hljs-number">60</span> * <span class="hljs-number">24</span> * <span class="hljs-number">28</span>;
                response.newBuilder()
                        .header(<span class="hljs-string">"Cache-Control"</span>, <span class="hljs-string">"public, only-if-cached, max-stale="</span> + maxStale)
                        .removeHeader(CACHE_NAME)
                        .build();
            }
            <span class="hljs-keyword">return</span> response;
        }
    };
    okHttpBuilder.cache(cache).addInterceptor(cacheInterceptor);


    <span class="hljs-comment">/**
     * 設置頭信息
     */</span>
    Interceptor headerInterceptor = <span class="hljs-keyword">new</span> Interceptor() {
        <span class="hljs-meta">@Override</span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> Response <span class="hljs-title">intercept</span><span class="hljs-params">(Chain chain)</span> <span class="hljs-keyword">throws</span> IOException </span>{
            Request originalRequest = chain.request();
            Request.Builder requestBuilder = originalRequest.newBuilder()
                    .addHeader(<span class="hljs-string">"Accept-Encoding"</span>, <span class="hljs-string">"gzip"</span>)
                    .addHeader(<span class="hljs-string">"Accept"</span>, <span class="hljs-string">"application/json"</span>)
                    .addHeader(<span class="hljs-string">"Content-Type"</span>, <span class="hljs-string">"application/json; charset=utf-8"</span>)
                    .method(originalRequest.method(), originalRequest.body());
            requestBuilder.addHeader(<span class="hljs-string">"Authorization"</span>, <span class="hljs-string">"Bearer "</span> + BaseConstant.TOKEN);<span class="hljs-comment">//添加請求頭信息,服務器進行token有效性驗證</span>
            Request request = requestBuilder.build();
            <span class="hljs-keyword">return</span> chain.proceed(request);
        }
    };
    okHttpBuilder.addInterceptor(headerInterceptor);

// if (BuildConfig.DEBUG) {
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
Logger.d(message);
}

    });
    loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
    <span class="hljs-comment">//設置 Debug Log 模式</span>
    okHttpBuilder.addInterceptor(loggingInterceptor);

// }
/**
* 設置超時和重新連接
*/

okHttpBuilder.connectTimeout(DEFAULT_CONNECT_TIMEOUT, TimeUnit.SECONDS);
okHttpBuilder.readTimeout(DEFAULT_WRITE_TIMEOUT, TimeUnit.SECONDS);
okHttpBuilder.writeTimeout(DEFAULT_READ_TIMEOUT, TimeUnit.SECONDS);
//錯誤重連
okHttpBuilder.retryOnConnectionFailure(true);

    retrofit = <span class="hljs-keyword">new</span> Retrofit.Builder()
            .client(okHttpBuilder.build())
            .addConverterFactory(GsonConverterFactory.create())<span class="hljs-comment">//json轉換成JavaBean</span>
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .baseUrl(BASE_URL)
            .build();
    httpService = retrofit.create(HttpService.class);
}

<span class="hljs-comment">//在訪問HttpMethods時創建單例</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SingletonHolder</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> HttpMethods INSTANCE = <span class="hljs-keyword">new</span> HttpMethods();

}

<span class="hljs-comment">//獲取單例</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> HttpMethods <span class="hljs-title">getInstance</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">return</span> SingletonHolder.INSTANCE;
}

<span class="hljs-comment">/**
 * 獲取retrofit
 */</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> Retrofit <span class="hljs-title">getRetrofit</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">return</span> retrofit;
}

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">changeBaseUrl</span><span class="hljs-params">(String baseUrl)</span> </span>{
    retrofit = <span class="hljs-keyword">new</span> Retrofit.Builder()
            .client(okHttpBuilder.build())
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .baseUrl(baseUrl)
            .build();
    httpService = retrofit.create(HttpService.class);
}

<span class="hljs-comment">/**
 * 獲取httpService
 */</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> HttpService <span class="hljs-title">getHttpService</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">return</span> httpService;
}

 <span class="hljs-comment">/**
 * 設置訂閱 和 所在的線程環境
 */</span>
<span class="hljs-keyword">public</span> &lt;T&gt; <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">toSubscribe</span><span class="hljs-params">(Observable&lt;T&gt; o, DisposableObserver&lt;T&gt; s)</span> </span>{

    o.subscribeOn(Schedulers.io())
            .unsubscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .retry(RETRY_COUNT)<span class="hljs-comment">//請求失敗重連次數</span>
            .subscribe(s);

}

}

4.設置回調:
調用者自己對請求數據進行處理 成功時 通過result是否等於1分別回調onSuccees和onFault,默認處理了401錯誤轉登錄。

public class OnSuccessAndFaultSub extends DisposableObserver<ResponseBody> implements ProgressCancelListener {
    /**
     * 是否需要顯示默認Loading
     */
    private boolean showProgress = true;
    private OnSuccessAndFaultListener mOnSuccessAndFaultListener;
<span class="hljs-keyword">private</span> Context context;
<span class="hljs-keyword">private</span> WaitProgressDialog progressDialog;

<span class="hljs-comment">/**
 * <span class="hljs-doctag">@param</span> mOnSuccessAndFaultListener 成功回調監聽
 */</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">OnSuccessAndFaultSub</span><span class="hljs-params">(OnSuccessAndFaultListener mOnSuccessAndFaultListener)</span> </span>{
    <span class="hljs-keyword">this</span>.mOnSuccessAndFaultListener = mOnSuccessAndFaultListener;
}

<span class="hljs-comment">/**
 * <span class="hljs-doctag">@param</span> mOnSuccessAndFaultListener 成功回調監聽
  * <span class="hljs-doctag">@param</span> context                    上下文
  */</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">OnSuccessAndFaultSub</span><span class="hljs-params">(OnSuccessAndFaultListener mOnSuccessAndFaultListener, Context context)</span> </span>{
    <span class="hljs-keyword">this</span>.mOnSuccessAndFaultListener = mOnSuccessAndFaultListener;
    <span class="hljs-keyword">this</span>.context = context;
    progressDialog = <span class="hljs-keyword">new</span> WaitProgressDialog(context, <span class="hljs-keyword">this</span>);
}

<span class="hljs-comment">/**
 * <span class="hljs-doctag">@param</span> mOnSuccessAndFaultListener 成功回調監聽
  * <span class="hljs-doctag">@param</span> context                    上下文
  * <span class="hljs-doctag">@param</span> showProgress               是否需要顯示默認Loading
 */</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">OnSuccessAndFaultSub</span><span class="hljs-params">(OnSuccessAndFaultListener mOnSuccessAndFaultListener, Context context, <span class="hljs-keyword">boolean</span> showProgress)</span> </span>{
    <span class="hljs-keyword">this</span>.mOnSuccessAndFaultListener = mOnSuccessAndFaultListener;
    <span class="hljs-keyword">this</span>.context = context;
    progressDialog = <span class="hljs-keyword">new</span> WaitProgressDialog(context, <span class="hljs-keyword">this</span>);
    <span class="hljs-keyword">this</span>.showProgress = showProgress;
}

<span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">showProgressDialog</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">if</span> (showProgress &amp;&amp; <span class="hljs-keyword">null</span> != progressDialog) {
        progressDialog.show();
    }
}

<span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">dismissProgressDialog</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">if</span> (showProgress &amp;&amp; <span class="hljs-keyword">null</span> != progressDialog) {
        progressDialog.dismiss();
    }
}

<span class="hljs-comment">/**
 * 訂閱開始時調用
  * 顯示ProgressDialog
 */</span>
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onStart</span><span class="hljs-params">()</span> </span>{
    showProgressDialog();
}

<span class="hljs-comment">/**
 * 完成,隱藏ProgressDialog
 */</span>
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onComplete</span><span class="hljs-params">()</span> </span>{
    dismissProgressDialog();
    progressDialog = <span class="hljs-keyword">null</span>;
}

<span class="hljs-comment">/**
 * 對錯誤進行統一處理
  * 隱藏ProgressDialog
 */</span>
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onError</span><span class="hljs-params">(Throwable e)</span> </span>{
    <span class="hljs-keyword">try</span> {

        <span class="hljs-keyword">if</span> (e <span class="hljs-keyword">instanceof</span> SocketTimeoutException) {<span class="hljs-comment">//請求超時</span>
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (e <span class="hljs-keyword">instanceof</span> ConnectException) {<span class="hljs-comment">//網絡連接超時</span>
            mOnSuccessAndFaultListener.onFault(<span class="hljs-string">"網絡連接超時"</span>);
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (e <span class="hljs-keyword">instanceof</span> SSLHandshakeException) {<span class="hljs-comment">//安全證書異常</span>
            mOnSuccessAndFaultListener.onFault(<span class="hljs-string">"安全證書異常"</span>);
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (e <span class="hljs-keyword">instanceof</span> HttpException) {<span class="hljs-comment">//請求的地址不存在</span>
            <span class="hljs-keyword">int</span> code = ((HttpException) e).code();
            <span class="hljs-keyword">if</span> (code == <span class="hljs-number">504</span>) {
                mOnSuccessAndFaultListener.onFault(<span class="hljs-string">"網絡異常,請檢查您的網絡狀態"</span>);
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (code == <span class="hljs-number">404</span>) {
                mOnSuccessAndFaultListener.onFault(<span class="hljs-string">"請求的地址不存在"</span>);
            } <span class="hljs-keyword">else</span> {
                mOnSuccessAndFaultListener.onFault(<span class="hljs-string">"請求失敗"</span>);
            }
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (e <span class="hljs-keyword">instanceof</span> UnknownHostException) {<span class="hljs-comment">//域名解析失敗</span>
            mOnSuccessAndFaultListener.onFault(<span class="hljs-string">"域名解析失敗"</span>);
        } <span class="hljs-keyword">else</span> {
            mOnSuccessAndFaultListener.onFault(<span class="hljs-string">"error:"</span> + e.getMessage());
        }
    } <span class="hljs-keyword">catch</span> (Exception e2) {
        e2.printStackTrace();
    } <span class="hljs-keyword">finally</span> {
        Log.e(<span class="hljs-string">"OnSuccessAndFaultSub"</span>, <span class="hljs-string">"error:"</span> + e.getMessage());
        dismissProgressDialog();
        progressDialog = <span class="hljs-keyword">null</span>;

    }

}

<span class="hljs-comment">/**
 * 當result等於1回調給調用者,否則自動顯示錯誤信息,若錯誤信息爲401跳轉登錄頁面。
 * ResponseBody  body = response.body();//獲取響應體
 * InputStream inputStream = body.byteStream();//獲取輸入流
 * byte[] bytes = body.bytes();//獲取字節數組
 * String str = body.string();//獲取字符串數據
 */</span>
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onNext</span><span class="hljs-params">(ResponseBody body)</span> </span>{
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">final</span> String result = CompressUtils.decompress(body.byteStream());
        Log.e(<span class="hljs-string">"body"</span>, result);
        JSONObject jsonObject = <span class="hljs-keyword">new</span> JSONObject(result);
        <span class="hljs-keyword">int</span> resultCode = jsonObject.getInt(<span class="hljs-string">"ErrorCode"</span>);
        <span class="hljs-keyword">if</span> (resultCode == <span class="hljs-number">1</span>) {
            mOnSuccessAndFaultListener.onSuccess(result);
        } <span class="hljs-keyword">else</span> {
            String errorMsg = jsonObject.getString(<span class="hljs-string">"ErrorMessage"</span>);
            mOnSuccessAndFaultListener.onFault(errorMsg);
            Log.e(<span class="hljs-string">"OnSuccessAndFaultSub"</span>, <span class="hljs-string">"errorMsg: "</span> + errorMsg);
        }
    } <span class="hljs-keyword">catch</span> (Exception e) {
        e.printStackTrace();
    }
}

<span class="hljs-comment">/**
 * 取消ProgressDialog的時候,取消對observable的訂閱,同時也取消了http請求
  */</span>
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCancelProgress</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">if</span> (!<span class="hljs-keyword">this</span>.isDisposed()) {
        <span class="hljs-keyword">this</span>.dispose();
    }
}

}

 *請求服務loading關閉監聽 
 */
public interface ProgressCancelListener {
    void onCancelProgress();
}

5.請求的用法:
建議分類成不同的api,以便快速查找
api裏面進行觀察者和被觀察者的訂閱

private void getUserData() {
    OnSuccessAndFaultListener l = new OnSuccessAndFaultListener() {
        @Override
        public void onSuccess(String result) {//成功回調
            YXSPreference.setUserData(result);
            userDataBean = GsonUtils.fromJson(result, UserDetailResponseBean.class);
            fullData();
        }
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onFault</span><span class="hljs-params">(String errorMsg)</span> </span>{<span class="hljs-comment">//失敗的回調</span>
        SnackBarManager.showShortMsg(getActivity(), errorMsg);
    }
};

UserApi.getUserAllDetails(<span class="hljs-keyword">new</span> OnSuccessAndFaultSub(l) , YXSPreference.getMemberId()
);

}

OnSuccessAndFaultSub 繼承 Subscriber<ResponseBody>

public class UserApi {
    /**
     * 獲取用戶詳細信息
     */
    public static void getUserAllDetails(DisposableObserver<ResponseBody> subscriber, int memberId) {
        GetUserAllDetailsRequestBean bean = new GetUserAllDetailsRequestBean();
        bean.getData().setMemberId(memberId);
        Observable observable = HttpMethods.getInstance().getHttpService().getUserAllDetails(bean); //在HttpServer中
        HttpMethods.getInstance().toSubscribe(observable, subscriber);
    }
}

以上就是RxJava2+Retrofit2+OkHttp3的具體使用,看不明白或者寫的不對的咱們可以互相探討,希望看了能幫助到大家,如需轉載請註明地址,喜歡請關注一下感激不盡,我是猿裏面比較會喫的,也會寫點京城美食的相關文章~
Github地址:https://github.com/bigeyechou/NetWorkFrame

      </div>
    </div>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章