Retrofit2的再封裝實(shí)戰(zhàn)—同步與異步請求

前言

首先這篇文章是面向?qū)etrofit有了解的朋友,如果您對Retrofit并不了解,請自行查閱其用法,本文不會(huì)講解Retrofit的基礎(chǔ)用法。
寫這篇文章的目的很簡單:
1.為了讓自己回憶一下(代碼半年前就完成了),看是否有改進(jìn)的地方。
2.如果能幫到有同樣需求的朋友,那是再好不過的。
3.如果大家對文章有不同意見之處,本人表示200%的歡迎提議。

這次封裝實(shí)現(xiàn)的功能:
1.抽離網(wǎng)絡(luò)層為module,實(shí)現(xiàn)即插即用。
2.統(tǒng)一網(wǎng)絡(luò)層入口,統(tǒng)一實(shí)現(xiàn)方法。
3.支持網(wǎng)絡(luò)請求緩存,自動(dòng)添加刪除緩存,也可以手動(dòng)cancel請求。

本文是基于Retrofit2.1版本,高于此版本在使用思想方法上應(yīng)該不會(huì)有任何問題。

正題

最終的版本應(yīng)該是這樣的,請忽略download包,這是實(shí)現(xiàn)了斷點(diǎn)續(xù)傳和多線程下載的功能

一、初始化

網(wǎng)絡(luò)層的總?cè)肟?,在這里進(jìn)行Retrofit的初始化,配置。包括網(wǎng)絡(luò)請求的調(diào)用方法,當(dāng)然,他一定是單列的。
很簡單,根據(jù)你app的需求,自行配置相關(guān)方法,相信用過Retrofit的朋友這部分不會(huì)陌生,所以這里不做解釋。

public NetWorkRequest init(Context context, String baseURL) {    
this.mContext = context;    
synchronized (NetWorkRequest.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)//主機(jī)地址                
.client(mOkHttpClient)                
.build();   
 }   
 return this;
}

第三行的synchronized關(guān)鍵字是為了保證初始化線程安全的。Retrofit是利用接口和注釋生成網(wǎng)絡(luò)請求的,這句話你一定不陌生:
ApiService mApiService = mRetrofit.create(ApiService.class);
因?yàn)槲覀儸F(xiàn)在將網(wǎng)絡(luò)層抽離為module,是由我們的app項(xiàng)目引用的。所以我們需要在代碼中提供create方法:
public <T> T create(Class<T> tClass) { return mRetrofit.create(tClass); }
這樣我們的主項(xiàng)目就可以拿到我們的APIService來創(chuàng)建我們的網(wǎng)絡(luò)請求Call<T>了。思路很清晰,在主項(xiàng)目中執(zhí)行網(wǎng)絡(luò)層的初始化方法,并利用create方法獲取Api實(shí)例,創(chuàng)建接口對應(yīng)的Call對象。我們在主項(xiàng)目中,創(chuàng)建一個(gè)單例類,ApiManager,部分代碼如下:

public void init(Context context) {  
  NetWorkRequest.getInstance().init(context, AppConfig.APP_ROOT_URL);    
  mApiService = NetWorkRequest.getInstance().create(ApiService.class);
}
public ApiService getApiService() {   
  return mApiService;
}

在主項(xiàng)目的Application的onCreate方法中初始化ApiManager:
ApiManager.getInstance().init(this);
初始化部分我們就搞定了,是不是很簡單。順便說一下:因?yàn)槲覀兊腘etWorkRequest是單列的,所以這里的Context一定要是Application的Context,以防內(nèi)存回收造成的泄漏問題。

二、調(diào)用方法封裝

先上代碼:

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顧名思義是異步的意思,方法中四個(gè)參數(shù)逐一解釋一下:
TAG:開始說到設(shè)計(jì)思路要緩存網(wǎng)絡(luò)請求,你可以把TAG當(dāng)做不同Activity的網(wǎng)絡(luò)請求區(qū)分字段。

