Latest Tweets

 

Rendering Android Views in the background

The Android AdapterView classes are very efficiently implemented and provide developers with a solid basis for creating fast, smooth views over arbitrarily large datasets. Their basic APIs have remained stable since Android was introduced and yet, based on the applications I try-out (and even some I use regularly) it seems that developers often make poor use of them.

This is the first of two posts that I’m hoping will improve this situation, by sharing some of the code I’ve developed for my own applications.

Creating smooth Adapters

The performance of an AdapterView is mostly determined by the Adapter you provide it with. There are two core obligations when developing an Adapter that will provide a smooth user experience†:

  1. Items must be returned without delay.
  2. Views must be returned without delay.

It’s that simple… Except what happens when your items are records that have to be retrieved from a Web server? And, to compound things, what if displaying the record requires downloading a couple of images? These problems can actually be tackled separately. This post provides a base class that helps with 2 (I’ll provide code to help with 1 at a later date).

Complications

What makes 2 complex in this scenario is that there’s no time to do anything other than return a preliminary rendering of the View from the Adapter, so the work to produce a complete rendering of a View must be done on a background thread.

Unfortunately, what makes this still more complex, is that the efficiency of classes like ListView and GridView is only possible because they recycle the Views they use to render items. This complicates the task of asynchronously updating the View because during the interval in which the complete content for the View is being generated, the parent AdapterView may have assigned it another item to display (possibly many times). In this case the existing rendering task should probably be abandoned (it certainly shouldn’t get assigned to the view) and instead a new rendering needs to commence.

Of course, this situation may itself be temporary: A user may briefly scroll an item off-screen and then back again; we don’t want to keep restarting the same rendering task without making any progress. To handle this efficiently caching may be necessary and may need to be combined with partial updates. And, as if things weren’t complex enough, the re-appearing item may be assigned to a different View to the one that initially displayed it (even if its position on-screen does not appear to have changed for the User).

A helpful class

So, yes, there is some complexity around implementing a good Adapter, but many Android developers have had years to get this right! Hopefully this code will help: it handles all of this apparent complexity in what is a fairly simple base class called ViewRenderer.

It’s designed to be invoked within the getView() method of Adapter. In this approach the getView() method should be limited to:

  • Obtaining rendering parameters for the specified position (most often this will simply be the adapter item)
  • Recycling the old View, or inflating/constructing a new one (but not customizing it for the item)
  • Calling the renderView() method of ViewRenderer to perform all subsequent customization of the View.

To make this work, you obviously need to customize the ViewRenderer with your own rendering logic. This is done by implementing the following three methods:

  • void prepare(View, Param, int) This method is called on the main application thread before the View is first displayed to the user. It’s your applications opportunity wipe-down the View (since it may have previously presented another adapter item) and display a quick placeholder/loading view.
  • Render render(Param, int) This method is called on a background thread and does the slow work (eg. drawing or loading) to convert the item into the resources needed to display it (eg. a Bitmap or POJO).
  • void update(View, Render, int) This method is called on the main application thread to apply a new render to a previously prepared View.

And that’s pretty much it, everything is reduced to these three digestible chunks. The int that’s being passed into these methods specifies an optional rendering pass to accommodate progressive rendering. There are a few additional operational aspects that can be controlled: render caching, view tagging, thread priority and execution. Documentation for these and everything else can be found in the source code comments.

ViewRenderer code

The code is amenable to a number of improvements (some possibilities are noted code comments), but I hope it’s useful and results in some smoother Android lists and grids, and here it is:


† Not creating garbage helps too.

ADC Judging

I’ve been meaning to post something about the Android Developer Challenge for a good while, but I’ve been so busy recently (architecting a multi-faceted e-commerce system at the moment) that I’ve had no time for anything else. In fact, I was so busy that the results almost passed me by.

Daisy Garden didn’t win. This was not surprising since I’d had an opportunity to judge a number of the applications in entertainment category. It’s not that I felt there were lots of significantly ‘better’ applications, but that so many of the applications were more suited to the challenge. Congratulations to all the winning entrants. My prediction had been for a music app 1,2,3 in my category and I was almost correct (it was a music app 2,3 instead).

But the results aren’t what prompted this post; I’ve been more interested in how the voting has been structured in both challenges, and I’ve had the opportunity to think about it: my entry into the first ADC flopped, then I joined a team that went on to win, this time round my entry made it to the shortlist but didn’t win - so I’ve pretty much run the gamut of possibilities.

I was mildly critical about the structure of the voting after the first ADC. In my opinion, it pretty much amounted to a map-reduce algorithm in which apps were distributed over judges. The ADC2 was essentially similar and introduced bucketing (perhaps ensure variety?) and pre-filtering with a public vote (to ensure the expert judges had a manageable task, I’m sure).

My basic criticism was, and remains, that such a voting scheme hampers people’s capacity to reach a good judgement because it doesn’t provide a medium through which they can influence each other’s evaluations. The judging process essentially atomizes the judges and fails to take advantage of their human strengths. And I think that understanding this has implications for my field of endeavour: creating software.

I think I’m paraphrasing Bjarne Stroustrup here when I say that software design involves balancing lots of subtle and complex considerations, and the best place to do that is inside the brain of a programmer. If one accepts this, there is an implicit assumption that all of the necessary considerations are known (and will fit into one brain*). I believe this perspective explains a lot about why ‘design by committee’ fails: a team of human’s can’t compete with synapses at transmitting and evaluating ideas. It also explains why ‘elite solo programmers’ may often fail: they can form good judgements but usually don’t have all the necessary data.

In trying to avoid the worst weaknesses of these two extremes, I look to encourage good design by:

  • encouraging everyone to share their ideas about what they think is the best design
  • observing which person best manages to keep all of the competing perspectives in their head
  • tasking that individual to come up with the design
  • validating the design by presenting it back to the team

With this approach I try both to preserve the clarity that solo-design frequently provides and avoid the narrowness that often accompanies it.

Back to the ADC judging. Forming an unqualified analogy between designing software and judging is application is clearly absurd, but there are similarities, and I think that a panel of judges who freely discussed their opinion about each application before recording individual ratings would provide fairer judgements than may have been the case in the Android Developer Challenge. For example, if there were five judges, and one judge knew that the application under consideration was a direct copy of an existing application, he could share that information with the other judges who could factor it into their rating. Without that exchange of information, the more knowledgeable contribution becomes marginalized.

The point is to target two human strengths: information sharing and individual decision making. Of course, the curse is that this approach doesn’t scale, but I’d love to find one that does, because I’m sure I’d learn a lot about designing software from it.

Thanks to Google for hosting and funding the Android Developer Challenges, it’s been fun.


(*) I think this is the simplest gauge of whether a software project will be successful: If the requirements can’t simultaneously fit into one brain then problems are unavoidable.