重拾Android之路之Retrofit+RxJava+OkHttp


引言

Android項(xiàng)目必備基本輪子--------異步網(wǎng)絡(luò)請(qǐng)求框架周蹭。

先不考慮在手項(xiàng)目的進(jìn)度趋艘,也不管UI組件的深探,先來(lái)將Restful客戶(hù)端的輪子造起來(lái)凶朗!

現(xiàn)在A(yíng)ndroid 市面上很火的當(dāng)然是 RetrofitRxJava + OkHttp, 功能強(qiáng)大瓷胧,簡(jiǎn)單易用,因此選用這套方案來(lái)改造網(wǎng)絡(luò)庫(kù)棚愤。


簡(jiǎn)介:

Retrofit: Retrofit是Square公司開(kāi)發(fā)的一款針對(duì)Android 網(wǎng)絡(luò)請(qǐng)求的框架搓萧。底層基于OkHttp實(shí)現(xiàn),OkHttp 已經(jīng)得到了Google 官方的認(rèn)可宛畦。Retrofit官網(wǎng)

OkHttp: 也是Square 開(kāi)源的網(wǎng)絡(luò)請(qǐng)求庫(kù)

RxJava:RxJava在 GitHub 主頁(yè)上的自我介紹是 "a library for composing asynchronous and event-based programs using observable sequences for the Java VM"(一個(gè)在 Java VM 上使用可觀(guān)測(cè)的序列來(lái)組成異步的矛绘、基于事件的程序的庫(kù))。這就是 RxJava 刃永,概括得非常精準(zhǔn)货矮。總之就是讓異步操作變得非常簡(jiǎn)單斯够。

各自的職責(zé):Retrofit 負(fù)責(zé)請(qǐng)求的數(shù)據(jù)和請(qǐng)求的結(jié)果囚玫,使用接口的方式呈現(xiàn),OkHttp 負(fù)責(zé)請(qǐng)求的過(guò)程读规,RxJava 負(fù)責(zé)異步抓督,各種線(xiàn)程之間的切換。

RxJava + Retrofit + okHttp 已成為當(dāng)前Android 網(wǎng)絡(luò)請(qǐng)求最流行的方式束亏。


分別實(shí)例介紹

一铃在,Retrofit 寫(xiě)一個(gè)網(wǎng)絡(luò)請(qǐng)求

以獲取豆瓣 Top250 榜單為例,地址:https://api.douban.com/v2/movie/

  1. 首先,要使用Retrofit ,你肯定需要把它的包引入定铜,在你的build.gradle文件中添加如下配置:
//下面兩個(gè)是RxJava 和RxAndroid 
compile 'io.reactivex:rxjava:1.1.0' 
compile 'io.reactivex:rxandroid:1.2.0'

compile 'com.squareup.retrofit2:retrofit:2.4.0'//retrofit   
compile 'com.squareup.retrofit2:converter-gson:2.4.0'//轉(zhuǎn)換器阳液,請(qǐng)求結(jié)果轉(zhuǎn)換成Model 
compile 'com.squareup.retrofit2:adapter-rxjava:2.4.0'//配合Rxjava 使用
  1. 創(chuàng)建一個(gè)Retrofit 實(shí)例,并且完成相關(guā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();

說(shuō)明:配置了接口的baseUrl和一個(gè)converter,GsonConverterFactory 是默認(rèn)提供的Gson 轉(zhuǎn)換器揣炕,Retrofit 也支持其他的一些轉(zhuǎn)換器帘皿,詳情請(qǐng)看官網(wǎng)Retrofit官網(wǎng)

  1. 創(chuàng)建一個(gè)接口 ,代碼如下:
public interface MovieService { 

 //獲取豆瓣Top250 榜單 
 @GET("top250")
 Call<MovieSubject> getTop250(@Query("start") int start,@Query("count")int count);
}
  1. 用Retrofit 創(chuàng)建 接口實(shí)例 MoiveService,并且調(diào)用接口中的方法進(jìn)行網(wǎng)絡(luò)請(qǐng)求畸陡,代碼如下:
//獲取接口實(shí)例
MovieService MovieService movieService = retrofit.create(MovieService.class); 
//調(diào)用方法得到一個(gè)Call 
Call<MovieSubject> call = movieService.getTop250(0,20);
 //進(jìn)行網(wǎng)絡(luò)請(qǐng)求 
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(); 
      } 
});

