OkHttp的使用簡介及封裝,實現更簡潔的調用
最近將項目使用的網絡請求庫換成了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