【Android】RxJava2+Retrofit2+OkHttp3的基礎、封裝和專案中的使用
前言:
近些年很火的Retrofit+RxJava+OkHttp網路請求框架,功能強大,結構合理,使用簡單方便。後面還會給大家發自己整理過的Retrofit和RxJava、RxAndroid和RxBus。希望大家點一下關注,讓我這個懶癌患者有動力繼續寫下去!
本篇分三個部分:基礎篇、封裝篇和自己專案使用篇,專案是自己公司的APP提取的,文章偏長可以分三部分一點點看,當初看了很多優秀的文章然後自己在整理寫在印象筆記中,可惜當初沒記下借鑑過的文章地址,十分抱歉。
最新更新: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 使用:
- 更改定義的介面,返回值不再是一個 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);//讀操作超時時間
// 新增公共引數攔截器
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(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;
}
/**
* 獲取對應的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);
@FormUrlEncoded
@POST("/x3/weather")
Call<String> getWeather(@Field("cityId") String cityId, @Field("key") String key);
}
}
建立一個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());*/
// 新的請求
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);
專案使用篇 —–>插入廣告!本專案來源於金融研習社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;
//構造方法私有
private HttpMethods() {
//手動建立一個OkHttpClient並設定超時時間
okHttpBuilder = new OkHttpClient.Builder();
/**
* 設定快取
*/
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;
// 有網路時 設定快取超時時間0個小時
response.newBuilder()
.header("Cache-Control", "public, max-age=" + maxAge)
.removeHeader(CACHE_NAME)// 清除頭資訊,因為伺服器如果不支援,會返回一些干擾資訊,不清除下面無法生效
.build();
} else {
// 無網路時,設定超時為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);
/**
* 設定頭資訊
*/
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);
// if (BuildConfig.DEBUG) {
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
Logger.d(message);
}
});
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
//設定 Debug Log 模式
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 = new Retrofit.Builder()
.client(okHttpBuilder.build())
.addConverterFactory(GsonConverterFactory.create())//json轉換成JavaBean
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl(BASE_URL)
.build();
httpService = retrofit.create(HttpService.class);
}
//在訪問HttpMethods時建立單例
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;
}
/**
* 設定訂閱 和 所在的執行緒環境
*/
public <T> void toSubscribe(Observable<T> o, DisposableObserver<T> s) {
o.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.retry(RETRY_COUNT)//請求失敗重連次數
.subscribe(s);
}
}
4.設定回撥:
呼叫者自己對請求資料進行處理 成功時 通過result是否等於1分別回撥onSuccees和onFault,預設處理了401錯誤轉登入。
public class OnSuccessAndFaultSub extends DisposableObserver<ResponseBody> implements ProgressCancelListener {
/**
* 是否需要顯示預設Loading
*/
private boolean showProgress = true;
private OnSuccessAndFaultListener mOnSuccessAndFaultListener;
private Context context;
private WaitProgressDialog progressDialog;
/**
* @param mOnSuccessAndFaultListener 成功回撥監聽
*/
public OnSuccessAndFaultSub(OnSuccessAndFaultListener mOnSuccessAndFaultListener) {
this.mOnSuccessAndFaultListener = mOnSuccessAndFaultListener;
}
/**
* @param mOnSuccessAndFaultListener 成功回撥監聽
* @param context 上下文
*/
public OnSuccessAndFaultSub(OnSuccessAndFaultListener mOnSuccessAndFaultListener, Context context) {
this.mOnSuccessAndFaultListener = mOnSuccessAndFaultListener;
this.context = context;
progressDialog = new WaitProgressDialog(context, this);
}
/**
* @param mOnSuccessAndFaultListener 成功回撥監聽
* @param context 上下文
* @param showProgress 是否需要顯示預設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();
}
}
/**
* 訂閱開始時呼叫
* 顯示ProgressDialog
*/
@Override
public void onStart() {
showProgressDialog();
}
/**
* 完成,隱藏ProgressDialog
*/
@Override
public void onComplete() {
dismissProgressDialog();
progressDialog = null;
}
/**
* 對錯誤進行統一處理
* 隱藏ProgressDialog
*/
@Override
public void onError(Throwable e) {
try {
if (e instanceof SocketTimeoutException) {//請求超時
} else if (e instanceof ConnectException) {//網路連線超時
mOnSuccessAndFaultListener.onFault("網路連線超時");
} else if (e instanceof SSLHandshakeException) {//安全證書異常
mOnSuccessAndFaultListener.onFault("安全證書異常");
} else if (e instanceof HttpException) {//請求的地址不存在
int code = ((HttpException) e).code();
if (code == 504) {
mOnSuccessAndFaultListener.onFault("網路異常,請檢查您的網路狀態");
} 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;
}
}
/**
* 當result等於1回撥給呼叫者,否則自動顯示錯誤資訊,若錯誤資訊為401跳轉登入頁面。
* ResponseBody body = response.body();//獲取響應體
* InputStream inputStream = body.byteStream();//獲取輸入流
* byte[] bytes = body.bytes();//獲取位元組陣列
* String str = body.string();//獲取字串資料
*/
@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();
}
}
}
*請求服務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();
}
@Override
public void onFault(String errorMsg) {//失敗的回撥
SnackBarManager.showShortMsg