OkHttp之Interceptors

简介

OkHttp实现了强大的拦截器机制,它的强大之处在于你可以用它来实现请求监控、请求重写以及重试。以下为一个简单的拦截器实现:

class LoggingInterceptor implements Interceptor {
    @Override
    public Response intercept(Interceptor.Chain chain) throws IOException {
        Request request = chain.request();

        long t1 = System.nanoTime();
        logger.info(String.format("Sending request %s on %s%n%s",
                request.url(), chain.connection(), request.headers()));

        Response response = chain.proceed(request);

        long t2 = System.nanoTime();
        logger.info(String.format("Received response for %s in %.1fms%n%s",
                response.request().url(), (t2 - t1) / 1e6d, response.headers()));

        return response;
    }
}

拦截通过链表来维护的,在实现Interceptor的过程中,必须要调用chain.proceed(request),okhttp是通过一个List的来维护的,调用的过程是按顺序来执行的,所以在这期间,如果你要添加一些处理逻辑,一定要把先后执行的这一逻辑考虑进去。
Interceptor又分为普通的应用application interceptor以及network interceptor,这两者是很不相同的。

这里写图片描述

Application Interceptor

  1. 不用关心redirects和reties的Response中间的过程
  2. 永远只会调用一次,不管Http Response是否是从缓存中直接取到的
  3. 可以监控原始的请求,不关心其它诸如If-None-Match的Header
  4. 允许不调用Chain.proceed()
  5. 允许重试多次调用Chain.proceed()。

Network Interceptors

  1. 可以操作redirects和reties的过程
  2. 不会调用缓存的Response
  3. 可以监控网络传输交互的数据
  4. 可以获取Connection携带的请求信息

Interceptor实现Cache

okhttp本身实现了一套Cache机制,但是并不是我们最终想要的。首先先说下okhttp的Cache实现,它实现了本地缓存(通过LruDiskCache)以及网络请求头缓存。每一个OkHttpClient对应了一个Cache,所以,如果你的应用里面有多个OkHttpClient,建议你公用一个Cache对象,否则,可能会出现Cache混乱,因为OkHttp为了提高效率,Cache内部通过一个链表来维护所有的Response缓存的Key,如果不使用一个Cache,则会出现一个OkHttpClient请求的缓存,使用另外一个OkHttpClient的时候获取不到,因为他们的key在不同的链表里存放。

接着来说下,我们理想的缓存,当没有网络的时候,缓存永远不失效;当有网络的时候如果如果请求失败则会返回缓存,如果请求成功,在会根据缓存设置的有效期,来决定是否访问缓存;同时可以调用刷新缓存,但是刷新缓存之后,新获取的数据依然会使用默认的缓存有效期。以下为通过Interceptor实现的缓存机制:

  • LocalCacheInterceptor来实现本地缓存的获取策略
/**
 * 此拦截器为拦截本地缓存,不会对缓存数据造成影响,只会影响到是否能获取到本地缓存数据
 */
public class LocalCacheInterceptor implements Interceptor {

    private int maxCacheSeconds;
    private Headers commonHeaders;

    public LocalCacheInterceptor(int maxCacheSeconds, Headers commonHeaders) {
        this.maxCacheSeconds = maxCacheSeconds;
        this.commonHeaders = commonHeaders;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        CacheControl cacheControl = new CacheControl.Builder().maxAge(maxCacheSeconds, TimeUnit.SECONDS)
                .maxStale(0, TimeUnit.SECONDS).build();
        Request request = chain.request();
        Request.Builder builder = request.newBuilder().cacheControl(cacheControl);
        if (commonHeaders != null) {
            for (String name : commonHeaders.names()) {
                builder.addHeader(name, commonHeaders.get(name));
            }
        }
        request = builder.build();
        if (AppUtils.isNetworkAvailable()) {
            try {
                Response response = chain.proceed(request);
                if (response.isSuccessful()) {
                    return response;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        //if request failed. always load in cache
        cacheControl = new CacheControl.Builder().maxAge(Integer.MAX_VALUE, TimeUnit.SECONDS).maxStale(Integer.MAX_VALUE, TimeUnit.SECONDS).build();
        request = builder.cacheControl(cacheControl).build();
        return chain.proceed(request);
    }
}
  • NetCacheInterceptor主要来实现Response的重写,来确保返回最新的数据设置缓存有效期
/**
 * 此拦截器为网络缓存器。只会改变本地缓存时间的大小,不会影响到是否能获取到本地缓存文件
 */
public class NetCacheInterceptor implements Interceptor {
    private int maxCacheSeconds;
    private Headers commonHeaders;

    public NetCacheInterceptor(int maxCacheSeconds, Headers commonHeaders) {
        this.maxCacheSeconds = maxCacheSeconds;
        this.commonHeaders = commonHeaders;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Request.Builder builder = request.newBuilder();
        if (commonHeaders != null) {
            for (String name : commonHeaders.names()) {
                builder.addHeader(name, commonHeaders.get(name));
            }
        }
        request = builder.build();
        Response originalResponse = chain.proceed(request);
        // rewrite the response headers to support cache.
        if (AppUtils.isNetworkAvailable()) {
            return originalResponse.newBuilder()
                    .header("Cache-Control", "public, max-age=" + EPocketHttpService.DEFAULT_CACHE_MAX_SECONDS)
                    .build();
        } else {
            return originalResponse.newBuilder()
                    .header("Cache-Control", "public, only-if-cached, max-stale=" + Integer.MAX_VALUE)
                    .build();
        }
    }
}
  • 调用OkhttpClient
private static final Cache OKHTTP_LOCAL_CACHE = new Cache(new File("cahce path"), 100 * 1024 *1024);
...

Headers commonHeaders = Headers.of("auth_token", "...");
OkHttpClient.Builder okHttpBuilder = new OkHttpClient.Builder()
    .addInterceptor(new LocalCacheInterceptor(maxCacheSeconds, commonHeaders))
    .addNetworkInterceptor(new NetCacheInterceptor(maxCacheSeconds, commonHeaders)).cache(OKHTTP_LOCAL_CACHE);

以上就实现了简单Cache策略。不过需要注意的一条是,OkHttp的Cache是根据URL以及请求参数来生成的,如若需要通过Header来实现,那么可能就需要修改okhttp的源码了。
总体而言,okhttp作为一个网络库来说,已经足够强大,它很纯粹,只是实现了网络请求及缓存,而不会掺杂其它注入返回处理的逻辑。同时,配合Retorfit来使用,简直是一个利器。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值