Picasso — Custom Request Handlers

In the previous blog post, we've given you an introduction to the Picasso.Builder, the base to adjust core functionalities of Picasso. If you haven't given it a read, you might need to do so in order to fully grasp this blog post's content.

In this blog post, we'll look at request handlers to load and display images from custom Uri schemes, like futurestudio://profile/image/norman. In order to make it a real-word example, we'll use custom Uri schemes from our eat foody project.

Picasso Series Overview

Custom Request Handlers via Picasso.Builder

Once again, we'll not go into details on how to setup your custom Picasso instance, so we expect you to understand the following code snippet:

// create Picasso.Builder object
Picasso.Builder picassoBuilder = new Picasso.Builder(context);

// add our custom eat foody request handler (see below for full class)
picassoBuilder.addRequestHandler(new EatFoodyRequestHandler());

// Picasso.Builder creates the Picasso object to do the actual requests
Picasso picasso = picassoBuilder.build();  

The only new part is the line, where we set the our EatFoody request handler as an example for this tutorial, which is quite straightforward:

picassoBuilder.addRequestHandler(new EatFoodyRequestHandler());  

Now our Picasso instance is aware of the request handler and will pass every future request to it. All of our prep work is done, so let's look in more detail at the RequestHandler class.

RequestHandler Implementation

The request handler interface has two methods:

  1. boolean canHandleRequest(Request data)
  2. Result load(Request request, int networkPolicy).

The first method will let Picasso know if this request handler can handle the current request. In case it can, the request will be passed to the load() method.

An entire implementation of a request handler could look like this:

public class EatFoodyRequestHandler extends RequestHandler {  
    private static final String EAT_FOODY_RECIPE_SCHEME = "eatfoody";

    @Override
    public boolean canHandleRequest(Request data) {
        return EAT_FOODY_RECIPE_SCHEME.equals(data.uri.getScheme());
    }

    @Override
    public Result load(Request request, int networkPolicy) throws IOException {
       // do whatever is necessary here to load the image
       // important: you can only return a Result object
       // the constructor takes either a Bitmap or InputStream object, nothing else!

       // get the key for the requested image
       // if the schema is "eatfoody://cupcake", it'd return "cupcake"
       String imageKey = request.uri.getHost();

       Bitmap bitmap;
       if (imageKey.contentEquals("cupcake")) {
           bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.cupcake);
       }
       else if (imageKey.contentEquals("full_cake")) {
           bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.full_cake);
       }
       else {
           // fallback image
           bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
       }

       // return the result with the bitmap and the source info
       return new Result(bitmap, Picasso.LoadedFrom.DISK);
    }
}

As you can see, the snippet contains a custom Uri schema for our eat foody project.

In the canHandleRequest method the eat foody implementation checks if the incoming request has a Uri schema of "eatfoody" (e.g. eatfoody://cupcake). If the request does not have the eat foody schema, Picasso will check the other request handlers and, finally, if none of them is able to handle the request, revert back to the standard downloader.

If the request actually has a eatfoody schema, the load() method implementation of our class is called and is responsible of returning a result object (which ideally contains a Bitmap). Our implementation could either do a network request or load the image from the phone's disk. In order to keep it simple, we just check the path of the Uri for two of our favorite recipes and load the according image from our app resources. If it doesn't match, we return the eat foody icon.

At the end, we've to create a Result object to return our bitmap. It's a wrapper for the bitmap itself, and the indication for the source. In our case, we loaded the image from disk.

Example Requests

Enough dry theory, let's move on to some examples. As expected, if you pass regular http Uris to Picasso, it'll not use our eat foody request handler (since http doesn't match eatfoody). Picasso will load the image normally.

// example #1: regular HTTP Uri schema, which will not use our custom request handler
picasso  
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .into(imageView1);

However, you can use the same Picasso instance to pass eatfoody Uris. Picasso will detect that our request handler is suited for the task and let us load the appropriate image from disk:

// example #2 & #3: custom eatfoody Uri schema, which will trigger our custom request handler
picasso  
    .load("eatfoody://cupcake")
    .into(imageView2);

picasso  
    .load("eatfoody://full_cake")
    .into(imageView3);

Summary

Picasso makes handling custom Uri schemes as easy as it can be. The implementation is straightforward and easy to grasp. This blog post gives you the fundamentals to make custom request handlers work. Of course, we only showed the tip of the ice berg of the possibilities with Picasso's request handler.

Let us know how you're using Picasso request handlers in the comments!

Explore the Library

Find interesting tutorials and solutions for your problems.