Retrofit 二次封裝實戰
前言
首先這篇文章是面向對Retrofit有了解的朋友,如果您對Retrofit并不了解,請自行查閱其用法,本文不會講解Retrofit的基礎用法。
寫這篇文章的目的很簡單:
1.為了讓自己回憶一下(代碼半年前就完成了),看是否有改進的地方。
2.如果能幫到有同樣需求的朋友,那是再好不過的。
3.如果大家對文章有不同意見之處,本人表示200%的歡迎提議。
這次封裝實現的功能:
1.抽離網絡層為module,實現即插即用。
2.統一網絡層入口,統一實現方法。
3.支持網絡請求緩存,自動添加刪除緩存,也可以手動cancel請求。
(本文是基于Retrofit2.1版本)高于此版本在使用思想方法上應該不會有任何問題。
正題開始
最終的版本應該是這樣的,請忽略download包,這是實現了斷點續傳和多線程下載的功能
一、初始化
網絡層的總入口,在這里進行Retrofit的初始化,配置。包括網絡請求的調用方法,當然,他一定是單列的。
很簡單,根據你app的需求,自行配置相關方法,相信用過Retrofit的朋友這部分不會陌生,所以這里不做解釋。
public KKNetWorkRequest init(Context context, String baseURL) {
this.mContext = context;
synchronized (KKNetWorkRequest.this) {
mOkHttpClient = new OkHttpClient.Builder()
.cache(new Cache(new File(context.getExternalCacheDir(), "http_cache"), 1024 * 1024 * 100))
.readTimeout(15, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.connectTimeout(10, TimeUnit.SECONDS)
.addInterceptor(new CommonInterceptor())
.addInterceptor(new LoggingInterceptor())
.cookieJar(new CookieManager())
.authenticator(new AuthenticatorManager())
.build();
mRetrofit = new Retrofit.Builder()
.addConverterFactory(FastJsonConverterFactory.create())
.baseUrl(baseURL)//主機地址
.client(mOkHttpClient)
.build();
}
return this;
}
第三行的synchronized關鍵字是為了保證初始化線程安全的。Retrofit是利用接口和注釋生成網絡請求的,這句話你一定不陌生:
ApiService mApiService = mRetrofit.create(ApiService.class);
因為我們現在將網絡層抽離為module,是由我們的app項目引用的。所以我們需要向外提供Retrofit的引用,交于上層來創建ApiService的實例。在代碼中提供get方法:
public Retrofit getRetrofit() { return mRetrofit; }
這樣我們的主項目就拿到了retrofit的引用,可以創建我們的網絡請求Call了。思路很清晰,在主項目中執行網絡層的初始化方法,并利用getRetrofit()方法,創建每個接口對應的Call對象。我們在主項目中,創建一個單例類,ApiManager,部分代碼如下:
public void init(Context context) {
KKNetWorkRequest.getInstance().init(context, AppConfig.APP_ROOT_URL);
mApiService = KKNetWorkRequest.getInstance().getRetrofit().create(ApiService.class);
}
public ApiService getApiService() {
return mApiService;
}
在主項目的Application的onCreate方法中初始化ApiManager:
ApiManager.getInstance().init(this);
初始化部分我們就搞定了,是不是很簡單。順便說一下:因為我們的KKNetWorkRequest是單列的,所以這里的Context一定要是Application的Context,以防內存回收造成的泄漏問題。
二、調用方法封裝
先上代碼:
public <T extends BaseResponseEntity> void asyncNetWork(final String TAG, final int requestCode, final Call<T> requestCall, final KKNetworkResponse<T> responseListener) {
1. Call<T> call;
if (requestCall.isExecuted()) {
call = requestCall.clone();
} else {
call = requestCall;
}
2. addCall(TAG, requestCode, call);
call.enqueue(new Callback<T>() {
@Override
public void onResponse(Call<T> call, Response<T> response) {
3. cancelCall(TAG, requestCode);
if (response.isSuccessful()) {
T result = response.body();
4. if (result == null) {
responseListener.onDataError(requestCode, "");
return;
}
5. result.requestCode = requestCode;
result.serverTip = response.message();
result.responseCode = response.code();
responseListener.onDataReady(result);
} else {
responseListener.onDataError(requestCode, NetErrCodeConfig.getErrString(mContext, response.code()));
}
}
@Override
public void onFailure(Call<T> call, Throwable t) {
cancelCall(TAG, requestCode);
responseListener.onDataError(requestCode, NetErrCodeConfig.getErrString(mContext, t));
}
});
}
async顧名思義是異步的意思,方法中四個參數逐一解釋一下:
TAG:開始說到設計思路要緩存網絡請求,你可以把TAG當做不同Activity的網絡請求區分字段。
requestCode:上面把TAG比作一個Activity,那requestCode就是同一個頁面的不同請求,為了區分并發回調判斷是哪個接口。那怎么做緩存呢,思路很簡單了,用Map!
private Map<String, Map<Integer, Call>> mRequestMap = new ConcurrentHashMap<>();
我們使用ConcurrentHashMap實現,為了保證數據的安全性。key即是TAG,value是另一個map集合,requestCode即是key,相信看到這里已經很清楚了。結合代碼看第2、3標注部分看分別是加入、清除緩存,至于怎么實現,就不貼代碼了,都是很基礎的東西,對map進行增和刪。
requestCall:即是retrofit為我們創建的call對象,對應ApiManager.getInstance().getApiService().xxxx();
KKNetworkResponse<T>:
KKNetworkResponse
可以看到這里使用了泛型,范圍是繼承BaseResponseEntity的類,解釋一下為什么這么做:
我們可能會遇到這樣的情況,一個頁面同時會并發多個網絡請求,每個接口我們都調用asyncNetWork()方法,如果不使用內部類傳遞KKNetworkResponse對象,我們可能會這么做:
public class WaitWeightIPresenter implements WaitWeightContract.IPresenter, KKNetworkResponse<PadUserWeighingGetUserDataResponseArgs> {
使用類實現KKNetworkResponse接口,然后重寫KKNetworkResponse中的方法,在asyncNetWork()中傳入this對象像這樣:
public void onDataReady(BaseResponseEntity response) {
ViewUtils.closeLoadingDialog();
switch (response.requestCode) {
case HttpConstants.HTTP_GET_MY_DIET_DETAIL:
if (mIMyDietView != null){
mIMyDietView.onGetedMyDietData((ResponseMyDietEntity) response);
}
break;
case HttpConstants.HTTP_POST_EVERYDAY_DIET_DETAIL:
if (mIMyDietView != null) {
mIMyDietView.onSubmitedMealData((ResponseUpLoadDietPlanEntity) response);
}
break;
case HttpConstants.HTTP_GET_EVERYMONTH_DIET_STATUS:
if (mIMyDietView != null) {
mIMyDietView.onGetedDietMonthData((DietCalendarEntity) response);
}
break;
}
}
我們知道Retrofit的response對象是在Api接口中定義好的,接口成功返回后會自動反序列化得到response bean。我們如何在多個response中區別是哪個接口返回的數據呢,你不可能要求后臺大哥哥們單獨為你在每個返回對象中加個接口參數吧,所以這就需要我們自己去做。代碼中的
switch (response.requestCode) {
requestCode是不是很眼熟?這不就是asyncNetWork()中的參數嗎?看asyncNetWork中的代碼,第5部分,我們自己賦值給response這樣回調中就可以通過requestCode來判斷是哪個接口了,你都可以在BaseResponse中存放什么字段?基于你們后臺大哥的習慣,放一些共有的屬性,比如responseCode,serverTip等等。
整體的邏輯大概就是這些了,最后我們來回顧一下到底做了些什么:
1.初始化Retrofit庫
2.在主項目中獲取retrofit對象,create ApiService
3.調用KKNetWorkRequest.getInstance().asyncNetWork()方法
4.對回調數據進行處理
網絡緩存的緩存和清楚都在asyncNetWork()中自動進行處理,當然你也可以在每個Activity的Destory方法中,手動清除當前TAG的所有
請求,或者是哪個TAG的哪個request請求,都隨你啦。
來自:http://www.jianshu.com/p/21fd4e468343