okhttp3缓存设置及拦截器的使用

OkHttp是由Square发布的一个HTTP client,它支持高速缓存服务器响应.

缓存:

如果服务器支持缓存,请求返回的Response会带有这样的Header:Cache-Control, max-age=xxx,这种情况下我们只需要手动给okhttp设置缓存就可以让okhttp自动帮你缓存了。这里的max-age的值代表了缓存在你本地存放的时间。

OkHttpClient okHttpClient = new OkHttpClient();
OkHttpClient newClient = okHttpClient.newBuilder()
               .cache(new Cache(mContext.getCacheDir(), 10240*1024))
               .connectTimeout(20, TimeUnit.SECONDS)
               .readTimeout(20, TimeUnit.SECONDS)
               .build();
如果服务器不支持缓存就可能没有指定这个头部,这种情况下我们就需要使用Interceptor来重写Respose的头部信息,从而让okhttp支持缓存。

Interceptor interceptor = new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            Response response = chain.proceed(request);

            String cacheControl = request.cacheControl().toString();
            if (TextUtils.isEmpty(cacheControl)) {
                cacheControl = "public, max-age=60";
            }
            return response.newBuilder()
                    .header("Cache-Control", cacheControl)
                    .removeHeader("Pragma")
                    .build();
        }
    };
设置client:

//设置缓存路径
        File httpCacheDirectory = new File(mContext.getCacheDir(), "responses");
        //设置缓存 10M
        Cache cache = new Cache(httpCacheDirectory, 10 * 1024 * 1024);

        //创建OkHttpClient,并添加拦截器和缓存代码
        OkHttpClient client = new OkHttpClient.Builder()
                .addNetworkInterceptor(interceptor)
                .cache(cache)
		.build();
上面这种情况是不管有没有网络,都先读缓存,缓存时间为60s;

接下来还有一种情况,离线时,可以获取缓存,在线时获取最新数据。

主要是对拦截器的设置,这里要使用Interceptor。

Interceptor interceptor = new Interceptor() {
        @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
   	if (!isNetworkAvailable(TradeApplication.getContextObject())) 
            request = request.newBuilder()
                     .cacheControl(CacheControl.FORCE_CACHE)
                     .build();
            LogCat.i("no network");
         }
	Response response = chain.proceed(request);
	if (isNetworkAvailable(TradeApplication.getContextObject())) {
                int maxAge = 0 * 60; // 有网络时 设置缓存超时时间为0;
                
                response.newBuilder()
                        .header("Cache-Control", "public, max-age=" + maxAge)
                        .removeHeader("Pragma")// 清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效
                        .build();
         } else {
                int maxStale = 60 * 60 * 24; // 无网络时,设置超时为1天
                LogCat.i("has maxStale="+maxStale);
                response.newBuilder()
                        .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                        .removeHeader("Pragma")
                        .build();
            }
            return response; 
    };
上面缓存的设置主要是通过拦截器来实现的,但是在实际开发中,基本上都是使用一个okhttpclient去进行网络访问的,因此只有一个缓存文件访问入口,但是实际需求中,
往往都是有的接口需要进行缓存,有的接口需要获得最新数据。因此okhttp官方文档建议用CacheControl这个类来进行缓存策略的制定。
首先看一下这个类内部两个重要的静态实例。
public static final CacheControl FORCE_NETWORK = new Builder().noCache().build();

public static final CacheControl FORCE_CACHE = new Builder()
      .onlyIfCached()
      .maxStale(Integer.MAX_VALUE, TimeUnit.SECONDS)
      .build();
很明显,FORCE_NETWORK常量是用来使用网络请求。FORCE_CACHE只取本地的缓存。不同于拦截器设置缓存,CacheControl是针对Request的,所以它可以针对每个请求设置不同的缓存策略。
//缓存文件夹
File cacheFile = new File(getExternalCacheDir().toString(),"cache");
//缓存大小为10M
int cacheSize = 10 * 1024 * 1024;
//创建缓存对象
final Cache cache = new Cache(cacheFile,cacheSize);

 OkHttpClient client = new OkHttpClient.Builder()
                        .cache(cache)
                        .build();
                //设置缓存时间为60秒
                CacheControl cacheControl = new CacheControl.Builder()
                        .maxAge(60, TimeUnit.SECONDS)
                        .build();
                Request request = new Request.Builder()
                        .url(URL)
                        .cacheControl(cacheControl)
                        .build();
强制使用缓存:CacheControl.FORCE_CACHE这个常量。
public static final CacheControl FORCE_CACHE = new Builder()
      .onlyIfCached()
      .maxStale(Integer.MAX_VALUE, TimeUnit.SECONDS)
      .build();
