Search code examples
androidpicasso

Picasso working incorrectly after overriding OkHttpDownloader.load()


I have the following requirements for image download:

  • ignoring SSL errors (yes I am aware of the risks)

  • using a session cookie

I tried to adapt Picasso 2.4.0 to do that, below is my approach:

   public static Picasso getPicasso(Context context) {
    /* an OkHttpClient that ignores SSL errors */
    final OkHttpClient client = getUnsafeOkHttpClient();

    return new Picasso.Builder(context)
            .downloader(new OkHttpDownloader(client) {
                @Override
                public Response load(Uri uri, boolean localCacheOnly) throws IOException {

                    final String RESPONSE_SOURCE_ANDROID = "X-Android-Response-Source";
                    final String RESPONSE_SOURCE_OKHTTP = "OkHttp-Response-Source";

                    HttpURLConnection connection = openConnection(uri);
                    connection.setRequestProperty("Cookie", getCookieHandler().
                            getCookieStore().getCookies().get(0).toString());
                    connection.setUseCaches(true);
                    if (localCacheOnly)
                        connection.setRequestProperty("Cache-Control", "only-if-cached,max-age=" + Integer.MAX_VALUE);

                    int responseCode = connection.getResponseCode();

                    if (responseCode == 401)
                        relogin();
                    else if (responseCode >= 300) {
                        connection.disconnect();
                        throw new ResponseException(responseCode + " " + connection.getResponseMessage());
                    }
                    String responseSource = connection.getHeaderField(RESPONSE_SOURCE_OKHTTP);
                    if (responseSource == null)
                        responseSource = connection.getHeaderField(RESPONSE_SOURCE_ANDROID);

                    long contentLength = connection.getHeaderFieldInt("Content-Length", -1);
                    boolean fromCache = parseResponseSourceHeader(responseSource);

                    return new Response(connection.getInputStream(), fromCache, contentLength);
                }

            }).build();
}

The only thing that I changed from the original source is adding a Cookie for the HttpURLConnection. I also copied (unchanged) the parseResponseSourceHeader() method since it has private access.

Note that the approach given here does NOT work (response code 401).

The image loading basically works, but there are major issues:

  • caching doesn't work (fromCache is always false and Picasso always reloads an image which has already been downloaded)
  • there's no "Content-Length" header, so contentLength is always -1
  • though the cache doesn't work, the RAM usage increases when loading next image (into exactly the same or any other ImageView), it seems the Bitmap object stays somewhere in the memory
  • when used inside the BaseAdapter of a GridView, it seems that Picasso tries to load all (or at least as many as the number of times getView() was called) images at the same time. Those images appear, then the app freezes and closes with the following (OOM?) log:

A/Looper﹕ Could not create wake pipe. errno=24

or

 A/Looper﹕ Could not create epoll instance.  errno=24

The described issues occur no matter if I use a custom Target of just an ImageView.

It seems I have broken some of Picasso mechanisms by overriding the load() method of the OkHttpDownloader, but I'm not getting what's wrong since I did minimal changes. Any suggestions are appreciated.


Solution

  • In case someone has a similar problem: it was a really lame mistake of mine. I was creating multiple Picasso instances which is complete nonsense. After ensuring the singleton pattern with a helper class that returns a single Picasso instance everything works as intended.