Download images from the Internet is a common task we will soon or later have to implement when creating an app. We can either use an external library or implement the whole process by our own. But do it manually is not an easy task since we need to deal with issues like network failures, threads, memory management, concurrence, etc, and we may often end up forgetting something. On the other hand, when using libraries like Picasso, Glide, etc, all those issues are handled by the them and we usually need to add just a few lines of code.

Using those libraries really makes our life easier, but even so there are some topics we should take a close look, such as pause downloads on scrolling, image caching, listen for errors, etc, because normally they need additional steps to work properly. It is also a good idea to try to understand what really happens under the hood. This can give us some background that is useful when we need to do something else than the standard configuration.

So, our goal is to demonstrate how to deal with some of those issues when using Picasso, Glide, UIL, Fresco libraries. As usual, we provide a demo app which implements all topics covered on this article. You can easily change between the available download engines and enable/disable cache mechanisms (disk and/or memory) as well as clear the entire cache with a simple click. There is also a manual image loading implementation for the comparison purpose.  

Before start

Glide, UIL and Picasso, all offer a simple way to load an image to an imageView in a one line code.

Picasso

Glide

UIL

Fresco

Fresco, on the other hand, provides another way (but still simple) by using a special view called SimpleDraweeView, to be used in place of the standard ImageView in our XML layout file.

Then, in the Java code, we just get a reference to it and call its setImageURI method.

For all these four libraries, the simple form will work for most implementation, but depends on what we want, we need to use a more advanced way. Fresco call it as ImagePipeline. For UIL the name is DisplayImageOptions. On Glide, we can make changes on a DrawableTypeRequest object and Picasso uses RequestCreator class.

In the demo app we use only these objects instead of the simple way.

Disk and memory caching

All libraries mentioned on this article come with an out of the box memory and disk caching, and we can turn them on or off when needed.

The normal behavior when both memory and disk caches are enabled, is to first try to load the image from the memory cache. If the image cannot be found, the next step is to look into the disk cache, and only if the image is not on this cache, a network request is made.

Picasso

When we need to disable memory cache, we need to use the MemoryPolicy enum. When using NO_CACHE constant, it will skip memory cache lookup. When using NO_STORE constant, it will skip storing the final result into memory cache. When using both, neither lookup nor storing steps will be taken.

For the disk cache, we do something very similar, but using the NetworkPolice enum instead:

Picasso also has an interesting feature which allow us to visually detects where images came from.

By calling the method setIndicatorsEnabled(true), all images will have an indicator (on the top left corner) which, depends on the color, tells us the image source:

  • Red → Network request
  • Blue → Disk cache
  • Green → Memory cache

Note that this feature does not work when using Targets (i.e. callback).

UIL

Universal Image Loader caching management is pretty straightforward. When building the DisplayImageOptions object we just call either cacheInMemory or cacheOnDisk methods (or both) passing true or false as an argument to enable or disable caches:

Glide

Glide is even simpler. We only need to call a method when we want to skip a cache. For the memory cache we call DrawableTypeRequest::skipMemoryCache(true). For the disk cache, simply call DrawableTypeRequest::diskCacheStrategy(DiskCacheStrategy.NONE).

Note: Glide also offers some other disk cache strategies. See documentation for details.

Fresco

Fresco is pretty similar to Glide. All we have to do is to get the ImagePipeline and call evictFromMemoryCache or evictFromDiskCache to prevent memory and disk caching respectively.

Clear cache

If for some reason we need to clear all cache at once, Fresco, UIL and Glide libraries offer methods which allow us to do it. For Picasso we need to add a little bit of code in order for this to work.

Glide

Glide provides two useful methods: Glide::clearMemory and Glide::clearDiskCache. Note that clearMemory must be called in the UI thread, while clearDiskCache should be called in a worker thread, since it may take longer to finish its job.

UIL

UIL is even simpler. There are two methods pretty straightforward:

Fresco

As UIL, Fresco provides two methods to clear the cache:

It also provides a single method which clears both memory and disk caches at once:

Picasso

Picasso does not provide a method to clear its cache at once, so we need to do it by ourselves. For the memory cache it contains an interface called Cache with a method called “clear”, but since it is a package-private method, it is only visible inside its package (com.squareup.picasso). By creating a class in a package with the same name (i.e.: “com.squareup.picasso”), we can have access to that method.

I found this workaround on this SO question. As the author suggests, “…because cache has package visibility, this util class can clear the cache for you. You just have to call it…”:

