OkHttp的使用簡介及封裝,實現更簡潔的調用

jopen 8年前發布 | 59K 次閱讀 Android開發 移動開發

最近將項目使用的網絡請求庫換成了OkHttp,體驗感覺上升了好幾個檔次啊,-。-,之前項目是好幾年前的,封裝了原生的httpClient,沒有實現異步請求,每次都要自己開個線程,然后再實現退出的時候把線程關了,還要實現本地緩存,啊,聽起來好麻煩有木有,然后我終于受不了了,自己封裝了下OkHttp(。。。其實這個是好久前寫的代碼,一直沒機會實際運用,剛好可以當小白鼠)。。。。不廢話了

1.首先,OkHttp本身是有緩存這個東西的,只是如果你不去設置,是不起作用的

 client.setCache(new Cache(context.getCacheDir(),maxCacheSize));

 

設置緩存目錄和緩存大小,OkHttp內部是使用LRU來管理緩存的
2.當然,設置了緩存和目錄還是不夠的,http請求總該有個過期時間吧,緩存是由HTTP消息頭中的”Cache-control”來控制的,常見的取值有private、no-cache、max-age、must-revalidate等,默認為private。
下面是作用:

  • public   所有內容都將被緩存

  • private  內容只緩存到私有緩存中

  • no-cache 所有內容都不會被緩存

  • no-store 所有內容都不會被緩存到緩存或 Internet 臨時文件中

  • must-revalidation/proxy-revalidation 如果緩存的內容失效,請求必須發送到服務器/代理以進行重新驗證

  • max-age=xxx (xxx is numeric) 緩存的內容將在 xxx 秒后失效, 這個選項只在HTTP 1.1可用, 并如果和Last-Modified一起使用時, 優先級較高

一般來說我們用到的是no-cache和max-age比較多,既然我們要實現緩存,那么自然就要在我們的每一條的請求頭里面添加這個屬性,OkHttp提供了Interceptor 攔截器這個東西,做過web應該明白,就是在你一條http請求要發送之前,攔截下來,做一些處理然后再繼續發送,因此,我們就可以添加一個攔截器,在請求前把Cache-control:max-age=36000添加到請求頭里去。

Interceptor cacheInterceptor = new Interceptor() {
    @Override public Response intercept(Chain chain) throws IOException {
        Response originalResponse = chain.proceed(chain.request());
                        return originalResponse.newBuilder()
                .removeHeader("Pragma")//Pragma:no-cache。在HTTP/1.1協議中,它的含義和Cache-Control:no-cache相同。為了確保緩存生效
                .header("Cache-Control", String.format("max-age=%d", maxCacheAge))//添加緩存請求頭
                .build();
    }
};

3.嗯,差不多到這一步就已經快完成了,剩下的就是調用OkHttp的方法了。

Request.Builder requestBuilder = new Request.Builder().url(url).cacheControl(cacheControl);

一個OkHttp的請求大致是這樣子的,url是必須的,然后如果我們要實現緩存,cacheControl也是必須的,OkHttp提供了CacheControl這個類,里面FORCE_CACHE 和FORCE_NETWORK分別表示只從緩存獲取數據和只通過網絡請求獲取數據,有了前面的兩步設置,這時我們是可以通過設置FORCE_CACHE 來從緩存獲取數據而不通過網絡獲取服務器的數據的(前提是你本地要有緩存,也就是必須先通過網絡請求獲取到一次數據才能獲取到緩存),代碼沒什么,就不貼了。

嗯,我們的網絡請求實現本地緩存已經實現,當你網絡請求失敗的時候又不希望展示給用戶的是一片空白,那你就可以調用本地之前的緩存了!!你別告訴我這樣你就滿足了,這每次都要判斷是不是請求失敗了,要不要請求緩存,這想想都蛋疼啊!!!

所以我對OkHttp進行封裝,實現了只查詢緩存,網絡請求失敗自動查詢本地緩存等功能
支持4種不同的查詢方式

*ONLY_NETWORK  只查詢網絡數據

*ONLY_CACHED   只查詢本地緩存

*CACHED_ELSE_NETWORK  先查詢本地緩存,如果本地沒有,再查詢網絡數據

*NETWORK_ELSE_CACHED  先查詢網絡數據,如果沒有,再查詢本地緩存