Request request = new Request.Builder()
            .url(URL)
            .cacheControl(Cache.FORCE_CACHE)
            .build();
如果缓存不符合条件会返回504.这个时候我们要根据情况再进行编码,如缓存不行就再进行一次网络请求。
Response forceCacheResponse = client.newCall(request).execute();
     if (forceCacheResponse.code() != 504) {
       // 资源已经缓存了,可以直接使用
     } else {
       // 资源没有缓存,或者是缓存不符合条件了。
     } 
不使用缓存:CacheControl.FORCE_NETWORK这个常量。
public static final CacheControl FORCE_NETWORK = new Builder().noCache().build();
Request request = new Request.Builder()
            .url(URL)
            .cacheControl(Cache.FORCE_NETWORK)
            .build();
或者,我们将maxAge设置为0,也会直接访问网络。
Request request = new Request.Builder()
            .url(URL)
            .cacheControl(new CacheControl.Builder()
            .maxAge(0, TimeUnit.SECONDS))
            .build();


拦截器:
上面设置缓存的时候也使用到了拦截器,接下来简单介绍一下拦截器。
拦截器是一种能够监控,重写,重试调用的强大机制。我们可以使用拦截器添加我们的头信息,网络请求,网络缓存等。
拦截器的接口类:
public interface Interceptor {
  Response intercept(Chain chain) throws IOException;
  interface Chain {
    Request request();
    Response proceed(Request request) throws IOException;
    Connection connection();
  }
}
在注册拦截器时,可以注册成两类拦截器,分别为应用拦截器(Application Interceptors)和网络拦截器(Network Interceptors);
Application Interceptors 应用拦截器:
应用拦截器一般使用最多的是打印日志,查看请求信息及返回信息,下面简单的看下示例代码:
class LoggingInterceptor implements Interceptor {
       @Override
       public Response intercept(Interceptor.Chain chain) throws IOException {
           Request request = chain.request();
 
           long t1 = System.nanoTime();
           Log.d(TAG,String.format("Sending request %s on %s%n%s",
                   request.url(), chain.connection(), request.headers()));
 
           Response response = chain.proceed(request);
 
           long t2 = System.nanoTime();
           Log.d(TAG, String.format("Received response for %s in %.1fms%n%sconnection=%s",
                   response.request().url(), (t2 - t1) / 1e6d, response.headers(), chain.connection()));
 
           return response;
       }
   }
然后调用:
OkHttpClient client = new OkHttpClient.Builder()
               .addInterceptor(new LoggingInterceptor())
               .build();
Request request = new Request.Builder()
               .url("http://www.publicobject.com/helloworld.txt")
               .header("User-Agent", "OkHttp Example")
               .build();
       Response response = client.newCall(request).execute();
       response.body().close();
该拦截器只是在请求发出前和请求发出后分别打印出log日志,


拦截器中:Response response=chain.proceed(request)是每个拦截器实现的关键部分,是所有HTTP 工作发生的地方。
上面请求的URL地址是 http://www.publicobject.com/helloworld.txt 通过重定向被重定向到了这个地址http://www.publicobject.com/helloworld.txt。
okhttp将会自动跟随这个重定向,应用拦截器被调用一次,响应通过chain.proceed(request); 返回重定向的响应。
Network Interceptors 网络拦截器:

可以添加、删除或替换请求头信息,还可以改变的请求携带的实体。
比如请求时附上我们的token:

public final class TokenInterceptor implements Interceptor{
        private static final String USER_TOKEN = "Authorization";
        private final String token;
 
        public TokenInterceptor(String token) {
            this.token = token;
        }
        @Override
        public Response intercept(Chain chain) throws IOException {
            final Request originalRequest = chain.request();
            if(token == null || originalRequest.header("Authorization") != null){
                return chain.proceed(originalRequest);
            }
            Request request = originalRequest.newBuilder()
                    .header(USER_TOKEN,token)
                    .build();
            return chain.proceed(request);
        }
    }
上面我们判断token为不为空,有时候还没有请求到token或者登陆的时候不需要token,这个时候直接请求就好了。如果我们请求的header已经有token了,那么也不需要在添加token了,token的键一般是Authorization ,如果不是可以修改成你的值。

拦截器的选择
1)应用拦截器
不需要担心中间过程的响应,如重定向和重试.
总是只调用一次,即使HTTP响应是从缓存中获取.
观察应用程序的初衷. 不关心OkHttp注入的头信息如: If-None-Match.
允许短路而不调用 Chain.proceed(),即中止调用.
允许重试,使 Chain.proceed()调用多次.
2)网络拦截器
能够操作中间过程的响应,如重定向和重试.
当网络短路而返回缓存响应时不被调用.
只观察在网络上传输的数据.
携带请求来访问连接.

  • 9
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值