以上是異步方式請(qǐng)求鹰溜,還有同步方式execute(),返回一個(gè)Response,代碼如下:

Response<MovieSubject> response = call.execute();

以上就是用Retrofit 完成了一個(gè)網(wǎng)絡(luò)請(qǐng)求,獲取豆瓣top250 榜單電影丁恭,效果圖如下:

以上示例是用get方式完成曹动,如果要使用post方式,我們只需要修改一下接口中的方法定義牲览,如下:

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

說(shuō)明:使用POST請(qǐng)求方式時(shí)仁期,只需要更改方法定義的標(biāo)簽,用@POST 標(biāo)簽竭恬,參數(shù)標(biāo)簽用 @Field 或者@Body或者FieldMap跛蛋,注意:使用POST 方式時(shí)注意2點(diǎn),1痊硕,必須加上 @FormUrlEncoded標(biāo)簽赊级,否則會(huì)拋異常。2岔绸,使用POST方式時(shí)理逊,必須要有參數(shù),否則會(huì)拋異常, 源碼拋異常的地方如下:

if (isFormEncoded && !gotField) { 
      throw methodError("Form-encoded method must contain at least one @Field."); 
}

以上就是一個(gè)使用Retrofit 完成一個(gè)網(wǎng)絡(luò)請(qǐng)求的完整示例盒揉,其他標(biāo)簽使用方式請(qǐng)看官網(wǎng)Retrofit官網(wǎng)晋被,官網(wǎng)用法也介紹的比較詳細(xì),此外刚盈,發(fā)現(xiàn)了一篇博客也介紹得比較詳細(xì)羡洛,Retrofit用法詳解

二,配合RxJava 使用
  1. 更改定義的接口藕漱,返回值不再是一個(gè)Call ,而是返回的一個(gè)Observble.
public interface MovieService { 
    //獲取豆瓣Top250 榜單  
    @GET("top250") 
    Observable<MovieSubject> getTop250(@Query("start") int start, @Query("count")int count);
 }
  1. 創(chuàng)建Retrofit 的時(shí)候添加如下代碼
addCallAdapterFactory(RxJavaCallAdapterFactory.create())
  1. 添加轉(zhuǎn)換器Converter(將json 轉(zhuǎn)為JavaBean)
addConverterFactory(GsonConverterFactory.create())
  1. Activity 或者 Fragment 中傳入 Subscriber 建立訂閱關(guān)系
Subscription subscription = movieService.getTop250(0,20) 
.subscribeOn(Schedulers.io()) 
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<MovieSubject>() { 
@Override
 public void onCompleted() { 

 } 
@Override 
public void onError(Throwable e) { 

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

以上是加入RxJava后的網(wǎng)絡(luò)請(qǐng)求欲侮,返回不再是一個(gè)Call ,而是一個(gè)Observable, 在Activity/ Fragment 中傳入一個(gè)Subscriber 建立訂閱關(guān)系,就可以在 onNext 中處理結(jié)果了肋联,RxJava 的好處是幫我處理線(xiàn)程之間的切換威蕉,我們可以在指定訂閱的在哪個(gè)線(xiàn)程,觀(guān)察在哪個(gè)線(xiàn)程橄仍。我們可以通過(guò)操作符進(jìn)行數(shù)據(jù)變換韧涨。整個(gè)過(guò)程都是鏈?zhǔn)降碾蛊荩?jiǎn)化邏輯。其中FlatMap 操作符 還可以解除多層嵌套的問(wèn)題虑粥∪缧ⅲ總之,RxJava 很強(qiáng)大舀奶,能幫我處理很多復(fù)雜的場(chǎng)景暑竟,如果熟練使用的話(huà)斋射,那么能提升我們的開(kāi)發(fā)效率育勺。這里不打算講RxJava 的內(nèi)容,如果還不了解RxJava ,或者還對(duì)RxJava不熟悉的話(huà)罗岖,推薦幾篇寫(xiě)很優(yōu)秀的博客涧至。

1,RxJava 的經(jīng)典文章,扔物線(xiàn)的 給 Android 開(kāi)發(fā)者的 RxJava 詳解
2桑包,關(guān)于RxJava 友好的文章
3南蓬,關(guān)于RxJava 友好的文章-進(jìn)階

三,加入 OkHttp 配置