Then, we just call it:

Regarding to the disk cache, as suggested in this SO question, we can clear it by removing the entire “picasso-cache” folder that resides in the default cache dir (unless we have change it before):

Using callback

Sometimes we want to have more control over the image being downloaded. For example, to do something when an error happen (e.g.: log a message, retry a download, etc) or take some action prior display the image in the ImageView. For such cases, we need to use something called callback. When using it, instead of the libraries display the image to the ImageView, they will call a callback so that we will have a chance to do what we want.

Picasso

Picasso call it as Target. Instead of passing an ImageView instance to the into method, we pass an instance of the Target class. Once the image is ready, onBitmapLoaded will be called. In case of error, onBitmapFailed is called.

UIL

UIL uses similar approach, but call it as ImageLoadingListener. Their methods are self explanatory.

Glide

As Picasso, Glide calls a callback as Target. So we can create a Target instance and pass it to the GenericRequestBuilder::into method

If we want to listen for errors, we can implement the RequestListener interface and add it to the DrawableTypeRequest. Then, inside the onException callback we can check for any error (if they occur) and take the appropriate actions.

Fresco

When using Fresco, we need first create a DataSubscriber and add it to the dataSource.

Note: According to the Fresco documentation “…You can not assign the bitmap to any variable not in the scope of the onNewResultImpl method. The reason is … after the subscriber has finished executing, the image pipeline will recycle the bitmap and free its memory. If you try to draw the bitmap after that, your app will crash with an IllegalStateException….”. Due that, we need to clone the bitmap so that we can use it outside the onNewResultImpl method.

Cancel downloads on scrolling

When loading images to an adapter which uses recycling (i.e. RecyclerView), it is a good practice to cancel those downloads which refer to views that are no longer visible. Once that view is visible again, we could restart the download. RecyclerView provides a method which is called whenever a view is detached from the window. The method is RecyclerView::onViewDetachedFromWindow.

When using the one line code, Glide, UIL and Picasso handling it automatically, so we do not need to care about it. But when using Fresco, we need to do it by ourselves. Also, when using Picasso with target, we also need to implement this mechanism. Let’s see how it works for both Fresco and Picasso (with target):

Fresco

According to the Fresco documentation, this is part of an advanced topic. To achieve it we need to deal with ImagePipeline.

Basically we need to hold a reference to every image that are being downloaded. On our app we created a map to hold it.

Then, when starting the download, we add that information to the map. Do not forget to synchronize this step, since it can be accessed at any time by both the adapter when the view related to this url is no longer visible and when the download finishes.

Now that the image is being downloaded, if we need to cancel it, we just need to search for the url and close the datasource. This will make pending download to be canceled. We can find a reference for it in this SO question.

Do not forget to remove the datasource from the pendingImageLoad list after the download finishes (either in case of success or fail), otherwise we will end up in a memory leak

Picasso

As we told before, when using one line code, Picasso handle it for us, but this is not true when we use Target. So we need to do something similar as we did in the Fresco implementation to handle download cancellation.

First create a map to hold a reference for all images being downloaded:

Then, while requesting the download, add the target instance to the map:

While image is being downloaded, if we need to cancel it, search for the related object in our map and call Picasso::cancelRequest() method.

And once again, do not forget to remove the target reference from the map when download is finished:

Additional notes

On the demo app you will also find another implementation (under “com.motondon.lazyloaddemoapp.imageloader.manual” package) which does the entire image download process manually, including disk and memory cache. This code was taken from this GitHub repository. We added it to our demo app and changed it a little to fit our architecture, but all the credits go to the author. As he states on this SO question, “…The cache implementation is very simple and is just enough for the demo…”, and I believe it is also good enough for the learning purpose.

Another issue related to the demo app is that we use very simple implementation of MVP pattern to separate business logic to the view layer. But as you can realize, we are still passing architecture specific objects (e.g.: context, view objects, etc) through presenter layer which is undesirable. This could be easily solved by using dependency injection (maybe Dagger2), but this is out of scope of this article.

Conclusion

Libraries such as Glide, Picasso, UIL and Fresco are very powerful. We demonstrated only some topics of what they can do for us. We strongly recommend you to read their documentation, which provide a very good explanation about what they offer. Also there are many good articles online which give a step-by-step on how to use them, since the basic usage until some advanced topics.

We hope by putting these topics using different libraries in a single article, it can help those whose need to deal with lazy loading images.

In case of questions, please, leave a comment.