Android版&Kotlin版RxJava2+Retrofit2+OkHttp3的基礎(chǔ)夯接、封裝和項目中的使用

前言:

近些年很火的Retrofit+RxJava+OkHttp網(wǎng)絡(luò)請求框架晴弃,功能強(qiáng)大上鞠,結(jié)構(gòu)合理芍阎,使用簡單方便谴咸。后面還會給大家發(fā)自己整理過的Retrofit和RxJava岭佳、RxAndroid和RxBus珊随。希望大家點一下關(guān)注叶洞,讓我這個懶癌患者有動力繼續(xù)寫下去京办!
本篇分三個部分:基礎(chǔ)篇惭婿、封裝篇和自己項目使用篇财饥,項目是自己公司的APP提取的钥星,文章偏長可以分三部分一點點看谦炒,當(dāng)初看了很多優(yōu)秀的文章然后自己在整理寫在印象筆記中。
感謝大佬們的學(xué)習(xí)參考文章:
扔物線:http://gank.io/post/560e15be2dca930e00da1083
依然范特西http://www.reibang.com/p/5bc866b9cbb9
拉丁吳:https://juejin.im/post/580103f20e3dd90057fc3e6d
玉剛說https://juejin.im/post/5b17560e6fb9a01e2862246f
持續(xù)迭代中...还蹲,更新內(nèi)容是升級版本替換成RxJava2谜喊,修改了一些語法斗遏。
Github地址:java版本:https://github.com/bigeyechou/NetWorkFrame
Github地址:kotlin的版本:https://github.com/bigeyechou/AndroidKotlin

簡單介紹Retrofit怒坯、OKHttp和RxJava之間的關(guān)系:

  • Retrofit:Retrofit是Square公司開發(fā)的一款針對Android 網(wǎng)絡(luò)請求的框架(底層默認(rèn)是基于OkHttp 實現(xiàn))剔猿。
  • OkHttp:也是Square公司的一款開源的網(wǎng)絡(luò)請求庫归敬。
  • RxJava :"a library for composing asynchronous and event-based programs using observable sequences for the Java VM"(一個在 Java VM 上使用可觀測的序列來組成異步的汪茧、基于事件的程序的庫)舱污。RxJava使異步操作變得非常簡單扩灯。

各自職責(zé):Retrofit 負(fù)責(zé) 請求的數(shù)據(jù) 和 請求的結(jié)果,使用 接口的方式 呈現(xiàn)捻撑,OkHttp 負(fù)責(zé)請求的過程顾患,RxJava 負(fù)責(zé)異步描验,各種線程之間的切換膘流。

基礎(chǔ)篇:

一呼股、Retrofit寫一個網(wǎng)絡(luò)請求:

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

compile 'com.squareup.retrofit2:retrofit:2.3.0'//導(dǎo)入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'//轉(zhuǎn)換器缠局,請求結(jié)果轉(zhuǎn)換成Model
compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'//配合Rxjava 使用

2.創(chuàng)建一個Retrofit 實例狭园,并且完成相關(guān)的配置:
配置了接口的 BASE_URL 和一個 converter , GsonConverterFactory 是默認(rèn)提供的 Gson轉(zhuǎn)換器罚舱。

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.創(chuàng)建一個接口:
定義了一個方法 getTop250 ,使用 get請求方式管闷,加上@GET 標(biāo)簽包个,標(biāo)簽后面是這個接口的 尾址top250,完整的地址應(yīng)該是 baseUrl+尾址 呕臂,參數(shù) 使用@Query標(biāo)簽,如果參數(shù)多的話可以用@QueryMap標(biāo) 簽州既,接收一個Map吴叶。
使用 POST 請求方式時蚌卤,只需要更改方法定義的標(biāo)簽,用 @POST 標(biāo)簽侮叮,參數(shù)標(biāo)簽用 @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標(biāo)簽囊榜,否則會拋異常砂沛。
  • 必須要有參數(shù)尺上,否則會拋異常, 源碼拋異常的地方如下:
if (isFormEncoded && !gotField) {
      throw methodError("Form-encoded method must contain at least one @Field.");
}