通過(guò)OkHttpClient可以配置很多東西哑了,比如鏈接超時(shí)時(shí)間赘方,緩存攔截器等等弱左。代碼如下:

// 創(chuàng)建 OKHttpClient 
OkHttpClient.Builder builder = new OkHttpClient.Builder(); 
     builder.connectTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS);//連接超時(shí)時(shí)間 
     builder.writeTimeout(DEFAULT_TIME_OUT,TimeUnit.SECONDS);//寫(xiě)操作 超時(shí)時(shí)間 
     builder.readTimeout(DEFAULT_TIME_OUT,TimeUnit.SECONDS);//讀操作超時(shí)時(shí)間 

  // 添加公共參數(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(RxJavaCallAdapterFactory.create()) 
     .addConverterFactory(GsonConverterFactory.create()) 
     .baseUrl(ApiConfig.BASE_URL) 
     .build();

以上只是配置了一些簡(jiǎn)單的項(xiàng)窄陡,如,連接超時(shí)時(shí)間拆火,實(shí)際項(xiàng)目中跳夭,我們可能有一些公共的參數(shù),如 :設(shè)備信息们镜,渠道币叹,Token之類(lèi)的,每個(gè)接口都需要用模狭,我們可以寫(xiě)一個(gè)攔截器颈抚,然后配置到OKHttpClient里,通過(guò) builder.addInterceptor(basicParamsInterceptor) 添加嚼鹉,這樣我們就不用每個(gè)接口都添加這些參數(shù)了邪意。緩存也可以通過(guò)寫(xiě)一個(gè)攔截器來(lái)實(shí)現(xiàn)(后面文章再講)。

以上就是Retrofit+RxJava+OkHttp實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求的簡(jiǎn)單演示反砌,如果每個(gè)接口都這么寫(xiě)的話(huà)雾鬼,代碼量太多,而且不優(yōu)雅宴树。所以還需要我們封裝一下策菜,接下來(lái)講一下對(duì)網(wǎng)絡(luò)請(qǐng)求框架的封裝。

參考博客:
1,Retrofit用法詳解
2又憨,基于Retrofit翠霍、OkHttp、Gson封裝通用網(wǎng)絡(luò)框架
3, RxJava 與 Retrofit 結(jié)合的最佳實(shí)踐


封裝

一蠢莺,創(chuàng)建一個(gè)統(tǒng)一生成接口實(shí)例的管理類(lèi)RetrofitServiceManager

我們知道寒匙,每一個(gè)請(qǐng)求,都需要一個(gè)接口躏将,里面定義了請(qǐng)求方法和請(qǐng)求參數(shù)等等锄弱,而獲取接口實(shí)例需要通過(guò)一個(gè)Retrofit實(shí)例,這一步都是相同的,因此祸憋,我們可以把這些相同的部分抽取出來(lái)会宪,代碼如下:

/*
* 
*/
public class RetrofitServiceManager { 
  private static final int DEFAULT_TIME_OUT = 5;//超時(shí)時(shí)間 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);//連接超時(shí)時(shí)間
  builder.writeTimeout(DEFAULT_READ_TIME_OUT,TimeUnit.SECONDS);//寫(xiě)操作 超時(shí)時(shí)間        
  builder.readTimeout(DEFAULT_READ_TIME_OUT,TimeUnit.SECONDS);//讀操作超時(shí)時(shí)間  

  // 添加公共參數(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(RxJavaCallAdapterFactory.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; 
  }  
  /** 
    * 獲取對(duì)應(yīng)的Service 
    * @param service Service 的 class     
    * @param <T>    
    * @return  
    */  
  public <T> T create(Class<T> service){ 
       return mRetrofit.create(service);    
  }
}

說(shuō)明:創(chuàng)建了一個(gè)RetrofitServiceManager類(lèi),該類(lèi)采用單例模式蚯窥,在私有的構(gòu)造方法中掸鹅,生成了Retrofit 實(shí)例,并配置了OkHttpClient和一些公共配置拦赠。提供了一個(gè)create()方法巍沙,生成接口實(shí)例,接收Class范型荷鼠,因此項(xiàng)目中所有的接口實(shí)例Service都可以用這個(gè)來(lái)生成句携,代碼如下:

mMovieService = RetrofitServiceManager.getInstance().create(MovieService.class);

通過(guò)create()方法生成了一個(gè)MovieService

