Caching error with Retrofit 2 and okhttp 3

2019-07-10 17:00发布

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: remove failed: ENOENT (No such file or directory) : /data/user/0/com.appname/cache/cache_file/journal.tmp

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

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);
    }
}

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.

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

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

2条回答
相关推荐>>
2楼-- · 2019-07-10 17:26

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();
}

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();

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

查看更多
淡お忘
3楼-- · 2019-07-10 17:46

You might be missing the INTERNET permission in the manifest.

<uses-permission android:name="android.permission.INTERNET" />

That's a common reason for getting UnknownHostException errors.

Alternatively, make sure you can actually reach the site in question on that device by visiting the site in a browser.

查看更多
登录 后发表回答