4.用 Retrofit 創(chuàng)建 接口實例 MoiveService 并且調(diào)用接口中的方法進(jìn)行網(wǎng)絡(luò)請求:
異步方式請求:

//獲取接口實例
MovieService movieService = retrofit.create(MovieService.class);
//調(diào)用方法得到一個Call
Call<MovieSubject> call = movieService.getTop250(0,20);
 //進(jìn)行網(wǎng)絡(luò)請求
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.創(chuàng)建 Retrofit 的時候添加如下代碼:

addCallAdapterFactory(RxJava2CallAdapterFactory.create())

3.添加轉(zhuǎn)換器Converter(將 json 轉(zhuǎn)為 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 建立訂閱關(guān)系:

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 后的網(wǎng)絡(luò)請求白胀,返回不再是一個 Call 或杠,而是一個 Observable向抢。
  • 在Activity / Fragment 中傳入一個Subscriber 建立訂閱關(guān)系叉信,就可以在 onNext 中處理結(jié)果了硼身。
  • RxJava 的好處是幫我處理 線程之間的切換佳遂,我們可以在指定 訂閱的在哪個線程讶迁,觀察在哪個線程啸驯。
  • 可以 通過操作符 進(jìn)行數(shù)據(jù)變換罚斗。
  • 整個過程都是鏈?zhǔn)降恼胱耍喕壿嬀嘁F渲蠪latMap 操作符 還可以解除多層嵌套的問題榕暇。

RxJava 很強(qiáng)大,能幫我處理很多復(fù)雜的場景缴啡,如果熟練使用的話业栅,那么能提升我們的開發(fā)效率反镇。

三歹茶,加入 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);//錯誤重連

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

builder.addInterceptor(basicParamsInterceptor);

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

列舉項目中用到的如下:

//項目中設(shè)置頭信息
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);//添加請求頭信息萍程,服務(wù)器進(jìn)行token有效性驗證
        Request request = requestBuilder.build();
        return chain.proceed(request);
    }
};
okHttpBuilder.addInterceptor(headerInterceptor);

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

封裝篇

一,創(chuàng)建一個 統(tǒng)一生成接口實例的管理類 RetrofitServiceManager

創(chuàng)建了一個 RetrofitServiceManager 類兔仰,該類采用 單例模式茫负,在 私有的 構(gòu)造方法中乎赴,生成了 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(){ 
      // 創(chuàng)建 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);//讀操作超時時間 
     
     // 添加公共參數(shù)攔截器       
     HttpCommonInterceptor commonInterceptor = new HttpCommonInterceptor.Builder()
               .addHeaderParams("paltform","android")
               .addHeaderParams("userToken","1234343434dfdfd3434")
               .addHeaderParams("userId","123445")     
               .build();       
     builder.addInterceptor(commonInterceptor);  
 
     // 創(chuàng)建Retrofit       
     mRetrofit = new 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();
    }

    /**
     * 獲取RetrofitServiceManager
     * @return
     */   
 public static RetrofitServiceManager getInstance(){ 
      return SingletonHolder.INSTANCE;
   } 

  /**
    * 獲取對應(yīng)的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);
二嗤堰,創(chuàng)建接口,通過第一步獲取實例

有了可以獲取接口實例的方法度宦,然后創(chuàng)建一個接口踢匣,代碼如下:

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);
}
三,創(chuàng)建一個業(yè)務(wù)Loader 戈抄,如XXXLoder,獲取Observable并處理相關(guān)業(yè)務(wù)

創(chuàng)建 Loader 的原因:每一個Api 都寫一個接口很麻煩离唬,因此就把 請求邏輯 封裝在 一個業(yè)務(wù)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) {
          //可以處理對應(yīng)的邏輯后在返回
            return s;   
      }
    });
}

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); 
}
}
創(chuàng)建一個MovieLoader,構(gòu)造方法中生成了mHttpService输莺,而 Service 中可以定義和業(yè)務(wù)相關(guān)的多個api,比如:例子中的HttpService中裸诽,
可以定義和電影相關(guān)的多個api,獲取電影列表嫂用、獲取電影詳情、搜索電影等api丈冬,就不用定義多個接口了嘱函。

MovieLoader 是從 ObjectLoader 中繼承下來的,ObjectLoader 提取了一些公共的操作埂蕊。代碼如下:
/** 
 * 將一些重復(fù)的操作提出來往弓,放到父類以免Loader 里每個接口都有重復(fù)代碼 
 */
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 中的調(diào)用

