As you might have known (previous post), I’ve been tasked to go and learn Android. I’ve mentioned in the last post that I’m not a huge fan of the xml layout capabilities, but there isn’t much I can do with that, you’ve got what’ve got.
Android ListView
So I ran into another problem with ListViews this week, I couldn’t for the life of me get a customised list to run fast. The list in question is like a regular contact list, headers for each letter of the alphabet (where required) and contact rows for the users. Each contact row has a background image, different contact images and various text tweaks.
I didn’t realise at the time, but the getView method which you override to supply the new customised rows has an argument that you should not overlook (I did!). The key to performance lies in the second argument, this passes you a view from the list which isn’t displayed in the screen any more. The ListView is rather clever here and rather than creating new items for every single item in the list, it ask’s you the question “can you reuse this view?”. It’s now up to you to decide if you can reuse the view or not?
The simple answer: You should always reuse the view even if the views are different. In my case the headers are different from the contact rows, because you don’t know the order of the what the ListView is going to pass back the views. Without going down the path of forward look ups (expensive?). In my case I could sometimes get a header and what I really wanted was a contact row, so I would throw that view away. That row would then be left for the GC and I would have to create a new one. Memory allocation on any device is very expensive, be careful how you do it and where you do it!
I worked out for one contact user row we can reuse a lot of the views the more contact rows there is, which normally goes against the grain. The more rows there are the more optimised the code becomes. Unfortunately if you have not enough contacts in the ListView you end up running the worst case, not enough contacts and you run slow and just to many contacts to run slow as well. This isn’t good enough!
Object pooling
At first I thought about creating a pool of rows for both header and contacts. So the creation of the rows are up front, reusing the rows depending on the content required. Except this won’t work because the ListView already manages this and you’ll end up getting into a mess, where you’re doing some pooling and the ListView is doing it’s own pooling. Avoid this at all costs, the next solution is a lot simpler.
Abstract your views
So I was discussing this with a friend of mine and he said I was doing this the wrong way (cheers!). What I should be doing is finding similarities of my layouts and abstracting them into a view. Adding backgrounds and just setting the visibilities to “gone”. Effectively hiding them, when needing them setting them back to “visible” in code. This way, when ever getView passes back a view I can use it! It’s better to add images to a ImageView by code than create a new row.
Inflation of code is expensive, creation of lots of new layouts, views are expensive. It’s far better to create one item than many, it may seem obvious saying it now, but behind all the method calls and other logic it’s hard to see the trees from the wood.
Reuse your views!