Retrofit 2和okhttp 3导致缓存错误 [英] Caching error with Retrofit 2 and okhttp 3

查看:91
本文介绍了Retrofit 2和okhttp 3导致缓存错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试从公司API缓存HTTP响应,但是该应用似乎无法访问缓存目录:

I'm trying to cache HTTP responses from my company API but it looks like the app cannot access the cache directory:

W/System.err:删除失败:ENOENT(无此文件或目录): /data/user/0/com.appname/cache/cache_file/journal.tmp

W/System.err: remove failed: ENOENT (No such file or directory) : /data/user/0/com.appname/cache/cache_file/journal.tmp

W/System.err: java.net.UnknownHostException:无法解析主机 "www.domain.com":没有与主机名关联的地址

W/System.err: java.net.UnknownHostException: Unable to resolve host "www.domain.com": No address associated with hostname

我已经遵循了教程.这是我设置Retrofit(2.1.0)的方法:

I have followed this tutorial. Here is how I setup Retrofit (2.1.0):

import lu.CompanyName.R;
import lu.CompanyName.interfaces.CompanyNameAPI;
import okhttp3.Cache;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

import static okhttp3.logging.HttpLoggingInterceptor.Level.HEADERS;


public class Injector {

    private static final String CACHE_CONTROL = "Cache-Control";

    private static Retrofit provideRetrofit (String baseUrl) {
        return new Retrofit.Builder()
                .baseUrl(baseUrl)
                .client(provideOkHttpClient())
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }

    private static OkHttpClient provideOkHttpClient () {
        return new OkHttpClient.Builder()
                .addInterceptor(provideHttpLoggingInterceptor())
                .addInterceptor(provideOfflineCacheInterceptor())
                .addNetworkInterceptor(provideCacheInterceptor())
                .cache(provideCache())
                .build();
    }

    private static Cache provideCache () {
        /*
        Cache cache = null;
        try
        {
            File dir = CompanyName.getInstance().getExternalCacheDir();

            if (dir == null)
                dir = CompanyName.getInstance().getCacheDir();

            if (dir == null)
                Log.e("provideCache", "dir is null");

            cache = new Cache(new File(dir, "http-cache"), 10 * 1024 * 1024); // 10 MB

            if (cache == null)
                Log.e("provideCache", "cache is null");

        }
        catch (Exception e)
        {
            Log.e("provideCache", "Could not create Cache!");
        }
        return cache;*/

        /*
        File httpCacheDirectory = new File(CompanyName.getInstance().getCacheDir(), "responses");
        httpCacheDirectory.getParentFile().mkdirs();
        int cacheSize = 10 * 1024 * 1024; // 10 MiB
        Cache cache = new Cache(httpCacheDirectory, cacheSize);
        try {
            cache.initialize();
            Iterator<String> iterator = cache.urls();
            Log.i("provideCache", "URLs in cacheHttpClient : ");
            while (iterator.hasNext()) {
                Log.i("provideCache", iterator.next());
            }
        } catch (IOException e) {
            e.printStackTrace();
            Log.i("provideCache", "CACHE NOT INIT");
        }
        return cache;*/

        return new Cache(new File(CompanyName.getInstance().getCacheDir(), "cache_file"), 20 * 1024 * 1024);
    }

    private static HttpLoggingInterceptor provideHttpLoggingInterceptor () {
        HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
        httpLoggingInterceptor.setLevel(HEADERS);
        return httpLoggingInterceptor;
    }

