A group of question often seen on StackOverflow is about inserting data fetched from a webservice into a ListView
. Several examples exist, but I find they are often too chatty, and a number of mistakes can be found in them.
This is therefore my attempt at a clear, precise, and complete demonstration of implementing such a feature.
Code is (as with previous posts) presented as a fully functional Eclipse project that can be cloned from https://github.com/njzk2/JSONToListView
The intent is to be as concise as possible and avoid redundancy.
This demo is based on Sna.pr API to fetch some JSON data.
Download a JSONArray
The Downloader
class is an AsyncTask
that expects a single url as the execute param, and takes an ArrayAdapter<String>
in the constructor.
Using an AsyncTask
is the recommended way of handling networking (or any long operation) that has an impact on the UI. The reasons are that it comes with the callbacks to act on the UI thread, and also that it is executed in a thread pool, meaning that using AsyncTasks guaranties the maximum number of threads used at a given moment for these tasks.
Moreover, networking on the UI thread will give you NetworkOnMainThreadException
on any recent Android.
The actual download of the JSON content is made using HttpClient
for concision, but any http library can be used. It is performed in the doInBackground
method.
An important part here is to explicitly specify the encoding. JSON is by default encoded in UTF-8. (And always in Unicode anyway. See RFC http://www.ietf.org/rfc/rfc4627.txt?number=4627)
Populating the ArrayAdapter
Here, there is no need for intermediary array. The ArrayAdapter
has a add(T item)
method that allows to directly add items. The ListView
will be automatically notified.
In this example, the ArrayAdapter
is a stock object. It is however, possible to extend it. In this case, too, keeping a reference to an array is not necessary. The default methods getItem(int position)
, getPosition(T item)
, getCount()
… allow access to the underlying data, with the guaranty to properly notify the ListView of any change.
Launching the request
To actually launch the request, very little is still needed. A Downloader
instance only need the url and an ArrayAdapter<String>
.
Very simple.
Conclusion
This is not perfect because the AsyncTask
is not stopped when the activity is exited.
This is also not perfect because HttpClient
is not recommended for android >= Gingerbread. I like it for its clarity and simplicity, but I should really start using HttpUrlConnection
.
Some (all) resources are not closed. I know we are supposed to, but I never had any problem with un-closed resources.
In conclusion:
- Don’t hold instance of arrays everywhere. The
ArrayAdapter
alone is sufficient to hold the data in most cases - Don’t do your networking on the main thread. Use an
AsyncTask
, or aService
- JSON is encoded in UTF-8 by default. Any other value could work for a while and then break when you encounter a more exotic character
- Use a
ListActivity
when using aListView
. It removes some boilerplate - Use existing Android method, such as
EntityUtils
, to lighten and shorten your code