requestCode:上面把TAG比作一個(gè)Activity,那requestCode就是同一個(gè)頁面的不同請求,為了區(qū)分并發(fā)回調(diào)判斷是哪個(gè)接口。那怎么做緩存呢,思路很簡單了,用Map!
private Map<String, Map<Integer, Call>> mRequestMap = new ConcurrentHashMap<>();
我們使用ConcurrentHashMap實(shí)現(xiàn),為了保證數(shù)據(jù)的安全性。key即是TAG,value是另一個(gè)map集合,requestCode即是key,相信看到這里已經(jīng)很清楚了。結(jié)合代碼看第2、3標(biāo)注部分看分別是加入、清除緩存,至于怎么實(shí)現(xiàn),就不貼代碼了,都是很基礎(chǔ)的東西,對map進(jìn)行增和刪。

requestCall:即是retrofit為我們創(chuàng)建的call對象,對應(yīng)ApiManager.getInstance().getApiService().xxxx();

KKNetworkResponse<T>:

KKNetworkResponse
可以看到這里使用了泛型,范圍是繼承BaseResponseEntity的類,解釋一下為什么這么做:
我們可能會(huì)遇到這樣的情況,一個(gè)頁面同時(shí)會(huì)并發(fā)多個(gè)網(wǎng)絡(luò)請求,每個(gè)接口我們都調(diào)用asyncNetWork()方法,如果不使用內(nèi)部類傳遞KKNetworkResponse對象,我們可能會(huì)這么做:
public class WaitWeightIPresenter implements WaitWeightContract.IPresenter, NetworkResponse<PadUserWeighingGetUserDataResponseArgs> {
使用類實(shí)現(xiàn)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接口中定義好的,接口成功返回后會(huì)自動(dòng)反序列化得到response bean。我們?nèi)绾卧诙鄠€(gè)response中區(qū)別是哪個(gè)接口返回的數(shù)據(jù)呢,你不可能要求后臺大哥哥們單獨(dú)為你在每個(gè)返回對象中加個(gè)接口參數(shù)吧,所以這就需要我們自己去做。代碼中的
switch (response.requestCode) {
requestCode是不是很眼熟?這不就是asyncNetWork()中的參數(shù)嗎?看asyncNetWork中的代碼,第5部分,我們自己賦值給response這樣回調(diào)中就可以通過requestCode來判斷是哪個(gè)接口了,回調(diào)實(shí)體BaseResponseEntity直接向下強(qiáng)轉(zhuǎn)T類型,注意這里的向下轉(zhuǎn)型是安全的,response此時(shí)已經(jīng)是子類的引用。
至于你都可以在BaseResponse中存放什么字段?基于你們項(xiàng)目的規(guī)范,放一些共有的屬性,比如responseCode,serverTip等等。(建議這里跟后臺好好商量,合理的公有屬性設(shè)定可以減少客戶端很多的工作)
整體的邏輯大概就是這些了,最后我們來回顧一下到底做了些什么:
1.初始化Retrofit庫
2.在主項(xiàng)目中獲取retrofit對象,create ApiService
3.調(diào)用NetWorkRequest.getInstance().asyncNetWork()方法
4.對回調(diào)數(shù)據(jù)進(jìn)行處理
網(wǎng)絡(luò)緩存的緩存和清除都在asyncNetWork()中自動(dòng)進(jìn)行處理,當(dāng)然你也可以在每個(gè)Activity的Destory方法中,手動(dòng)清除當(dāng)前TAG的所有請求,或者是哪個(gè)TAG的哪個(gè)request請求,都隨你啦(當(dāng)然這里建議統(tǒng)一在基類里處理)。

最后,如有疑問,請留言,我會(huì)認(rèn)真回復(fù),第一篇文章,多提意見。如果大家感興趣,我會(huì)分享mvp設(shè)計(jì)模式的個(gè)人經(jīng)驗(yàn)和基于Retrofit的斷點(diǎn)續(xù)傳和多線程下載,稍后會(huì)提交代碼到git,后會(huì)有期。
代碼傳送門

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容