二,創(chuàng)建接口颊咬,通過(guò)第一步獲取實(shí)例

上面已經(jīng)有了可以獲取接口實(shí)例的方法因此我們需要?jiǎng)?chuàng)建一個(gè)接口务甥,代碼如下:

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);
}

好了,有了接口我們就可以獲取到接口實(shí)例了mMovieService

三喳篇,創(chuàng)建一個(gè)業(yè)務(wù)Loader 敞临,如XXXLoder,獲取Observable并處理相關(guān)業(yè)務(wù)

解釋一下為什么會(huì)出現(xiàn)Loader ,我看其他相關(guān)文章說(shuō)麸澜,每一個(gè)Api 都寫(xiě)一個(gè)接口挺尿,我覺(jué)得這樣很麻煩,因此就把請(qǐng)求邏輯封裝在在一個(gè)業(yè)務(wù)Loader 里面炊邦,一個(gè)Loader里面可以處理多個(gè)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) {
           //可以處理對(duì)應(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)建一個(gè)MovieLoader,構(gòu)造方法中生成了mMovieService,而Service 中可以定義和業(yè)務(wù)相關(guān)的多個(gè)api,比如:例子中的MovieService中,可以定義和電影相關(guān)的多個(gè)api,獲取電影列表馁害、獲取電影詳情、搜索電影等api碘菜,就不用定義多個(gè)接口了凹蜈。

上面的代碼中限寞,MovieLoader是從ObjectLoader 中繼承下來(lái)的,ObjectLoader 提取了一些公共的操作仰坦。代碼如下:

/** 
 *
 * 將一些重復(fù)的操作提出來(lái)履植,放到父類(lèi)以免Loader 里每個(gè)接口都有重復(fù)代碼 
 * Created by zhouwei on 16/11/10.
 * 
 */
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());  
  }
}

相當(dāng)于一個(gè)公共方法,其實(shí)也可以放在一個(gè)工具類(lèi)里面悄晃,后面做緩存的時(shí)候會(huì)用到這個(gè)父類(lèi)玫霎,所以就把這個(gè)方法放到父類(lèi)里面。

四妈橄,Activity/Fragment 中的調(diào)用
創(chuàng)建Loader實(shí)例
mMovieLoader = new MovieLoader();

通過(guò)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());     
     }  
   });
}

以上就完成請(qǐng)求過(guò)程的封裝庶近,現(xiàn)在添加一個(gè)新的請(qǐng)求,只需要添加一個(gè)業(yè)務(wù)Loader 類(lèi)眷细,然后通過(guò)Loader調(diào)用方法獲取結(jié)果就行了拦盹,是不是方便了很多鹃祖?但是在實(shí)際項(xiàng)目中這樣是不夠的溪椎,還能做進(jìn)一步簡(jiǎn)化。

五恬口,統(tǒng)一處理結(jié)果和錯(cuò)誤
1,統(tǒng)一處理請(qǐng)求結(jié)果

現(xiàn)實(shí)項(xiàng)目中校读,所有接口的返回結(jié)果都是同一格式,如:

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

我們?cè)谡?qǐng)求api 接口的時(shí)候祖能,只關(guān)心我們想要的數(shù)據(jù)歉秫,也就上面的data,其他的東西我們不太關(guān)心,請(qǐng)求失敗的時(shí)候可以根據(jù)status判斷進(jìn)行錯(cuò)誤處理养铸,所以我們需要包裝一下雁芙。首先需要根據(jù)服務(wù)端定義的JSON 結(jié)構(gòu)創(chuàng)建一個(gè)BaseResponse 類(lèi),代碼如下:

/*
*
* 
* 網(wǎng)絡(luò)請(qǐng)求結(jié)果 基類(lèi) 
* Created by zhouwei on 16/11/10. 
*/
public class BaseResponse<T> {   
  public int status;  
  public String message;    
  public T data;    
  public boolean isSuccess(){   
     return status == 200;  
  }
}

有了統(tǒng)一的格式數(shù)據(jù)后钞螟,我們需要?jiǎng)冸x出data 返回給上層調(diào)用者兔甘,創(chuàng)建一個(gè)PayLoad 類(lèi),代碼如下:

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

