前面一篇文章講了一下Retrofit+ RxJava 請求網絡的一些基本用法,還沒有看過的可以去看一下Retrofit + RxJava + OkHttp 讓網絡請求變的簡單-基礎篇,正如標題所說的,Retrofit+RxJava 是讓我們的網絡請求變得簡單,代碼精簡。通過前一篇文章,我們感覺寫一個請求還是有點麻煩,作為程序員,我們的目標就是“偷懶”,絕不重復搬磚。因此我們還需要封裝一下,來簡化我們使用,接下來進入正題。
一,創建一個統一生成接口實例的管理類RetrofitServiceManager
我們知道,每一個請求,都需要一個接口,里面定義了請求方法和請求參數等等,而獲取接口實例需要通過一個Retrofit實例,這一步都是相同的,因此,我們可以把這些相同的部分抽取出來,代碼如下:
/*
*
* Created by zhouwei on 16/11/9.
*/
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);//讀操作超時時間
// 添加公共參數攔截器
HttpCommonInterceptor commonInterceptor = new HttpCommonInterceptor.Builder()
.addHeaderParams("paltform","android")
.addHeaderParams("userToken","1234343434dfdfd3434")
.addHeaderParams("userId","123445")
.build();
builder.addInterceptor(commonInterceptor);
// 創建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;
}
/**
* 獲取對應的Service
* @param service Service 的 class
* @param <T>
* @return
*/
public <T> T create(Class<T> service){
return mRetrofit.create(service);
}
}
說明:創建了一個RetrofitServiceManager類,該類采用單例模式,在私有的構造方法中,生成了Retrofit 實例,并配置了OkHttpClient和一些公共配置。提供了一個create()方法,生成接口實例,接收Class范型,因此項目中所有的接口實例Service都可以用這個來生成,代碼如下:
mMovieService = RetrofitServiceManager.getInstance().create(MovieService.class);
通過create()方法生成了一個MovieService
二,創建接口,通過第一步獲取實例
上面已經有了可以獲取接口實例的方法因此我們需要創建一個接口,代碼如下:
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);
}
好了,有了接口我們就可以獲取到接口實例了mMovieService
三,創建一個業務Loader ,如XXXLoder,獲取Observable并處理相關業務
解釋一下為什么會出現Loader ,我看其他相關文章說,每一個Api 都寫一個接口,我覺得這樣很麻煩,因此就把請求邏輯封裝在在一個業務Loader 里面,一個Loader里面可以處理多個Api 接口。代碼如下:
/*
*
* Created by zhouwei on 16/11/10.
*/
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);
@FormUrlEncoded
@POST("/x3/weather")
Call<String> getWeather(@Field("cityId") String cityId, @Field("key") String key);
}
}
創建一個MovieLoader,構造方法中生成了mMovieService,而Service 中可以定義和業務相關的多個api,比如:例子中的MovieService中,
可以定義和電影相關的多個api,獲取電影列表、獲取電影詳情、搜索電影等api,就不用定義多個接口了。
上面的代碼中,MovieLoader是從ObjectLoader 中繼承下來的,ObjectLoader 提取了一些公共的操作。代碼如下:
/**
*
* 將一些重復的操作提出來,放到父類以免Loader 里每個接口都有重復代碼
* 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());
}
}
相當于一個公共方法,其實也可以放在一個工具類里面,后面做緩存的時候會用到這個父類,所以就把這個方法放到父類里面。
四,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());
}
});
}
以上就完成請求過程的封裝,現在添加一個新的請求,只需要添加一個業務Loader 類,然后通過Loader調用方法獲取結果就行了,是不是方便了很多?但是在實際項目中這樣是不夠的,還能做進一步簡化。
五,統一處理結果和錯誤
1,統一處理請求結果
現實項目中,所有接口的返回結果都是同一格式,如:
{
"status": 200,
"message": "成功",
"data": {}
}
我們在請求api 接口的時候,只關心我們想要的數據,也就上面的data,其他的東西我們不太關心,請求失敗的時候可以根據status判斷進行錯誤處理,所以我們需要包裝一下。首先需要根據服務端定義的JSON 結構創建一個BaseResponse 類,代碼如下:
/*
*
*
* 網絡請求結果 基類
* 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;
}
}
有了統一的格式數據后,我們需要剝離出data 返回給上層調用者,創建一個PayLoad 類,代碼如下:
/*
*
*
* 剝離 最終數據
* Created by zhouwei on 16/11/10.
*/
public class PayLoad<T> implements Func1<BaseResponse<T>,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等等,我們不必要,每個接口都去寫,這樣就太麻煩了,因此我們可以寫一個攔截器,在攔截器里面攔截請求,為每個請求都添加相同的公共參數。攔截器代碼如下:
/*
*
* 攔截器
*
* 向請求頭里添加公共參數
* Created by zhouwei on 16/11/10.
*/
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());*/
// 新的請求
Request.Builder requestBuilder = oldRequest.newBuilder();
requestBuilder.method(oldRequest.method(),
oldRequest.body());
//添加公共參數,添加到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;
}
}
}
以上就是添加公共參數的攔截器,在RetrofitServiceManager 類里面加入OkHttpClient 配置就好了。代碼如下:
// 添加公共參數攔截器
HttpCommonInterceptor commonInterceptor = new HttpCommonInterceptor.Builder()
.addHeaderParams("paltform","android")
.addHeaderParams("userToken","1234343434dfdfd3434")
.addHeaderParams("userId","123445")
.build();
builder.addInterceptor(commonInterceptor);
這樣每個請求都添加了公共參數。
** 好了,以上一個簡易的網絡請求庫就封裝得差不多了,完整代碼請戳Retrofit + RxJava +OkHttp 簡易封裝基本上能滿足項目中的網絡請求,由于項目中暫時沒有文件上傳下載的需求,這一塊還沒有添加,后面有時間會補充這一塊的東西。**
封裝的類放在http包下:
最后放幾張Demo示例的效果圖:(數據來自干貨集中營)
重點是看妹紙!!!(滑稽臉)
電影列表:(數據來自豆瓣)
**以上就是封裝的全部內容,還沒有用Retrofit 的,趕快用上它來改造你想網絡請求庫吧!!! **