Picasso — Request Management by Grouping of Images via Tag()

After learning how to load, resize, scale images and how to influence the image managements with multiple aspects, we'll look at a very advanced optimization today: tagging. Picasso offers to group many image requests in order to manage them together.

Picasso Series Overview

Idea of Picasso's tag()

In the last blog post, you've seen how to prioritize certain images. This might be not enough, if you need to cancel, pause or resume multiple images at the same time. If your view changes rapidly, it'd be very useful to cancel all requests of the images on the previous, outdated screen and start the images of the new view. Picasso covers that functionality with tag().

tag(Object object) takes any Java object as parameter. Thus, you can build your image groups based on almost any logic. You have the following options with the image groups:

  • pause requests with pauseTag()
  • resume requests with resumeTag()
  • cancel requests with cancelTag().

Basically, whenever you need to pause or cancel the loading of one or more images, apply a tag to them and then call the respective method. This might sound a little abstract, so let's look at an example.

Example #1: pauseTag() and resumeTag()

The standard example for the use of Picasso tags is a smart Listview. Let's imagine an inbox ListView, where you see messages with their according sender. The sender will be represented by their profile picture.

GMail Inbox Screenshot

Now, let's think about the following scenario: the user is searching for an old message and scrolls down with fast fling movements. The ListView is designed to quickly recycle and reuse the items. If you implemented the adapter correctly, it'll be a smooth experience. However, Picasso will try to start a request for every single row and then has to cancel it right away again, since the user scrolls through the list so fast.

It'd be much more efficient to pause the image loading until the fling movement is finished. The user won't notice any difference, but your app reduces the amount of requests significantly.

The implementation is easy. First, add tags to your Picasso requests:

Picasso  
    .with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .tag("Profile ListView") // can be any Java object
    .into(imageViewWithTag);

Second, implement a `AbsListView.OnScrollListener` and override `onScrollStateChanged()`:

@Override
  public void onScrollStateChanged(AbsListView view, int scrollState) {
    final Picasso picasso = Picasso.with(context);

    if (scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_TOUCH_SCROLL) {
          picasso.resumeTag(context);
    } else {
          picasso.pauseTag(context);
    }
  }

Lastly, set the listener to the ListView:

ListView listView = ... // e.g. findById()  
listView.setOnScrollListener(onScrollListener);  

When the ListView scroll state changes to a fling, it'll pause all requests. When the scroll state returns to a idle or regular scroll position, it'll resume the requests.

The code for this example is in the official Picasso sample project.

Example #2: cancelTag()

The previous ListView example does not utilize the cancelTag() method. Let's look at a different example. You implemented a shopping cart where you display all the selected store items with images. Once the user clicks on the "Buy!" button, you display a ProgressDialog while making the request to the server and checking the validity of the transaction. Once the user clicked on the "Buy!" button, the previous item list is partially hidden. There is no reason to further burden the device's network, battery and memory by continuing to load the item images.

We can optimize the behavior by calling .cancelTag() after the ProgressDialog is being displayed:

public void buyButtonClick(View v) {  
    // display ProgressDialog
    // ...

    // stop image requests
    Picasso
        .with(context)
        .cancelTag("ShoppingCart");

    // make 'buy'-request to server
    // ...
}

Summary and A Warning

These two examples are just the tip of the iceberg of what you can do with request tagging. Depending on your use case, you might want to use a different object as tag. This blog post used Strings, but you can use anything. Some might want to do use the Context (or Activity, Fragment) object as tag. While this is possible and by all means can be smart, keep the following warning from the official JavaDocs in mind:

Picasso will keep a reference to the tag for as long as this tag is paused and/or has active requests. Look out for potential leaks.

In other words, if the user leaves an Activity, where you paused the loading of Picasso images, the garbage collector might be unable to destroy the Activity object. That's a standard example of a memory leak.

Outlook

While it's possible that some of you might never use the technique presented in this blog post, next week will be helpful for many app implementations. We'll investigate other targets, besides ImageViews! Picasso also supports images in notifications, RemoteViews and in a regular callback style. Stay tuned.

Explore the Library

Find interesting tutorials and solutions for your problems.