創(chuàng)建Loader實例:

mMovieLoader = new MovieLoader();

通過Loader 調(diào)用方法獲取結(jié)果,代碼如下:

/**
 * 獲取電影列表
 */
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());   
      } 
  });
}
五蓄氧,統(tǒng)一處理結(jié)果和錯誤

1.統(tǒng)一處理請求結(jié)果:
現(xiàn)實項目中函似,所有接口的返回結(jié)果都是同一格式,如:

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

在請求api 接口的時候喉童,只關(guān)心想要的數(shù)據(jù)撇寞,也就上面的 data{ },其他的東西不太關(guān)心,請求失敗 的時候可以根據(jù) status 判斷進(jìn)行 錯誤處理蔑担。
包裝返回結(jié)果:首先需要根據(jù)服務(wù)端定義的 JSON 結(jié)構(gòu)創(chuàng)建一個 BaseResponse 類露氮,代碼如下:

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

有了統(tǒng)一的格式數(shù)據(jù)后,我們需要 剝離出data{ }返回給 上層調(diào)用者钟沛,創(chuàng)建一個 PayLoad 類畔规,代碼如下:

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

PayLoad 繼承自 Func1恨统,接收一個BaseResponse<T> , 就是接口返回的 JSON 數(shù)據(jù)結(jié)構(gòu)叁扫,返回的是 T,就是data{ }畜埋,判斷是否請求成功莫绣,請求成功 返回Data,請求失敗 包裝成一個 Fault 返回給上層統(tǒng)一處理錯誤悠鞍。
在Loader類里面獲取結(jié)果后对室,通過map 操作符剝離數(shù)據(jù)。代碼如下:

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

2.統(tǒng)一處理錯誤:
在PayLoad 類里面咖祭,請求失敗時掩宜,拋出了一個Fault 異常給上層,我在Activity/Fragment 中拿到這個異常么翰,然后判斷錯誤碼牺汤,進(jìn)行異常處理。在onError () 中添加浩嫌。
對應(yīng) 錯誤碼 處理 相應(yīng)的錯誤檐迟,代碼如下:

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){     
      //錯誤處理 
    } 
  }
}
六,添加公共參數(shù)

實際項目中码耐,每個接口都有一些基本的相同的參數(shù)追迟,我們稱之為公共參數(shù),比如:userId骚腥、userToken敦间、userName、deviceId等等桦沉,我們不必每個接口都去寫每瞒,可以寫一個攔截器金闽,在攔截器里面攔截請求纯露,為每個請求都添加相同的公共參數(shù)。
攔截器代碼如下:

/*
 * 攔截器
 *
 * 向請求頭里添加公共參數(shù)
 */
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();   
        // 添加新的參數(shù)代芜,添加到url 中 
        /*HttpUrl.Builder authorizedUrlBuilder = oldRequest.url().newBuilder()     
        .scheme(oldRequest.url().scheme()) 
        .host(oldRequest.url().host());*/
 
      // 新的請求 
      Request.Builder requestBuilder =  oldRequest.newBuilder();
      requestBuilder.method(oldRequest.method(), oldRequest.body());

      //添加公共參數(shù),添加到header中       
     if(mHeaderParamsMap.size() > 0){     
          for(Map.Entry<String,String> 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;   
  } 

  }
}

以上就是添加公共參數(shù)的攔截器埠褪,在 RetrofitServiceManager 類里面加入OkHttpClient 配置就好了。
代碼如下:

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

