In my Application onCreate, I'm creating a 10MB cache:
try
{
File httpCacheDir = new File(getApplicationContext().getCacheDir(), Constants.AppName);
long httpCacheSize = 10 * 1024 * 1024; // 10 MiB
HttpResponseCache.install(httpCacheDir, httpCacheSize);
}
catch (IOException ex)
{
Log.i(Constants.AppName, "HTTP response cache installation failed: " + ex);
}
And my call to a resource:
OkHttpClient client = new OkHttpClient();
client.setResponseCache(HttpResponseCache.getInstalled());
HttpURLConnection connection = client.open(url);
connection.addRequestProperty("Cache-Control", "max-age=60");
InputStream inputStream = connection.getInputStream();
I'll initialize this call twice, within 10 seconds of one another, and the OkHttp-Response-Source header is always NETWORK 200.
for (Map.Entry<String, List<String>> k : connection.getHeaderFields().entrySet())
{
for (String v : k.getValue())
{
Log.d(Constants.AppName, k.getKey() + ": " + v);
}
}
What am I missing here?
OkHttp implement the HTTP 1.1 RFC for HTTP cache. That means, for any URL your are trying to reach, the response needs to return at least the Cache-Control header:
The basic cache mechanisms in HTTP/1.1 (server-specified expiration
times and validators) are implicit directives to caches. In some
cases, a server or client might need to provide explicit directives to
the HTTP caches. We use the Cache-Control header for this purpose.
The Cache-Control header allows a client or server to transmit a
variety of directives in either requests or responses. These
directives typically override the default caching algorithms. As a
general rule, if there is any apparent conflict between header values,
the most restrictive interpretation is applied (that is, the one that
is most likely to preserve semantic transparency). However,
in some cases, cache-control directives are explicitly specified as
weakening the approximation of semantic transparency (for example,
"max-stale" or "public").
The cache-control directives are described in detail in section 14.9.
Related
I am using Picasso for image loading. Picasso doesn't have a disk cache. OkHttp maintains an HTTP cache that is controlled by HTTP cache headers. I want to set an expiry for disk cached images so added cache-control headers in HTTP response cache-control: public, max-age=7200 but it is not respecting cache headers. The current behavior is the default HTTPResponseCache which honors RFC 7234.
Is there anything we are missing?
You can rewrite cache headers in the response with a network interceptor. Here's an example from the OkHttp interceptors doc to get you started:
/** Dangerous interceptor that rewrites the server's cache-control header. */
private static final Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
#Override public Response intercept(Interceptor.Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
return originalResponse.newBuilder()
.header("Cache-Control", "max-age=60")
.build();
}
};
Note that you might need to remove headers from the server's response to get your desired caching behavior.
Note also that it's strictly better to fix the server to do what you want; that way it'll work correctly on iOS and on the web.
I'm building an Android application which uses Volley to load images from the web.
I'm using an LruCache with Volley to cache bitmaps in memory, and this is fine. In addition, I'd like Volley to leverage the built in support for http disk based caching using HttpResponseCache.
I implemented the example in the given link, but I noticed that nothing is being cached in the HttpResponseCache (I checked by sampling the HitCount field in the cache object).
After sniffing around in the Volley source code, I found that Volley manually removes the caching flag when opening a URLConnection:
private URLConnection openConnection(URL url, Request<?> request) throws IOException {
URLConnection connection = createConnection(url);
int timeoutMs = request.getTimeoutMs();
connection.setConnectTimeout(timeoutMs);
connection.setReadTimeout(timeoutMs);
connection.setUseCaches(false); // <-- Disables the caching
connection.setDoInput(true);
// use caller-provided custom SslSocketFactory, if any, for HTTPS
if ("https".equals(url.getProtocol()) && mSslSocketFactory != null) {
((HttpsURLConnection)connection).setSSLSocketFactory(mSslSocketFactory);
}
return connection;
}
You can see this code for yourself here: Volley - HurlStack (line 167)
The moment I comment out this line, the HttpResponseCahce works as expected.
My question is: Why does Volley disable the UseCaches flag in all URLConnections, and what is the risk / side-effects of removing this?
I'm using OkHttp to cache responses to PUT requests that use an "Authorize" header for authentication, and am not seeing any caching.
The Android client code sets the cache like this:
int cacheSize = 10 * 1024 * 1024; // 10 MiB
cache = new Cache(context.getCacheDir(), cacheSize);
client.setCache(cache);
My server is responding with:
Cache-Control: public, max-age=60, s-maxage=60
When I log the cache, getRequestCount() increments, but there are no hits, or any urls in the cache.
Any idea what I'm doing wrong?
Thanks!
Only GET responses are cached, so my fix was to convert to GETs. In OkHttp Cache.java:-
if (!requestMethod.equals("GET")) {
// Don't cache non-GET responses. We're technically allowed to cache
// HEAD requests and some POST requests, but the complexity of doing
// so is high and the benefit is low.
return null;
}
This may be a bug in Picasso, but I wanted to post to StackOverflow first.
I am getting the error "Received response with 0 content-length" when the responses are being read from the cache from disk. I can reproduce this error every time by
1) Run my app without OKHttp in classpath. Let pictures load
2) Add OkHttp into classpath, I get that error.
I added Picasso source to my project to investigate further. I found out that
1) Turning off caching connection.setUseCaches(false); will bypass the error (since it ignores the cache)
2) I found the switch in the Picasso source where it checks if OkHttp was available
try {
Class.forName("com.squareup.okhttp.OkHttpClient");
okHttpClient = true;
} catch (ClassNotFoundException ignored) {}
and was able to reproduce the bug by hardcoding true, then false between runs.
I want to solve this problem so I can use OKHttp (and provide a viable upgrade for my current users) and all the benefits that come with it. I also have seen this "reading response with no content-length from cache" problem in other cases in my live environment. Once I get into the state with a bad response in cache, the pictures will never show up.
OkHttpClient okHttpClient = new OkHttpClient();
RestAdapter restAdapter = new RestAdapter.Builder().setClient(new OkClient(okHttpClient)).build();
OkHttpDownloader downloader = new OkHttpDownloader(okHttpClient);
Picasso picasso = new Picasso.Builder(this).downloader(downloader).build();
Source: https://stackoverflow.com/a/23832172/2158970
I have to download one large compressed file (contains multiple files) from an FTP server. During downloading, if it is interrupted/paused by the user or network, the broken downloaded file should be saved. Later, user can resume download the same file from where it is broken.
You can use DownloadManager class.
Download manager is a system service that handles long-running HTTP downloads.
Check http://developer.android.com/reference/android/app/DownloadManager.html
Above class available since API level 9.
One can set the range of bytes to download using the Range HTTP header. All you have to do is remember how many bytes you previously downloaded. Using the classes from apache, it would look like this :
DefaultHttpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(youSourceUri);
// That's the important part. Count represents the number of bytes already downloaded.
// We leave the range open, so it will download 'till the end of the file.
httpGet.addHeader(new BasicHeader("Range", "bytes=" + count + "-"));
HttpResponse response = httpClient.execute(httpGet);
HttpEntity entity = response.getEntity();
InputStream inputStream = entity.getContent();
// Then read from the input stream
Remember to add the appropriate try/catch/finally clauses to handle the streams safely and be sure to close them. They were omitted here for readability.