In the mobile world, we always have to take the user’s internet connection quality into consideration. Unfortunately, most people rather than using our app at home with their super fast Wi-Fi within reach will do it on the go. Their internet connection may sometimes have very poor quality and because of that loading screens and proper error handling are crucial to creating apps with the great user experience. And it’s even more important if you’re creating an app which consists mainly of high-quality images downloaded from the web.

Most of you probably used Picasso in their projects. It’s a very popular image downloading and caching library for Android. I also use it heavily at work and I really like it because it’s powerful, easy to use and efficient.

However, Picasso has an irritating trait

There is one thing that disturbs me in Picasso and may really annoy users if they‘re having a slow and unstable internet connection.

If internet connection suddenly drops while the image loads user may see the image that’s partially black.

Partially loaded image (Picasso)

You may think it’s not a problem. It’s even better because you may serve partially fetched content, show an error and the user may reload image when the internet connection is back. But to properly handle this situation you have to know that something went wrong.

You’d probably expect that callback passed to into method or global error handler will be notified in this scenario. However, neither of those are.

Fetching image with callback

Setting a global error handler for Picasso

The reason is, that Picasso doesn’t check if Bitmap converted from Stream is correct. It uses Androids bitmap decoder ( BitmapFactory.decodeStream method) which takes the stream and returns Bitmap. If stream ends due to internet connection problem but Bitmap was partially created, it will be returned from BitmapFactory.decodeStream without throwing an exception or notifying that something went wrong. So Picasso simply doesn’t know if an image was fetched completely, it assumes that everything went fine even if the image was loaded only in 5% or 10% and it’s completely useless to the user.

Refreshing partially loaded image

What’s worse — if you try to load image again you may get the same, corrupted file as earlier even if internet connection quality is good now.

The reason is that Picasso stores that incorrect image in cache. The only way to reload image is to disable cache completely for that request (not recommended) or clear cache before reloading image. To do it you have to create a new file in com.squareup.picasso package (I called mine CacheHelper ) with following extension functions. It’s important to place this file in this specific package because cache is package-private inside Picasso class.

CacheHelper.kt

Using those functions you may clear all Picasso cache or cache for specific URL. If you clear cache before reloading, corrupted file will be removed and with good internet connection quality the image will finally be fetched correctly.

reloading image

But it’s still not an ideal solution. You don’t know when to show the refresh button to the user since the library doesn’t notify that anything went wrong. So you can either show the refresh button always (even if the image was fetched correctly) or to ignore the problem and not allow users to refresh content if the fetched image is partially black.

There’re open issues on Picasso Github page regarding that problem ([1] [2]), but there’s no sign that it may be fixed since the bug is in Android’s bitmap decoding function and library creators have no control over it.

Universal Image Loader

If you test the same scenario using Universal Image Loader library, sadly, you’ll get the same result. If internet connection drops while image loads image will be fetched only partially and some part of it will be black. Also if you enable memory cache you’ll have to clear cache before reloading image because incorrect bitmap will be stored in cache and returned even after internet connection is back.

If you check library implementation you’ll see that Universal Image Image Loader also uses the flawed BitmapFactory.decodeStream method which doesn’t throw an exception if internet connection breaks during decoding.

Possible workaround

However, if you’re using Universal Image Loader and want to work around this issue there is a way to do it. If you enable disc cache ( .cacheOnDisk(true) ) the first step while fetching image will be to download and save the image on the disc. The method responsible for it correctly throws ProtocolException with detailMessage="unexpected end of stream" if the connection drops during download. Information about that error is propagated further and a listener is properly notified that something went wrong.

What about Glide?

As it turns out Glide doesn’t use flawed BitmapFactory.decodeStream method at all so this problem there doesn’t exist 🎉🎉🎉.

Glide decodes stream without using BitmapFactory.decodeStream and ProtocolException with detailMessage="unexpected end of stream" is thrown internally if stream terminates due to the connection error.

part of StreamEncoder class in Glide library

Then if the full image was not fetched successfully, in DecodeJob class GlideException is created which is later passed to error listener. If internet connection is lost during image fetch image won’t be displayed and a listener will be notified about exception so you can properly handle it.

So once again: Glide 1 – 0 Picasso 😉

TL;DR