前面一篇文章講了一下Retrofit+ RxJava 請求網(wǎng)絡的一些基本用法,還沒有看過的可以去看一下Retrofit + RxJava + OkHttp 讓網(wǎng)絡請求變的簡單-基礎篇,正如標題所說的,Retrofit+RxJava 是讓我們的網(wǎng)絡請求變得簡單,代碼精簡量瓜。通過前一篇文章,我們感覺寫一個請求還是有點麻煩,作為程序員滓侍,我們的目標就是“偷懶”,絕不重復搬磚牲芋。因此我們還需要封裝一下撩笆,來簡化我們使用捺球,接下來進入正題。
一夕冲,創(chuàng)建一個統(tǒng)一生成接口實例的管理類RetrofitServiceManager
我們知道氮兵,每一個請求,都需要一個接口歹鱼,里面定義了請求方法和請求參數(shù)等等泣栈,而獲取接口實例需要通過一個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(){
// 創(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(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);
}
}
說明:創(chuàng)建了一個RetrofitServiceManager類,該類采用單例模式庭敦,在私有的構(gòu)造方法中疼进,生成了Retrofit 實例,并配置了OkHttpClient和一些公共配置秧廉。提供了一個create()方法伞广,生成接口實例,接收Class范型疼电,因此項目中所有的接口實例Service都可以用這個來生成嚼锄,代碼如下:
mMovieService = RetrofitServiceManager.getInstance().create(MovieService.class);
通過create()方法生成了一個MovieService
二,創(chuàng)建接口澜沟,通過第一步獲取實例
上面已經(jī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);
}
好了,有了接口我們就可以獲取到接口實例了mMovieService
三茫虽,創(chuàng)建一個業(yè)務Loader 刊苍,如XXXLoder,獲取Observable并處理相關(guān)業(yè)務
解釋一下為什么會出現(xiàn)Loader ,我看其他相關(guān)文章說濒析,每一個Api 都寫一個接口正什,我覺得這樣很麻煩,因此就把請求邏輯封裝在在一個業(yè)務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);
}
}
創(chuàng)建一個MovieLoader,構(gòu)造方法中生成了mMovieService,而Service 中可以定義和業(yè)務相關(guān)的多個api,比如:例子中的MovieService中,
可以定義和電影相關(guān)的多個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 中的調(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());
}
});
}
以上就完成請求過程的封裝透绩,現(xiàn)在添加一個新的請求翘骂,只需要添加一個業(yè)務Loader 類,然后通過Loader調(diào)用方法獲取結(jié)果就行了渺贤,是不是方便了很多雏胃?但是在實際項目中這樣是不夠的,還能做進一步簡化志鞍。
五,統(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ù)服務端定義的JSON 結(jié)構(gòu)創(chuàng)建一個BaseResponse 類呜师,代碼如下:
/*
*
*
* 網(wǎng)絡請求結(jié)果 基類
* 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ù)后,我們需要剝離出data 返回給上層調(diào)用者贾节,創(chuàng)建一個PayLoad 類汁汗,代碼如下:
/*
*
*
* 剝離 最終數(shù)據(jù)
* Created by zhouwei on 16/11/10.
*/
public class PayLoad<T> implements Func1<BaseResponse<T>,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 中拿到這個異常疚脐,然后判斷錯誤碼亿柑,進行異常處理。在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){
//錯誤處理
}
}
}
以上就可以對應錯誤碼處理相應的錯誤了亮曹。
六橄杨,添加公共參數(shù)
在實際項目中秘症,每個接口都有一些基本的相同的參數(shù),我們稱之為公共參數(shù)式矫,比如:userId乡摹、userToken、userName,deviceId等等采转,我們不必要聪廉,每個接口都去寫,這樣就太麻煩了故慈,因此我們可以寫一個攔截器板熊,在攔截器里面攔截請求,為每個請求都添加相同的公共參數(shù)察绷。攔截器代碼如下:
/*
*
* 攔截器
*
* 向請求頭里添加公共參數(shù)
* 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();
// 添加新的參數(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);
這樣每個請求都添加了公共參數(shù)容劳。
** 好了,以上一個簡易的網(wǎng)絡請求庫就封裝得差不多了闸度,完整代碼請戳Retrofit + RxJava +OkHttp 簡易封裝基本上能滿足項目中的網(wǎng)絡請求竭贩,由于項目中暫時沒有文件上傳下載的需求,這一塊還沒有添加莺禁,后面有時間會補充這一塊的東西留量。**
封裝的類放在http包下:
最后放幾張Demo示例的效果圖:(數(shù)據(jù)來自干貨集中營)
重點是看妹紙!S炊Bハā(滑稽臉)
電影列表:(數(shù)據(jù)來自豆瓣)
**以上就是封裝的全部內(nèi)容,還沒有用Retrofit 的柒傻,趕快用上它來改造你想網(wǎng)絡請求庫吧P⒑铡!红符! **