In the last eleven blog posts, we've covered all major functionalities of Picasso. You've learned a lot of various features in the library. In the last two blog posts, we're going to modify the Picasso instance itself. This gives us access to the core of Picasso's behavior.
Picasso Series Overview
Picasso.Builder for Custom Picasso
Picasso has a direct way to modify the Picasso instance: the Picasso.Builder class. We'll use the Picasso.Builder
to create our own custom Picasso instance. Our new Picasso instance can have several substituted components. Before we look at the possible replacement components, let's look at how to create the precious custom Picasso instance.
Using Custom Picasso Instance Locally
Before we jump into our custom Picasso instance, let's briefly reflect on how we got our standard Picasso instance so far:
Picasso picasso = Picasso.with(Context);
The Picasso.with(Context context)
always returns the standard Picasso instance. In case you require a custom instance, one option is to simply create a Picasso.Builder
object, make your adjustments and finally build a Picasso instance.
// create Picasso.Builder object
Picasso.Builder picassoBuilder = new Picasso.Builder(context);
// todo make your adjustments here (will do in a minute)
// Picasso.Builder creates the Picasso object to do the actual requests
Picasso picasso = picassoBuilder.build();
The newly created Picasso object has the same capabilities as our standard Picasso.
// instead of Picasso.with(Context context) you directly use this new custom Picasso object
picasso
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.into(imageView1);
In case you need a special Picasso behavior for all of your requests, you've the option to globally use your custom Picasso instance.
Using Custom Picasso Instance Globally
The way to create and modify the Picasso instance stays the same:
// create Picasso.Builder object
Picasso.Builder picassoBuilder = new Picasso.Builder(context);
// Picasso.Builder creates the Picasso object to do the actual requests
Picasso picasso = picassoBuilder.build();
To make this Picasso instance the global one, call Picasso.setSingletonInstance(picasso);
. Important is that you can only do this before you did any Picasso requests! Ideally, you should call this on application start.
// set the global instance to use this Picasso object
// all following Picasso (with Picasso.with(Context context) requests will use this Picasso object
// you can only use the setSingletonInstance() method once!
try {
Picasso.setSingletonInstance(picasso);
} catch (IllegalStateException ignored) {
// Picasso instance was already set
// cannot set it after Picasso.with(Context) was already in use
}
Once you implemented it on app start, all future Picasso.with(Context context)
calls will return your custom Picasso instance:
// you can continue to use Picasso.with(Context context), but it'll return your custom instance
Picasso
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[1])
.into(imageView2);
You'll have to decide which option is ideal for your app. In order to help you decide, we'll show you one possible custom Picasso behavior: a replaced network component.
Influencing Picasso's Behavior: Replacing the Downloader
Picasso will by default use the best available cache & network component available. If you want to make sure that Picasso is using a specific one, use the Picasso.Builder
and call .downloader(Downloader downloader)
on it. One implementation of the Downloader interface is Square's HTTP client OkHttp.
// create Picasso.Builder object
Picasso.Builder picassoBuilder = new Picasso.Builder(context);
// let's change the standard behavior before we create the Picasso instance
// for example, let's switch out the standard downloader for the OkHttpClient
picassoBuilder.downloader(new OkHttpDownloader(new OkHttpClient()));
// Picasso.Builder creates the Picasso object to do the actual requests
Picasso picasso = picassoBuilder.build();
picasso
.load(UsageExampleListViewAdapter.eatFoodyImages[2])
.into(imageView3);
This doesn't have any real effect, since Picasso would pick OkHttp anyway (if it's available). A practical example would be the following real-world-scenario: your app downloads images from your own server. That server uses HTTPS, but also has a self-signed certificate assigned. The standard OkHttp implementation would reject this connection due to the SSL certification issue, followingly not download the image and consequently your ImageView would stay empty.
You could fix this issue with an OkHttp implementation (see UnsafeOkHttpClient), which ignores the HTTPS issues. By setting this HTTP client as your Picasso downloader, you'll be able to display images, which are hosted on a self-signed HTTPS environment.
picassoBuilder.downloader(
new OkHttpDownloader(
UnsafeOkHttpClient.getUnsafeOkHttpClient()
)
);
Obligatory warning: make sure you know what you're doing before using a network component, which ignores all security checks!
Further Customizations
The Picasso.Builder
class offers more customizations besides just the downloader (which also functions as the disk cache). In our opinion the two most interesting pieces are:
- Memory Cache: if you don't agree with the standard settings (15% the available application RAM), you can implement your own caching solution and apply it with the
Picasso.Builder
. - Request Handlers: if you've images on a custom Uri format, request handlers give you a powerful tool. The next and final content blog post in this series will be all about request handlers.
Outlook
In this blog post, we've given you a quick introduction on how to change core behaviors of the Picasso library. Your use of the Picasso.Builder
might be completely different, but this blog post should have given you a jump start.
Next week, we'll look at one core behavior in much greater detail: request handlers.
As always, let us know if you're missing something or need additional explanations in the comments or on twitter @futurestud_io.