    private static Interceptor provideCacheInterceptor () {
        /*return new Interceptor() {
            @Override
            public Response intercept (Chain chain) throws IOException {
                Response response = chain.proceed(chain.request());

                // re-write response header to force use of cache
                CacheControl cacheControl = new CacheControl.Builder()
                        .maxAge(2, TimeUnit.HOURS)
                        .build();

                return response.newBuilder()
                        .header(CACHE_CONTROL, cacheControl.toString())
                        .build();
            }
        };*/
        return new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request originalRequest = chain.request();
                String cacheHeaderValue = CompanyName.getInstance().checkIfHasNetwork()
                        ? "public, max-age=2419200"
                        : "public, only-if-cached, max-stale=2419200" ;
                Request request = originalRequest.newBuilder().build();
                Response response = chain.proceed(request);
                return response.newBuilder()
                        .removeHeader("Pragma")
                        .removeHeader("Cache-Control")
                        .header("Cache-Control", cacheHeaderValue)
                        .build();
            }
        };
    }

    private static Interceptor provideOfflineCacheInterceptor () {
        /*return new Interceptor()
        {
            @Override
            public Response intercept (Chain chain) throws IOException
            {
                Request request = chain.request();

                if (!CompanyName.hasNetwork())
                {
                    CacheControl cacheControl = new CacheControl.Builder()
                            //.maxStale(7, TimeUnit.DAYS)
                            .build();

                    request = request.newBuilder()
                            .cacheControl(cacheControl)
                            .build();
                }

                return chain.proceed(request);
            }
        };*/
        return new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request originalRequest = chain.request();
                String cacheHeaderValue = CompanyName.getInstance().checkIfHasNetwork()
                        ? "public, max-age=2419200"
                        : "public, only-if-cached, max-stale=2419200" ;
                Request request = originalRequest.newBuilder().build();
                Response response = chain.proceed(request);
                return response.newBuilder()
                        .removeHeader("Pragma")
                        .removeHeader("Cache-Control")
                        .header("Cache-Control", cacheHeaderValue)
                        .build();
            }
        };
    }

    public static CompanyNameAPI provideCompanyNameAPI () {
        return provideRetrofit(CompanyName.getInstance().getString(R.string.base_url)).create(CompanyNameAPI.class);
    }
}

我尝试了一些通过Internet和stackoverflow找到的解决方案(仍在上面的代码中进行了注释),因为起初我认为这是一个"Cache-Control"重写问题.

I tried some solutions found over internet and stackoverflow (still in comment in the code above) because at first I thought it was a "Cache-Control" rewriting problem.

我还向清单添加了权限READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE,但它没有任何改变.

I have also added permission READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE to the manifest but it doesn't change anything.

我错过了什么吗? (我正在使用Android 6.0.1 API 23在Samsung上进行测试)

Did I miss something? (I am testing on a Samsung with Android 6.0.1 API 23)

推荐答案

关于第一个错误,让我们看看为OkHttpClient提供的缓存目录:

Regarding the first error, let's see the cache directory provided for OkHttpClient:

    private static OkHttpClient provideOkHttpClient () {
    return new OkHttpClient.Builder()
            .addInterceptor(provideHttpLoggingInterceptor())
            .addInterceptor(provideOfflineCacheInterceptor())
            .addNetworkInterceptor(provideCacheInterceptor())
            .cache(provideCache())
            .build();
}

您对许多OkHttpClient使用了相同的缓存目录,许多实例可能会相互踩踏,从而破坏了响应缓存. 要解决此问题,您可以只使用一次OkHttpClient,为其配置缓存,并在所有地方使用同一实例.您可以尝试如下操作:

You have used the same cache directory for many OkHttpClient, many instances may stomp on each other, corrupt the response cache. To fix this, you can use exactly once OkHttpClient, configure it with their cache, and use that same instance everywhere. You can try as below:

    private static Retrofit provideRetrofit (String baseUrl) {
    return new Retrofit.Builder()
            .baseUrl(baseUrl)
            .client(okHttpClient)
            .addConverterFactory(GsonConverterFactory.create())
            .build();
}

 private static OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .addInterceptor(provideHttpLoggingInterceptor())
            .addInterceptor(provideOfflineCacheInterceptor())
            .addNetworkInterceptor(provideCacheInterceptor())
            .cache(provideCache())
            .build();

有关OkHttpClient缓存的更多信息,您可以查看此链接.

For more info about cache with OkHttpClient, you can take a look at this link.

这篇关于Retrofit 2和okhttp 3导致缓存错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