java - 如何使用Guava缓存HTTP响应? (没有线程被阻塞)

标签 java retrofit guava rx-java2 okhttp

所以我尝试使用 Guava 缓存来缓存 HTTP 响应(使用 OkHtttp + 改造、rxjava 进行多线程)。目前看起来是这样的:

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("https://blablabla.com")
            .client(client)
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .addConverterFactory(GsonConverterFactory.create())
            .build();

    apiClient = retrofit.create(Api.class);

    CacheLoader<Integer, HttpResponse> cacheLoader = new CacheLoader<Integer, HttpResponse>() {
        @Override
        public Response load( Integer key ) throws InterruptedException, ExecutionException {
            return apiClient.getHttpResponse(key)
                    .subscribeOn(Schedulers.io())
                    .blockingFirst();
        }
    };

    responseCache = CacheBuilder.newBuilder()
            .concurrencyLevel(8)
            .maximumSize( 10 )
            .build(cacheLoader);

apiClient 返回 Observable,并在 CacheLoader 的 load 方法中订阅它。我还设置了 concurrencyLevel(8) 但它似乎不允许同时“加载”和“读取”。

我认为 blockingFirst() 调用可能会阻塞线程,因此我无法从缓存发出并发请求,即每当缓存加载新的 http 响应时,都无法读取缓存。

我不知道如何使其异步,非常感谢任何帮助:)

最佳答案

使用Guava,您仍然可以进行异步非阻塞调用并缓存HTTP响应,但您必须在收到HTTP响应后手动调用cache.put()。所以你可以像下面这样定义你的LoadingCache。

private LoadingCache<String, ServerResponse> cache =
    CacheBuilder.newBuilder()
        .maximumSize(50)
        .expireAfterWrite(24, TimeUnit.HOURS)
        .build(
            new CacheLoader<String, ServerResponse>() {
                public ServerResponse load(String id) {
                    return null; // or empty ServerResponse
                }
            });

在你的调用方法中:

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://blablabla.com")
        .client(client)
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .addConverterFactory(GsonConverterFactory.create())
        .build();

apiClient = retrofit.create(Api.class);

try {
    ServerResponse cachedResponse = cache.get(someUniqueKey);
    if (cachedResponse != null) {
        // return cachedResponse here
        return;
    }
    doNormalHttpRequest()
} catch (ExecutionException | CacheLoader.InvalidCacheLoadException e) {
    doNormalHttpRequest();
}

void doNormalHttpRequest() {
    apiClient.request(body).enqueue(
        new retrofit2.Callback<ServerResponse>() {
            @Override
            public void onResponse(Call<ServerResponse> call,
                Response<ServerResponse> response) {
                if (response.isSuccessful()) {
                    cache.put(someUniqueKey, response.body();
                }
            }

            @Override
            public void onFailure(Call<ServerResponse> call, Throwable {}
    });
}

作为一个额外的好处,这也可以缓存 POST 请求,因为 Retrofit 仅使用 OkHttp 的缓存实现来缓存 GET 请求。

关于java - 如何使用Guava缓存HTTP响应? (没有线程被阻塞),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51244793/

相关文章:

java - Google Collections ImmutableMap 迭代顺序

java - 为什么java.util.ArrayList中有私有(private)方法outOfBoundsMsg?

java - 未提供内部拆分列表!没有提供规则 URL?

Java 分页器

java - 今天哪些手机 SIM 实际上支持 javacard?

java - 使用 MVVM 和 Retrofit 调用我的 api 时没有得到我的响应?

android - Rx Android - 等待服务器响应

java - 将参数发送到订阅 CompositeDisposable 的方法 - Android?

java - Java中如何连接LinkedList?

java - 如何将 java Future<V> 转换为 guava ListenableFuture<V>