支持get和post請求,默認查詢方式為NETWORK_ELSE_CACHED,可通過Builder來指定默認查詢方式

===================我是分隔符=================
先貼代碼

 //實現一個最基本的請求方法
private Call request(Request request, Callback callback){
    if(DEBUG){
        Log.d("OKHttp",request.toString());
    }
    Call call = client.newCall(request);
    call.enqueue(callback);
    return call;
}
//實現自己的回調,添加了onStart和onFinish方法
public abstract class Callback implements com.squareup.okhttp.Callback {
    public void onStart(){

    }    
    public void onFinish(){

    }
    public abstract void onFailure(Request request, IOException e);
    public abstract void onResponse(Response response) throws IOException;
}
//定義一個公用的方法,實現最基本的封裝,無論是post還是get都適用
private void request(String url, String method, RequestBody requestBody, final CacheControl cacheControl, Headers headers,Object tag ,final Callback callback){
    final Request.Builder requestBuilder = new Request.Builder().url(url).cacheControl(cacheControl);
    if(headers!=null){
        requestBuilder.headers(headers);
    }
    requestBuilder.method(method,requestBody);//如果是get請求,這里requestBody就應該傳個null
    requestBuilder.tag(tag==null?url:tag);//OkHttp的tag是對請求的標志,可以通過tag來獲取到請求和取消請求,這里如果你不傳,就將當前url設置為tag 
    final Request request = requestBuilder.build();
    request(request,new Callback() {
        //這里是回調
        @Override
        public void onStart() {
            if(callback!=null){
                    callback.onStart();
                }
        }  
        @Override            
        public void onFinish() {                
        if(callback!=null){
                callback.onFinish();
            }
        }            
        @Override            
        public void onFailure(Request request, IOException e) {                
        if(callback!=null){
                callback.onFailure(request,e);
                callback.onFinish();
            }
        }            
        @Override            
        public void onResponse(Response response) throws IOException {                
            if(response.code()==504){
                //OkHttp如果緩存請求不到是會報504的                    
                if(CacheControl.FORCE_CACHE == cacheControl){                        
                    if(callback!=null){
                        callback.onFailure(request,new IOException("cached not found"));
                        callback.onFinish();
                    }                        
                    return;
                }
            }                
            if(callback!=null){
                callback.onResponse(response);
                callback.onFinish();
            }
        }
    });
}
//實現只請求網絡和只請求緩存的方法 
public void requestFromNetwork(final String url,String method,RequestBody requestBody, Headers headers,Object tag,final Callback callback){
        request(url,method,requestBody,CacheControl.FORCE_NETWORK,headers,tag,callback);
}    
public void requestFromCached(String url,String method,RequestBody requestBody,Headers headers ,Object tag,final Callback callback){
      
   request(url,method,requestBody,CacheControl.FORCE_CACHE,headers,tag,callback);
}

重點來了!!
定義了4種請求類型
*ONLY_NETWORK  只查詢網絡數據

*ONLY_CACHED   只查詢本地緩存

*CACHED_ELSE_NETWORK  先查詢本地緩存,如果本地沒有,再查詢網絡數據

*NETWORK_ELSE_CACHED  先查詢網絡數據,如果沒有,再查詢本地緩存

前兩種都沒什么好說的,直接調用寫好的兩個方法requestFromNetwork和requestFromCached就行了
后面兩種:
1.CACHED_ELSE_NETWORK  先查詢本地緩存,如果本地沒有,再查詢網絡數據,我們就需要自己再傳一個Callback  c2回調了,當回調執行成功的時候,我們直接就調用方法的回調的onResponse就行,其余情況我們就需要查詢網絡的數據,到了這一步,說明本地沒有緩存了,所以直接調用requestFromNetwork就行

