Further semantics and practices
Well, I personally think that ListView
s are some of the most complicated View
s in the Android ecosystem. So, I'm going to make two blog posts talking about them! The first is the ViewHolder vs. HolderView pattern, the second will be some nasty issues on ViewHolder caching I ran into. So, without further ado:
What is ViewHolder?
One of the biggest problems in displaying a ListView
in Android is just how inefficient it is. Specifically, calling findViewById()
is really expensive, so we try and do everything we can to avoid it. That's why the ViewHolder pattern was invented. The basic principle is:
The
ListView
gives us aView
object when it requests a view from the adapter withgetView()
If that
View
isnull
, we need to inflate a new one. Then, cache it in the view withsetTag()
If that
View
is notnull
, we can use the View'sgetTag()
method to retrieve a cached version of it (since it's been inflated prior)
So, what we actually cache in that sequence above is the ViewHolder
object. Basically, it just stores a static reference to the inflated fields so we don't have to reinflate everything. Then, the actual adapter is responsible for updating the inflated View
using the references in the ViewHolder
.
If you have any other questions on ViewHolder, check out this page because the author did an amazing job of explaining everything.
What is HolderView?
The HolderView pattern has the same basic principle. We want to make as few calls to findViewById()
as possible. There's a big difference though: The HolderView
is responsible for its own presentation.
- The
ViewHolder
just stores a reference to theView
elements that are being inflated (by the adapter). TheHolderView
actually inflates itself. - The Adapter driving the ListView is responsible for the presentation of data in each element. The
HolderView
handles it's own presentation through abind()
or some similar method. - The
ViewHolder
is a static class, and so can't be garbage collected. EachViewHolder
takes up incredibly little space, but if you're OCD, you still have unused objects you can't remove from memory. TheHolderView
is allowed to go out of scope and be deconstructed.
There are a few downsides too:
- The
ViewHolder
pattern is nearly ubiquitous on the Internet. Honestly, if I wasn't forced to useViewHolder
by Android Annotations, I would never have known it exists (I can't justify it, but I'm guessing a similar pattern is used on iOS development, feel free to let me know). - The
HolderView
likely means you'll have another class file. You don't necessarily have to do this, but it's usually better that way. - The
HolderView
is used in place of the view that theListView
is actually trying to inflate. That is, theViewHolder
just caches references to elements inside theView
. TheHolderView
actually replaces theView
(which means typecasting).
By any means, the HolderView
pattern kind of acts like an MVC layer for your views. You can check out my original implementation over here.
So the biggest benefit of using a HolderView
pattern is that you can move the presentation logic of a View
to its own class, and thus do some pretty cool stuff (for example, Dagger won't let you inject non-static inner classes).
So those are the two patterns! Coming up next: more than you ever wanted to know about ListView
caching.