項目使用篇 ----->插入廣告!本項目來源于金融研習(xí)社App钞速,金融理財類的在線教育

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

    compile 'com.google.code.gson:gson:2.6.2'//導(dǎo)入Gson 庫
    //導(dǎo)入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'//導(dǎo)入retrofit
    compile 'com.squareup.retrofit2:converter-gson:2.3.0'//轉(zhuǎn)換器贷掖,請求結(jié)果轉(zhuǎn)換成Model
    compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'//配合Rxjava 使用
    compile 'com.squareup.okhttp3:logging-interceptor:3.8.1'//添加HttpLoggingInterceptor進(jìn)行調(diào)試

2.創(chuàng)建一個HttpService接口:

public interface HttpService {
/**
 * 獲取用戶詳細(xì)資料
 */
@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.創(chuàng)建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;
    /**
     * 請求失敗重連次數(shù)
     */
    private int RETRY_COUNT = 0;
    private OkHttpClient.Builder okHttpBuilder;

    //構(gòu)造方法私有
    private HttpMethods() {
        //手動創(chuàng)建一個OkHttpClient并設(shè)置超時時間
        okHttpBuilder = new OkHttpClient.Builder();

        /**
         * 設(shè)置緩存
         */
        File cacheFile = new File(ApplicationContext.context.getExternalCacheDir(), CACHE_NAME);
        Cache cache = new Cache(cacheFile, 1024 * 1024 * 50);
        Interceptor cacheInterceptor = new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request request = chain.request();
                if (!NetUtil.isNetworkConnected()) {
                    request = request.newBuilder()
                            .cacheControl(CacheControl.FORCE_CACHE)
                            .build();
                }
                Response response = chain.proceed(request);
                if (!NetUtil.isNetworkConnected()) {
                    int maxAge = 0;
                    // 有網(wǎng)絡(luò)時 設(shè)置緩存超時時間0個小時
                    response.newBuilder()
                            .header("Cache-Control", "public, max-age=" + maxAge)
                            .removeHeader(CACHE_NAME)// 清除頭信息渴语,因為服務(wù)器如果不支持苹威,會返回一些干擾信息,不清除下面無法生效
                            .build();
                } else {
                    // 無網(wǎng)絡(luò)時驾凶,設(shè)置超時為4周
                    int maxStale = 60 * 60 * 24 * 28;
                    response.newBuilder()
                            .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                            .removeHeader(CACHE_NAME)
                            .build();
                }
                return response;
            }
        };
        okHttpBuilder.cache(cache).addInterceptor(cacheInterceptor);


        /**
         * 設(shè)置頭信息
         */
        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);//添加請求頭信息牙甫,服務(wù)器進(jìn)行token有效性驗證
                Request request = requestBuilder.build();
                return 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);
        //設(shè)置 Debug Log 模式
        okHttpBuilder.addInterceptor(loggingInterceptor);
//        }
        /**
         * 設(shè)置超時和重新連接
         */
        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 = new Retrofit.Builder()
                .client(okHttpBuilder.build())
                .addConverterFactory(GsonConverterFactory.create())//json轉(zhuǎn)換成JavaBean
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl(BASE_URL)
                .build();
        httpService = retrofit.create(HttpService.class);
    }

    //在訪問HttpMethods時創(chuàng)建單例
    private static class SingletonHolder {
        private static final HttpMethods INSTANCE = new HttpMethods();

    }

    //獲取單例
    public static HttpMethods getInstance() {
        return SingletonHolder.INSTANCE;
    }

    /**
     * 獲取retrofit
     */
    public Retrofit getRetrofit() {
        return retrofit;
    }

    public void changeBaseUrl(String baseUrl) {
        retrofit = new Retrofit.Builder()
                .client(okHttpBuilder.build())
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl(baseUrl)
                .build();
        httpService = retrofit.create(HttpService.class);
    }

    /**
     * 獲取httpService
     */
    public HttpService getHttpService() {
        return httpService;
    }

     /**
     * 設(shè)置訂閱 和 所在的線程環(huán)境
     */
    public <T> void toSubscribe(Observable<T> o, DisposableObserver<T> s) {

        o.subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .retry(RETRY_COUNT)//請求失敗重連次數(shù)
                .subscribe(s);

    }
}