public void request(final String url,final CacheType cacheType,final String method,final RequestBody requestBody,final Headers headers,final Object tag,final Callback callback){
    if(callback!=null)
    callback.onStart();
    switch(cacheType){
        case ONLY_NETWORK://只查詢網絡數據
            requestFromNetwork(url,method,requestBody,headers,tag,callback);
            break;
        case ONLY_CACHED://只查詢本地緩存
            requestFromCached(url,method,requestBody,headers,tag,callback);
            break;
        case CACHED_ELSE_NETWORK:
            requestFromCached(url,method,requestBody,headers,tag,new Callback(){
                @Override 
                public void onStart(){
                    if(callback!=null){
                        callback.onStart();
                    }
                }
                @Override public void onFinish(){
                    if(callback!=null){
                        callback.onFinish();
                    }
                }
                @Override 
                public void onFailure(Request request,IOException e){
                    requestFromNetwork(url,method,requestBody,headers,tag,callback);
                }
                @Override public void onResponse(Response response)throws IOException{
                    if(response.code()==200){
                        //response.isSuccessful()OkHttp是有這個方法判斷請求是否成功的,但判斷的方法是根據狀態碼是否是20開頭(200,201,202,203等,具體區別就不在這里描述了,有興趣的百度)來判斷的,但只有200返回的數據才是我們想要的                            
                        if(callback!=null){
                            callback.onResponse(response);
                            callback.onFinish();
                        }
                    }else{
                        requestFromNetwork(url,method,requestBody,headers,tag,callback);
                    }
                }
             });
             break;
         case NETWORK_ELSE_CACHED:
            requestFromNetwork(url,method,requestBody,headers,tag,new Callback(){
                @Override public void onStart(){
                    if(callback!=null){
                        callback.onStart();
                    }
                }
                @Override public void onFinish(){
                    if(callback!=null){
                    callback.onFinish();
                    }
                 }
                @Override public void onFailure(Request request,IOException e){
                    requestFromCached(url,method,requestBody,headers,tag,callback);
                }
                @Override public void onResponse(Response response)throws IOException{
                    if(response.code()==200){
                        if(callback!=null){
                            callback.onResponse(response);
                            callback.onFinish();
                        }
                    } else{
                            requestFromCached(url,method,requestBody,headers,tag,callback);
                    }
                }
            });
            break;
    }
}

2.NETWORK_ELSE_CACHED  這個和CACHED_ELSE_NETWORK 實現原理是一樣的,就略過。

。。。。。寫得好累


 
!!!你以為這樣就完了?還要有json解析啊,這年頭,還用自帶的json解析有點low
(。。。。。之前我是用自帶的)


目前json解析的有fastJson、jackJson(這個包比較大,不怎么推薦)、Gson,我這里就用Gson了
首先,再添加一個回調的類

public abstract class JsonCallback<T> {
    public abstract void onFailure(Request request, Exception e);    
    public abstract void onResponse(T object) throws IOException;    public void onStart(){

    }    
    public void onFinish(){

    }
    //這個才是重點,獲取Json的類型,因為數據可能集合也可能是Object
    Type getType(){
        Type type = ((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0];        
        if(type instanceof Class){            
            return type;//如果是Object直接返回
        }else{            
            return new TypeToken<T>(){}.getType();//如果是集合,獲取集合的類型map或list
        }
    }
}

接下來就是對返回的數據進行解析了

//這里只貼部分關鍵代碼 
public void request(final String url, final CacheType cacheType, final String method, final RequestBody requestBody, final Headers headers,Object tag,final JsonCallback callback)

//在onResponse進行解析,具體看文章結尾源碼
@Override
public void onResponse(Response response) throws IOException {     
    if(response.isSuccessful() && callback!=null){
        String jsonString = response.body().string();;         
        if(!TextUtils.isEmpty(jsonString)){
            Object result = null;             
            try {
                 result =  gson.fromJson(jsonString,callback.getType());//直接調用Gson解析
                 callback.onResponse(result);
                 callback.onFinish();
            } catch (JsonSyntaxException e) {
                 callback.onFailure(null,new Exception("json string parse error :"+e.toString()));
                 callback.onFinish();
                 e.printStackTrace();
            }

         }else{
             callback.onFailure(null,new Exception("json string may be null"));
             callback.onFinish();
         }
    }
}

嗯,寫到這里終于完了!!!!下篇寫OkHttp的文件下載
源碼戳下面鏈接,幾個點start給個贊啊,寫了好多重載函數很累的!!!
compile ‘git.dzc:okhttputilslib:1.0.4’ 代碼上傳到了mavenCentral,Android Studio直接這樣就可以引用
https://github.com/duzechao/OKHttpUtils


來自: http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/0105/3831.html

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!