android:Retrofit+RxJava的優雅封裝

image.png
前言
Retrofit
和 RxJava
已經出來很久了,很多前輩寫了很多不錯的文章,在此不得不感謝這些前輩無私奉獻的開源精神,能讓我們站在巨人的肩膀上望得更遠。
Retrofit:Retrofit是Square 公司開發的一款針對Android 網路請求的框架。
RxJava:RxJava 是一個鏈式呼叫的非同步框架。
RxJava在 GitHub 主頁上的自我介紹是 "a library for composing asynchronous and event-based programs using observable sequences for the Java VM"(一個在 Java VM 上使用可觀測的序列來組成非同步的、基於事件的程式的庫)。這就是 RxJava ,概括得非常精準。
一言以闢之,就是讓非同步操作變得非常簡單。
各自職責:Retrofit 負責請求的資料和請求的結果,使用介面的方式呈現,OkHttp 負責請求的過程,RxJava 負責非同步,各種執行緒之間的切換。
RxJava
的使用參考--> ofollow,noindex">給 Android 開發者的 RxJava 詳解
Retrofit
的使用參考--> Android Retrofit 2.0使用
本文內容是基於 Retrofit + RxJava
做的一些優雅的封裝。參考了很多文章加入了一些自己的理解,請多指教。
先引入依賴
build.gradle
// Okhttp庫 implementation 'com.squareup.okhttp3:okhttp:3.11.0' implementation 'com.squareup.okhttp3:logging-interceptor:3.8.1' // Retrofit庫 implementation 'com.squareup.retrofit2:retrofit:2.1.0' implementation 'com.squareup.retrofit2:adapter-rxjava2:2.2.0' //RxJava implementation 'io.reactivex.rxjava2:rxjava:2.0.1' implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
在此之前,
先來聊聊簡單使用,(優雅封裝見下文)
1、先寫一個介面Service
interface APIService { @GET("user/login" ) Call<UserInfo> login(@Query("username") String username,@Query("password")String password); }
2、獲取Call執行網路請求
Retrofit retrofit = new Retrofit.Builder() .addConverterFactory(GsonConverterFactory.create()) .baseUrl(BASE_URL) .build(); APIService service = retrofit.create(APIService.class); Call<UserInfo> call = service.login("張曉宇", "is sb"); call.enqueue(new Callback<UserInfo>() { @Override public void onResponse(Call<UserInfo> call, Response<UserInfo> response) { //請求成功 } @Override public void onFailure(Call<UserInfo> call, Throwable t) { //請求失敗 } });
以上就是Retrofit的簡單使用。但是實際專案中,我們肯定不會這麼寫,這樣寫完全不符合我們寫程式碼的優雅性和簡潔性。所以,我們要對它進行優雅的封裝。
Retrofit+RxJava優雅的封裝
1、請求實體類與返回實體類的封裝
BaseRequestEntity.java
public class BaseRequestEntity <T>{ private HeaderEntity header; private T data; public HeaderEntity getHeader() { return header; } public void setHeader(HeaderEntity header) { this.header = header; } public T getData() { return data; } public void setData(T data) { this.data = data; } }
BaseResponseEntity.java
public class BaseResponseEntity<T> { private int errorCode; private String errorMsg; private T data; public int getErrorCode() { return errorCode; } public void setErrorCode(int errorCode) { this.errorCode = errorCode; } public String getErrorMsg() { return errorMsg; } public void setErrorMsg(String errorMsg) { this.errorMsg = errorMsg; } public T getData() { return data; } public void setData(T data) { this.data = data; } }
2、請求介面服務類封裝
ObservableAPI.java
/** * 介面服務 **/ public interface ObservableAPI { /** * 登入 */ @POST(URL.URL_LOGIN) Observable<BaseResponseEntity<LoginResponseEntity>> login(@Body BaseRequestEntity<LoginRequestEntity> requestEntity); }
3、ObservableManager封裝(請求介面傳參封裝)
ObservableManager.java
public class ObservableManager { private static class SingletonHolder { static final ObservableManager INSTANCE = new ObservableManager(); } public static ObservableManager getInstance() { return ObservableManager.SingletonHolder.INSTANCE; } /** * login */ public BaseRequestEntity<LoginRequestEntityl> getLoginRequestEntity() { BaseRequestEntity<LoginRequestEntity> requestModel = new BaseRequestEntity<>(); requestModel.setHeader(HeaderUtils.setHeaderModel()); LoginRequestEntity loginRequestModel = new LoginRequestEntity(); requestModel.setData(loginRequestModel); return requestModel; } }
4、Retrofit封裝
RetrofitHandler.java
public class RetrofitHandler { private static Retrofit mRetrofit; private static OkHttpClient mOkHttpClient; private static RetrofitHandler mRetrofitHandler; private static ObservableAPI mObservableAPI; private RetrofitHandler() { initRetrofit(); } public static synchronized RetrofitHandler getInstance() { if (mRetrofitHandler == null) { synchronized (RetrofitHandler.class) { if (mRetrofitHandler == null) { mRetrofitHandler = new RetrofitHandler(); } } } return mRetrofitHandler; } /** * 獲取 Retrofit */ private void initRetrofit() { initOkHttpClient(); mRetrofit = new Retrofit.Builder() .baseUrl(URL.BASE_URL) //JSON轉換器,使用Gson來轉換 .addConverterFactory(GsonConverterFactory.create()) //RxJava介面卡 .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .client(mOkHttpClient) .build(); mObservableAPI = mRetrofit.create(ObservableAPI.class); } /** * 單例模式獲取 OkHttpClient */ private static void initOkHttpClient() { if (mOkHttpClient == null) { synchronized (RetrofitHandler.class) { if (mOkHttpClient == null) { // 指定快取路徑,快取大小100Mb Cache cache = new Cache(new File(HttpConfig.DIR_CACHE_FILE, "HttpCache"), 1024 * 1024 * 100); mOkHttpClient = new OkHttpClient.Builder() //設定連線超時時間 .connectTimeout(HttpConfig.HTTP_TIME_OUT_TIME, TimeUnit.SECONDS) //設定讀取超時時間 .readTimeout(HttpConfig.HTTP_TIME_OUT_TIME, TimeUnit.SECONDS) //設定寫入超時時間 .writeTimeout(HttpConfig.HTTP_TIME_OUT_TIME, TimeUnit.SECONDS) //預設重試一次 .retryOnConnectionFailure(true) //新增請求頭攔截器 .addInterceptor(InterceptorHelper.getHeaderInterceptor()) //新增日誌攔截器 .addInterceptor(InterceptorHelper.getLogInterceptor()) //新增快取攔截器 .addInterceptor(InterceptorHelper.getCacheInterceptor()) //新增重試攔截器 .addInterceptor(InterceptorHelper.getRetryInterceptor()) // 信任Https,忽略Https證書驗證 // https認證,如果要使用https且為自定義證書 可以去掉這兩行註釋,並自行配製證書。 .sslSocketFactory(SSLSocketTrust.getSSLSocketFactory()) .hostnameVerifier(SSLSocketTrust.getHostnameVerifier()) //快取 .cache(cache) .build(); } } } } /** * 對外提供呼叫 API的介面 * * @return */ public ObservableAPI getAPIService() { return mObservableAPI; } }
5、攔截器封裝(請求頭攔截器、日誌攔截器、快取攔截器、重試攔截器等)
InterceptorHelper.java
/** * @author wy * @description 攔截器工具類 */ public class InterceptorHelper { public static String TAG = "Interceptor"; /** * 日誌攔截器 */ public static HttpLoggingInterceptor getLogInterceptor() { return new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() { @Override public void log(String message) { Log.w(TAG, "LogInterceptor---------: " + message); } }).setLevel(HttpLoggingInterceptor.Level.BODY);//設定列印資料的級別 } /** * 快取攔截器 * * @return */ public static Interceptor getCacheInterceptor() { return new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); //CONTEXT不能為空 if (!NetworkUtils.isConnected(PalApplication.getInstance().getApplicationContext())) { int maxStale = 4 * 7 * 24 * 60; // 離線時快取儲存4周,單位:秒 CacheControl tempCacheControl = new CacheControl.Builder() .onlyIfCached() .maxStale(maxStale, TimeUnit.SECONDS) .build(); request = request.newBuilder() .cacheControl(tempCacheControl) .build(); } return chain.proceed(request); } }; } /** * 重試攔截器 * * @return */ public static Interceptor getRetryInterceptor() { return new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { int maxRetry = 10;//最大重試次數 int retryNum = 5;//假如設定為3次重試的話,則最大可能請求4次(預設1次+3次重試) Request request = chain.request(); Response response = chain.proceed(request); while (!response.isSuccessful() && retryNum < maxRetry) { retryNum++; response = chain.proceed(request); } return response; } }; } /** * 請求頭攔截器 * * @return */ public static Interceptor getHeaderInterceptor() { return new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { //在這裡你可以做一些想做的事,比如token失效時,重新獲取token //或者新增header等等 Request originalRequest = chain.request(); if (null == originalRequest.body()) { return chain.proceed(originalRequest); } Request compressedRequest = originalRequest.newBuilder() .header("Content-Encoding", "gzip") .header("User-Agent", "OkHttp Headers.java") .addHeader("Accept", "application/json; q=0.5") .addHeader("Accept", "application/vnd.github.v3+json") .addHeader("Accept-Encoding", "identity") //.addHeader(Constants.WEB_TOKEN, webi_token) .build(); Response proceed = chain.proceed(compressedRequest); return proceed; } }; } }
6、BaseObserver封裝(請求失敗、網路異常、介面錯誤、載入視窗等處理)
BaseObserver.java
public abstract class BaseObserver<T> implements Observer<BaseResponseEntity<T>> { protected Context mContext; public BaseObserver() { } public BaseObserver(Context cxt) { this.mContext = cxt; } @Override public void onSubscribe(Disposable d) { onRequestStart(); } @Override public void onNext(BaseResponseEntity<T> tBaseEntity) { onRequestEnd(); String message_common = "Oops, something went wrong. Please try again."; if (tBaseEntity.getErrorCode()==0) {//成功 try { onSuccess(tBaseEntity); } catch (Exception e) { e.printStackTrace(); } } else { try { if (!CommonUtils.isEmptyOrNull(tBaseEntity.getErrorMsg())) { onFailure(tBaseEntity.getErrorMsg()); } else { onFailure(message_common); } } catch (Exception e) { e.printStackTrace(); } } } @Override public void onError(Throwable e) { onRequestEnd(); String message_common = "Oops, something went wrong. Please try again."; String msg_timeout = "Oops, connection timeout, please try again later"; try { if (e instanceof ConnectException || e instanceof TimeoutException || e instanceof NetworkErrorException || e instanceof UnknownHostException) { onFailure(msg_timeout); } else { onFailure(message_common); } } catch (Exception e1) { e1.printStackTrace(); } } @Override public void onComplete() { } /** * 返回成功 * * @param tBaseEntity */ protected abstract void onSuccess(BaseResponseEntity<T> tBaseEntity); /** * 返回失敗 * * @param errorMessage */ protected abstract void onFailure(String errorMessage); /** * 請求開始 */ protected void onRequestStart() { showProgressDialog(); } /** * 請求結束 */ protected void onRequestEnd() { closeProgressDialog(); } /** * 載入彈窗 */ public void showProgressDialog() { } /** * 關閉載入彈窗 */ public void closeProgressDialog() { } }
7、排程類封裝
RxTransformerHelper.java
/** * 排程類 */ public class RxTransformerHelper { public static <T> ObservableTransformer<T, T> observableIO2Main(final Context context) { return new ObservableTransformer<T, T>() { @Override public ObservableSource<T> apply(Observable<T> upstream) { return upstream.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()); } }; } }
8、使用方式
private void login() { RetrofitHandler.getInstance().getAPIService() .login(ObservableManager.getInstance().getLoginRequestEntity()) .compose(RxTransformerHelper.<BaseResponseEntity<LoginResponseEntity>>observableIO2Main(this)) .subscribe(new BaseObserver<LoginResponseEntity>() { @Override protected void onSuccess(BaseResponseEntity<LoginResponseEntity> responseEntity) { showSuccessDialog("Success"); } @Override protected void onFailure(String errorMessage) { showErrorDialog(errorMessage); } }); }
9、到這裡,Retrofit+RxJava基本算是優雅的封裝完成了,其實,如果追求更完美的話,還可以進行二次封裝,將第八步的請求封裝的更為簡潔。這個,就看各人喜好而定了,本文不再贅述。