Picasso: Received response with 0 content-length header FROM DISK - android

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

Related

Android Volley: How to flush and disable cache?

On my Android phone, I use Volley to do an HTTP POST request. The server sent an error response indicating a problem in the JSON data that I have passed. I have fixed the error but the server still display the same error. I have tried to pass an empty JSON file and I still get the same error response so the response clearly comes from some cached data.
I have tried to use those 2 things to clear and disable the cache but it doesn't help:
mRequestQueue = Volley.newRequestQueue(this);
mRequestQueue.getCache().clear(); // <==== Here
JsonObjectRequest jsonObjReq = new JsonObjectRequest(Request.Method.POST, url, dataObject, ...);
jsonObjReq.setShouldCache(false); // <==== Here
mRequestQueue.add(jsonObjReq);
How can I disable the Cache used by Volley or Android?
If I use Curl to execute the same request it works.
Thanks
I have been able to fix the issue. I was using JsonObjectRequest and the server didn't understand the JSON file that I was sending.
I'm now using StringRequest() and it works.
Here is the non working code using JsonObjectRequest:
https://www.thethingsnetwork.org/forum/t/error-when-trying-to-add-an-end-device-from-http-api/55088/18?u=oliviergrenoble
And here is the working code using StringRequest:
https://www.thethingsnetwork.org/forum/t/error-when-trying-to-add-an-end-device-from-http-api/55088/21?u=oliviergrenoble
I have probably done something wrong in JsonObjectRequest version!

How to detect a redirection in Glide v4 / OkHttp3?

Using Glide v4 and OkHttp3, how can I detect a redirection and load another url when it happens?
My usecase: I use the Glide v4 library with OkHttp3 to download pictures in my app. Sometimes when a picture is not available, a redirection is performed by the server to provide another picture instead of the one I originaly wanted. I can see it in my browser because when I request url A, I finally land on url B with the second picture. When that happens I want to instead load url C that is derived from url A (so not a static url).
At the moment I can detect the redirection using an OkHttp3 Interceptor:
public Response intercept(#NonNull Chain chain) throws IOException {
String requestUrl = chain.request().url().toString();
Response response = chain.proceed(chain.request());
String responseUrl = response.request().url().toString();
boolean redirect = !requestUrl.equals(responseUrl);
if (redirect) {
Timber.d("Detected redirection");
}
return response;
}
but then I don't know how to cleanly load url C. I don't see how I can load another url in the interceptor, and if I throw an IOException to handle the error later in a Glide RequestListener it will just result in a GlideException so I can't determine why it was throw.
OkHttp should be automatically redirecting by default and loading the final content. See OkHttpClient.Builder.followRedirects.
https://square.github.io/okhttp/3.x/okhttp/okhttp3/OkHttpClient.Builder.html#followRedirects-boolean-
My own testing suggests this is working, e.g.
$ oksocial 'https://httpbin.org/redirect-to?url=https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg'
For detecting it, I assume your interceptor is registered via addInterceptor instead of addNetworkInterceptor. Is that correct?
It is also worth ensuring the redirect is via a 30X response and not via a HTML refresh.
The solution I ended up with is pretty ugly: the request interceptor I showed in the question raise an IOException when a redirection is detected. I will then use a GlideListener to detect the error, and load the url C. The ugly part is that in the GlideListener I can't determine the cause of the error, it can be a redirection but it can also be a network error or anything else.
A cleaner version of this solution can probably be achieved with a custom OkHttpUrlLoader but it's too much code for my simple usecase.
This is my solution in Glide v4 / OkHttp3
String redirect = response.header("location");
if(redirect != null && !request.url().toString().equals(redirect)) {
// if redirected your code here
}

Caching Images and strings using Retrofit, okhttp, picasso

I am working on an app with a lot of dynamic and changing content.
I pull all my data from my server when the app is loading.
As a result, nearly every activity/fragment is loading separately which will cause the user to wait a lot of time for each "page" to load individually.
My goal is to create one loading page when the app starts while being responssible for all the downloading and will disk cache all the images and info(strings) and ti pull them at the right time. (or at least to most of it)
I had the chance to use retrofit, okhttp and Picasso as a single additional library, I know though that they can work together and to be synced and that disk cacheing is available through at least two of this libraries (picasso and okhttp) I'm not sure though which one should do which part and how I can sync them together.
I will appreciate every Tip/Guidance, thanks ahead.
okhttp provides support for cache control headers. I've implemented them in an app before to provide a cache when network is flaky using this guide like so:
int cacheSize = 10 * 1024 * 1024; // 10 MiB
Cache cache = new Cache(cacheDirectory, cacheSize);
client = new OkHttpClient.Builder()
.cache(cache)
.build();
As Retrofit uses okhttp internally (if you're using the latest at least), you don't configure any caching for it. Just use the okhttp client you just configured:
RestAdapter restAdapter = new RestAdapter.Builder()
.setClient(new OkClient(client))
.setServer("http://example.com")
...
.build();
Picasso automatically caches images using some default cache size limit. You can change Picasso's default, and I've found some answers here and here. You could set the cache size in the onCreate of your application:
Picasso.Builder builder = new Picasso.Builder(this);
builder.downloader(new OkHttpDownloader(this,Integer.MAX_VALUE));
Picasso picasso = builder.build();
picasso.setIndicatorsEnabled(true);
picasso.setLoggingEnabled(true);
Picasso.setSingletonInstance(picasso);
Picasso also lets you prefetch images earlier on in an app's lifecycle if you have the time to begin with (say, on a loading screen) and want to make later parts of the app load quicker. To do that, I would use the fetch method from the Picasso builder to get the images, but not insert them into any ImageViews. You can Google it too, but there's a quick answer here which explains the background behind this:
Picasso.with(getApplicationContext())
.load(url)
.fetch();
IIRC you need to make sure you fetch the same sized and transformed image as you try and retrieve later, because Picasso caches the transformed image result rather than the raw downloaded image.

Picture lazy loading in android

I'm developing an App on which I have plenty of ListViews. On each ListView Item, there is a picture. In order to load the pictures from the server taking in consideration:
Basic Authentication.
SSL certification.
I would like to know what is the best way to do that. I've try to make a recursive method which uses ImageRequest from Volley Library but it seems like it is a little bit blocking the UI Thread...
I've tried Picasso also which allows to load asynchronously pictures thanks to an association between the ImageView and the request by couldn't make it work with Basic Authentication and by ignoring SSL Certification.
Here is my not solved problem
I'm looking for some other solutions... So If someone has an idea, would be really appreciated...
Thanks in advance !
Picasso is a god solution. To add a custom header just use your own downloader.
OkHttpClient picassoClient = client.clone();
picassoClient.interceptors().add(new MyInterceptor());
// ...
new Picasso.Builder(context).downloader(new OkHttpDownloader(picassoClient)).build();
Read about that in this issue: https://github.com/square/picasso/issues/900
You can also user Fresco (https://github.com/facebook/fresco) with OkHttp: http://frescolib.org/docs/using-other-network-layers.html
Context context;
OkHttpClient okHttpClient; // build on your own
ImagePipelineConfig config = OkHttpImagePipelineConfigFactory
.newBuilder(context, okHttpClient)
. // other setters
. // setNetworkFetcher is already called for you
.build();
Fresco.initialize(context, config);

Android Volley - HttpResponseCache

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?

Categories

Resources