When should you resize your network images?

In today’s mobile world, there’s a plethora of devices with many different screen sizes and varying processing power. This represents a huge problem for modern developers: What resolution should I use for my images?

Consider this: A user uploads an 8 MegaPixel photo from their phone. It makes sense that another user with the same screen resolution would want to see the image in the same size. But what about a user whose screen is only half or a quarter that size? Say, the difference between viewing the image on a laptop monitor and a phone?

Obviously, the smaller screen will display the image using fewer physical pixels (it has fewer of them to use, after all). As such, before the image can be displayed, it’ll need to be resized to fit the display dimensions.

But here’s the problem : You’ve now sent an 8MP image down to a device, which won’t render it at that resolution. So you’re incurring overhead in loading the image on the client device.

Perhaps more problematic, is that users with smaller screens are paying more money to download that high resolution content, which their device can’t fully display.

Sending huge images to devices, when they are displayed as tiny graphics, is never a good use of user’s bandwidth.

The issue here stems from developer burden. If you’re an aggressive, image-based social media app, then you don’t have time to customize each-and-every image size, for each of Android’s thousands of form factors. Sending the full-resolution image to the device, and resizing it before rendering, is certainly the easiest in terms of developer workload. The issue is that the user is paying the results of that trade-off, and both parties are throwing money down the drain.

With all this in mind, it seems like resizing the images on the client side just isn’t the best way to go about things (if you’re concerned about user bandwidth). You’re either creating a bad experience, or wasting user bandwidth.

And the truth is, even though we’ve talked all about various image formats, and how to make them smaller, some of the biggest savings you can get, comes from resizing images before they get to the network.

Do you have a problem?

It’s not always easy to figure out if your images are too large for their display sizes on device. Android generally handles this translation seamlessly, so it’s somewhat opaque in situations where a content creator might be uploading content larger than the display resolution for an image.

Thankfully, Android has some tool-chains to help you out.

Finding the offenders

One of the hard parts of this equation is figuring out what resolution is my image actually displayed at? For this, you can use HierarchyViewer. By selecting your ImageView in question, and clicking “Dump DisplayList” HV will spit out all the canvas commands used to draw that image to the screen to LogCat. (Make sure that you have turned off filtering)

The result is that you should see a line in the format of ;

Draw Bitmap 0xxxxxxxxx of size WxH

This line is describing the size of the texture being drawn. In the image above, it’s 84x84 pixels.

With this, you can get a sense of how the images are being drawn, at what resolution, on the device.

Logging the offenders

HV can show you what the drawn-size is for your images, but now what the input size is. For that, you need to start chipping away at your code, and logging the size of your images as they come down from the intertubes.

If you’re using volley, or glide, just put in some logging code next to the image resizing & loading functions; best way to get a sense of things.

By adding some logging code to volley, you can get a sense of the image sizes that are coming through your application.

Between checking out builds from hierarchy viewer, and grabbing logs from volley, you can get a good sense of what sizes are coming in, and what sizes are being displayed.

The Simple solution

In any situation where you want to be more sensitive to user resolution sizes, you are going to need to expand your app to support some server-side-compute module that’ll resize images into a set of resolutions you’re looking for. But what resolutions should you be targeting?

Android has been aware of this type of issue for some time. After all, that’s why there’s multiple resolution folders in the APK in the first place. Which means the simplest solution for you to adopt, is resize each image to the type of resolutions Android supports, and then when the client requests an image, provide up the desired DPI version for the image at hand.

If you check out the IOSHED2015 codebase, you’ll see that this is mostly what the engineers here at Google put together.

If you have an image at a set number of resolutions on your server, you can choose the closest one, and let the client resize it slightly to fit the bill.

Now it’s worth noting that you don’t need to support every resolution. Android has a really good image-resampler system, and in most of the time, sending down an LDPI image to an MDPI device isn’t going to break the bank (and it’s still better than sending down an XXXHDPI). Choosing a subset of images to support on the server-side could help you reduce your storage costs there, and still would end up decreasing the cost for users to download images.

The awesome solution

But let’s face it, if you’re an image-heavy app, then it doesn’t make sense to build a weak image-serving network; Go big, or go home, they say..

For that, it makes sense to invest in something more scalable, that can react more dynamically to new form factors, and where your users are coming from.

I wanted to talk about this during my Google IO talk on “Image Compression for Android Developers” but ran out of time, so here’s a slide that explains it all…

The gist is this:

  • Allow your client to request an image at a specific dimension
  • Your CDN should be able to validate if that image is cached, at that dimension. If it is, return it to the client.
  • If it’s not, kick off a request to a cloud-compute module (maybe running imagemagick) that can resize the image, and store it in the CDN cache.

The result of this process is that your cached sizes will be a representative sample of what types of resolutions your user devices have. You’re never storing more resolutions than you need, and each resolution comes optimized to the device that asked for it.

In addition, this whole process is entirely hands-off. Developer burden relies in upkeep of the working processes, but not in the content itself.

Every bit counts.

While you’re busy churning away on your android code base, the last thing on your mind is building a scalable image serving and resizing front-end. And the good news is that you don’t have to write all this stuff yourself. There’s options out there you can look at which imgix, cloudinary, resrc.it and the open source thumbor are good reference libraries.

If you’ve been paying attention, there’s more global users on 2G connections than 4G, which means that’s a lot of humans who are paying for content you’re sending down to them. Resizing your images to the optimal size will go a long way to creating happy, loyal users.