OkHttp的使用简介及封装,实现更简洁的调用

jopen 8年前

最近将项目使用的网络请求库换成了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