Implementing a Lazy Loading Android Gallery with Mirah

March 31, 2011 at 8:20 PM

Image Galleries

The Gallery is a horizontal scrolling list commonly used for displaying a list of images. Unfortunately, the standard Android Developers Gallery tutorial focuses on displaying a small, static list of local images. An API-based list of remote images is much more useful, but more complex.

Laziness

Needing to fetch remote images is the cause of this complexity. A naïve approach might download all of the images sequentially, but that quickly becomes untenable as the number of images in question grows.

The correct solution will build up a cache of images locally, fetching them only as they need to be displayed. Images that have not yet loaded will be represented by a placeholder.

Mirah

I've grown quite fond of Mirah since I wrote my initial reactions about Mirah-Android development. Despite the bleeding edge kinks and snares, I find myself being much more productive than when using vanilla Java. Since the language is relatively new, I thought it could be useful to walk through some real-world code and demonstrate what it can do.

Designing a Lazy Gallery

We'll make three components:

Implementation

This code is all available on Github; clone it and follow along:

git clone git://github.com/abscondment/lazy-gallery.git

LazyGalleryAdapter.mirah

Android's list-like views are backed by an Adapter, which provides a standard interface for creating or updating Views to represent list items.

This implementation accepts a JSON string and turns it into equivalent Java objects.

Using this list of caption/src pairs, it creates ImageViews for the gallery to display. It employs a common adapter optimization and re-uses a few existing views by updating them rather than repeatedly instantiating and discarding many objects.

Note that we're not using a standard ImageView – it has extra methods (e.g. refresh), which we'll talk about below.

While I chose to build the layout by hand to demonstrate more, we could have eliminated some of the creation lines by inflating an XML resource:

GallerySelectionListener.mirah

This listener handles selection changes. It figures out which views are visible when a selection is made and initiates loading of the associated images. By using gallery.setCallbackDuringFling false when creating the gallery, we disable the selection listener during flings so that it only gets called when we actually want to load images.

So, we use the LazyGalleryAdapter to get the caption/src map for our given position. We update the caption and show it.

Then we iterate over the parent's children – these are the visible children, mind you – and call load on the LazyImageViews that they house.

LazyImageView.mirah

The LazyImageView is an ImageView with plumbing for loading a remote image asynchronously (i.e. off the UI thread). It attempts to display an image that is already on disk, but defers fetching remote images until its load method is called.

LazyImageView also handles large images gracefully by asynchronously resizing them on disk with safe_image_to_drawable and AsyncResize; otherwise, we will get killed for using too much memory.

Finally, it can invalidate the disk-based cache of images based on image age.

AsyncDownload.mirah

AsyncDownload is a fun little helper for downloading basically anything. It uses some java.util.concurrent goodies to ensure that a given URL is only fetched once for a destination path. Imagine the scenario where a user goes back and forth between two neighboring images before either loads – we don't want the two selection events to trigger double downloads of the images.

I won't post source here, but it's worth a read.

Working Together

Using these components is fairly straightforward:

Try it out

The full source is available on Github as a working application. Try it out!

https://github.com/abscondment/lazy-gallery