4.設(shè)置回調(diào):
調(diào)用者自己對請求數(shù)據(jù)進(jìn)行處理 成功時 通過result是否等于1分別回調(diào)onSuccees和onFault,默認(rèn)處理了401錯誤轉(zhuǎn)登錄调违。

public class OnSuccessAndFaultSub extends DisposableObserver<ResponseBody> implements ProgressCancelListener {
    /**
     * 是否需要顯示默認(rèn)Loading
     */
    private boolean showProgress = true;
    private OnSuccessAndFaultListener mOnSuccessAndFaultListener;

    private Context context;
    private WaitProgressDialog progressDialog;

    /**
     * @param mOnSuccessAndFaultListener 成功回調(diào)監(jiān)聽
     */
    public OnSuccessAndFaultSub(OnSuccessAndFaultListener mOnSuccessAndFaultListener) {
        this.mOnSuccessAndFaultListener = mOnSuccessAndFaultListener;
    }

    /**
     * @param mOnSuccessAndFaultListener 成功回調(diào)監(jiān)聽
      * @param context                    上下文
      */
    public OnSuccessAndFaultSub(OnSuccessAndFaultListener mOnSuccessAndFaultListener, Context context) {
        this.mOnSuccessAndFaultListener = mOnSuccessAndFaultListener;
        this.context = context;
        progressDialog = new WaitProgressDialog(context, this);
    }

    /**
     * @param mOnSuccessAndFaultListener 成功回調(diào)監(jiān)聽
      * @param context                    上下文
      * @param showProgress               是否需要顯示默認(rèn)Loading
     */
    public OnSuccessAndFaultSub(OnSuccessAndFaultListener mOnSuccessAndFaultListener, Context context, boolean showProgress) {
        this.mOnSuccessAndFaultListener = mOnSuccessAndFaultListener;
        this.context = context;
        progressDialog = new WaitProgressDialog(context, this);
        this.showProgress = showProgress;
    }

    private void showProgressDialog() {
        if (showProgress && null != progressDialog) {
            progressDialog.show();
        }
    }

    private void dismissProgressDialog() {
        if (showProgress && null != progressDialog) {
            progressDialog.dismiss();
        }
    }

    /**
     * 訂閱開始時調(diào)用
      * 顯示ProgressDialog
     */
    @Override
    public void onStart() {
        showProgressDialog();
    }

    /**
     * 完成窟哺,隱藏ProgressDialog
     */
    @Override
    public void onComplete() {
        dismissProgressDialog();
        progressDialog = null;
    }

    /**
     * 對錯誤進(jìn)行統(tǒng)一處理
      * 隱藏ProgressDialog
     */
    @Override
    public void onError(Throwable e) {
        try {

            if (e instanceof SocketTimeoutException) {//請求超時
            } else if (e instanceof ConnectException) {//網(wǎng)絡(luò)連接超時
                mOnSuccessAndFaultListener.onFault("網(wǎng)絡(luò)連接超時");
            } else if (e instanceof SSLHandshakeException) {//安全證書異常
                mOnSuccessAndFaultListener.onFault("安全證書異常");
            } else if (e instanceof HttpException) {//請求的地址不存在
                int code = ((HttpException) e).code();
                if (code == 504) {
                    mOnSuccessAndFaultListener.onFault("網(wǎng)絡(luò)異常,請檢查您的網(wǎng)絡(luò)狀態(tài)");
                } else if (code == 404) {
                    mOnSuccessAndFaultListener.onFault("請求的地址不存在");
                } else {
                    mOnSuccessAndFaultListener.onFault("請求失敗");
                }
            } else if (e instanceof UnknownHostException) {//域名解析失敗
                mOnSuccessAndFaultListener.onFault("域名解析失敗");
            } else {
                mOnSuccessAndFaultListener.onFault("error:" + e.getMessage());
            }
        } catch (Exception e2) {
            e2.printStackTrace();
        } finally {
            Log.e("OnSuccessAndFaultSub", "error:" + e.getMessage());
            dismissProgressDialog();
            progressDialog = null;

        }

    }