PayLoad 繼承自Func1,接收一個(gè)BaseResponse<T> , 就是接口返回的JSON數(shù)據(jù)結(jié)構(gòu)洞焙,返回的是T,就是data,判斷是否請(qǐng)求成功,請(qǐng)求成功返回Data,請(qǐng)求失敗包裝成一個(gè)Fault 返回給上層統(tǒng)一處理錯(cuò)誤拯啦。在Loader類(lèi)里面獲取結(jié)果后澡匪,通過(guò)map 操作符剝離數(shù)據(jù)。代碼如下:

public Observable<List<Movie>> getMovie(int start, int count){ 
  return observe(mMovieService.getTop250(start,count))        
    .map(new PayLoad<List<Movie>>());
}
2褒链,統(tǒng)一處理錯(cuò)誤

在PayLoad 類(lèi)里面唁情,請(qǐng)求失敗時(shí),拋出了一個(gè)Fault 異常給上層甫匹,我在A(yíng)ctivity/Fragment 中拿到這個(gè)異常甸鸟,然后判斷錯(cuò)誤碼夯巷,進(jìn)行異常處理。在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){     
       //錯(cuò)誤處理 
    }else if(fault.getErrorCode() == 500){   
         //錯(cuò)誤處理  
    }else if(fault.getErrorCode() == 501){      
      //錯(cuò)誤處理   
    }  
  }
}

以上就可以對(duì)應(yīng)錯(cuò)誤碼處理相應(yīng)的錯(cuò)誤了哀墓。

六趁餐,添加公共參數(shù)

在實(shí)際項(xiàng)目中,每個(gè)接口都有一些基本的相同的參數(shù)篮绰,我們稱(chēng)之為公共參數(shù)后雷,比如:userIduserToken吠各、userName,deviceId等等臀突,我們不必要,每個(gè)接口都去寫(xiě)贾漏,這樣就太麻煩了候学,因此我們可以寫(xiě)一個(gè)攔截器,在攔截器里面攔截請(qǐng)求纵散,為每個(gè)請(qǐng)求都添加相同的公共參數(shù)梳码。攔截器代碼如下:

/*
 * 攔截器
 * 向請(qǐng)求頭里添加公共參數(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());*/ 
    // 新的請(qǐng)求   
    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 類(lèi)里面加入OkHttpClient 配置就好了掰茶。代碼如下:

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

這樣每個(gè)請(qǐng)求都添加了公共參數(shù)。

封裝的類(lèi)放在http包下:

電影列表:(數(shù)據(jù)來(lái)自豆瓣)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蜜笤,一起剝皮案震驚了整個(gè)濱河市濒蒋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌把兔,老刑警劉巖沪伙,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異县好,居然都是意外死亡围橡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)聘惦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)某饰,“玉大人,你說(shuō)我怎么就攤上這事善绎∏” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵禀酱,是天一觀(guān)的道長(zhǎng)炬守。 經(jīng)常有香客問(wèn)我,道長(zhǎng)剂跟,這世上最難降的妖魔是什么减途? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任酣藻,我火速辦了婚禮,結(jié)果婚禮上鳍置,老公的妹妹穿的比我還像新娘辽剧。我一直安慰自己,他們只是感情好税产,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布怕轿。 她就那樣靜靜地躺著,像睡著了一般辟拷。 火紅的嫁衣襯著肌膚如雪撞羽。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,760評(píng)論 1 289
  • 那天衫冻,我揣著相機(jī)與錄音诀紊,去河邊找鬼。 笑死隅俘,一個(gè)胖子當(dāng)著我的面吹牛邻奠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播考赛,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼惕澎,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼莉测!你這毒婦竟也來(lái)了颜骤?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤捣卤,失蹤者是張志新(化名)和其女友劉穎忍抽,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體董朝,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鸠项,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了子姜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片祟绊。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖哥捕,靈堂內(nèi)的尸體忽然破棺而出牧抽,到底是詐尸還是另有隱情,我是刑警寧澤遥赚,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布扬舒,位于F島的核電站,受9級(jí)特大地震影響凫佛,放射性物質(zhì)發(fā)生泄漏讲坎。R本人自食惡果不足惜孕惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望晨炕。 院中可真熱鬧衫画,春花似錦、人聲如沸瓮栗。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)遵馆。三九已至鲸郊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間货邓,已是汗流浹背秆撮。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留换况,地道東北人职辨。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像戈二,于是被迫代替她去往敵國(guó)和親舒裤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

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