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