    /**
     * 當(dāng)result等于1回調(diào)給調(diào)用者技肩,否則自動顯示錯誤信息且轨,若錯誤信息為401跳轉(zhuǎn)登錄頁面。
     * ResponseBody  body = response.body();//獲取響應(yīng)體
     * InputStream inputStream = body.byteStream();//獲取輸入流
     * byte[] bytes = body.bytes();//獲取字節(jié)數(shù)組
     * String str = body.string();//獲取字符串?dāng)?shù)據(jù)
     */
    @Override
    public void onNext(ResponseBody body) {
        try {
            final String result = CompressUtils.decompress(body.byteStream());
            Log.e("body", result);
            JSONObject jsonObject = new JSONObject(result);
            int resultCode = jsonObject.getInt("ErrorCode");
            if (resultCode == 1) {
                mOnSuccessAndFaultListener.onSuccess(result);
            } else {
                String errorMsg = jsonObject.getString("ErrorMessage");
                mOnSuccessAndFaultListener.onFault(errorMsg);
                Log.e("OnSuccessAndFaultSub", "errorMsg: " + errorMsg);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 取消ProgressDialog的時候虚婿,取消對observable的訂閱旋奢,同時也取消了http請求
      */
    @Override
    public void onCancelProgress() {
        if (!this.isDisposed()) {
            this.dispose();
        }
    }
}
 *請求服務(wù)loading關(guān)閉監(jiān)聽 
 */
public interface ProgressCancelListener {
    void onCancelProgress();
}

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

private void getUserData() {
    OnSuccessAndFaultListener l = new OnSuccessAndFaultListener() {
        @Override
        public void onSuccess(String result) {//成功回調(diào)
            YXSPreference.setUserData(result);
            userDataBean = GsonUtils.fromJson(result, UserDetailResponseBean.class);
            fullData();
        }

        @Override
        public void onFault(String errorMsg) {//失敗的回調(diào)
            SnackBarManager.showShortMsg(getActivity(), errorMsg);
        }
    };

    UserApi.getUserAllDetails(new OnSuccessAndFaultSub(l) , YXSPreference.getMemberId()
    );
}

OnSuccessAndFaultSub 繼承 Subscriber<ResponseBody>

public class UserApi {
    /**
     * 獲取用戶詳細(xì)信息
     */
    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的具體使用然痊,看不明白或者寫的不對的咱們可以互相探討黄绩,希望看了能幫助到大家,如需轉(zhuǎn)載請注明地址玷过,喜歡請關(guān)注一下感激不盡爽丹,我是猿里面比較會吃的,也會寫點京城美食的相關(guān)文章~
Github地址:https://github.com/bigeyechou/NetWorkFrame
轉(zhuǎn)換為kotlin的版本:https://github.com/bigeyechou/AndroidKotlin

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末辛蚊,一起剝皮案震驚了整個濱河市粤蝎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌袋马,老刑警劉巖初澎,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異虑凛,居然都是意外死亡碑宴,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門桑谍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來延柠,“玉大人,你說我怎么就攤上這事锣披≌昙洌” “怎么了贿条?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長增热。 經(jīng)常有香客問我整以,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮账忘,結(jié)果婚禮上振愿,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著番刊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪影锈。 梳的紋絲不亂的頭發(fā)上芹务,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天,我揣著相機(jī)與錄音鸭廷,去河邊找鬼枣抱。 笑死,一個胖子當(dāng)著我的面吹牛辆床,可吹牛的內(nèi)容都是我干的佳晶。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼讼载,長吁一口氣:“原來是場噩夢啊……” “哼轿秧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起咨堤,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤菇篡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后一喘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體驱还,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年凸克,在試婚紗的時候發(fā)現(xiàn)自己被綠了议蟆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡萎战,死狀恐怖咐容,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情撞鹉,我是刑警寧澤疟丙,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布颖侄,位于F島的核電站鸟雏,受9級特大地震影響享郊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜孝鹊,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一炊琉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧又活,春花似錦苔咪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至耐薯,卻和暖如春舔清,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背曲初。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工体谒, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人臼婆。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓抒痒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親颁褂。 傳聞我的和親對象是個殘疾皇子故响,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,611評論 2 353

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