06/30/2012 § 1 Comment
I am still covering the basis, but I can tell that Adapters are cool stuff.
An Adapter object acts as a bridge between a view and the underlying data for it. For example, the ListAdapter is specialized in handling data for a ListView (it corresponds to the implementation of UITableViewDelegate on iOS).
What is so cool about Adapters?
They allow high level of code reuse and easy customization.
How did I get to this conclusion? Well, let me tell you my story.
I started with a ListView backed by a custom ArrayAdapter that I called PlacesListAdapter. All that it did was receive a list of items as argument, inflate and populate a specific view for each item in the list.
To reduce the time for initial view loading and be as parsimonious with memory usage as possible, I considered to lazily load data as the user scrolls.
Why? Because it is a gorgeous solution. He came up with an adapter – he called it EndlessAdapter – that wraps a ListAdapter and provides the means for loading more items once the user gets to the end of the list. Basically it sums 1 to the number of items in the wrapped list adapter and returns a pending view for this last item. Once this pending view is inflated, the Endless Adapter calls cacheInBackground() in a worker thread for you to load data in background. Once its done, appendCachedData() is called in the main thread and you are able to add that data to your wrapped adapter and notifyDataSetChanged() so that more data is shown on the list. The stop criteria is the amount of items you fetched.
Once this was made past, I changed just a little bit my custom list adapter to fetch data from a ContentProvider. Basically I inherited from CursorAdapter instead of ListAdapter and provided a Cursor object as argument.
It happens a Cursor object represents the results of a query, so in order to load the next set of items, I needed to do a new query and retrieve a new cursor – so everytime cacheInBackground() is called, the query limit is increased by limit, grabbing more data each time. Besides that a CursorAdapter requires the current cursor to be swapped to the new one so that it can reflect the data set change in the list.
But this is not optimal. I looked the documentation and found the MergeCursor, a convience class that lets you present an array of Cursors as a single linear Cursor. Although this approach was very interesting it did’t quite meet my requirements because I needed to fetch the next set of items from the local cache – so the users sees right away the data he wants – and then from a web service – so I get to update the data I have in the cache. Therefore I would have to query the ContentProvider for all the items again.
So I thought of doing as Mark did and create a MergeAdapter class that would present an array of adapters as a single linear adapter. This way I could read only new data of page limit size from the local cache (by increasing the offset instead of the limit), provide the cursor to a brand new PlacesListAdapter and append it to the MergeAdapter. Once the data for that page is fetched from the web service, I could just grab the list adapter for that page and swap the cursors. Beauty!
Fortunately I found a convience class for this as well, and guess who wrote it? Mark Murphy.
In the end the architecture is very simple: an EndlessAdapter wraps a MergeAdapter that wraps a list of PlaceListAdapters.
To get this working on iOS is not that easy. Mostly because of its architecture. In first instance, UITableViewDelegate methods are implemented by the controller, so the developer needs to create the concept of ListAdapter and implement its lifecycle in the controller. Then inherit from this custom controller every time a table view is required. Once this is done, the Android approach can be taken.
But this is not the most important part. The highlight here is that Android helps the developer to write better code than iOS does in this case.