Monday, April 19, 2010

Returning values from AsyncTask

While developing an application for Android, very recently, I came across an issue. I had a bunch of ListViews, each of which fetched data from a web service and displayed it. Each ListView had a different way of displaying data and different source to fetch data from.

I subclassed AsyncTask and used that to pull the data in the background and display it in the ListView. AsyncTask is easy to use and allows results of background threads to be published to the UI thread. But I ran into an issue pretty quickly. All that example I came across, implemented the AsyncTask as a nested class. For me, this meant I had to duplicate the code for every ListActivity, which, for obvious reason, I did not want to do. So, my problem definition was this:

How can one return data from an AsyncTask? More specifically, if I were to reuse AsyncTask across multiple classes, what is the better way to do it than creating a nested class?

Here is one solution:
  • Declare an interface.
public interface NetworkCallListener { 

  public void onNetworkCallComplete(Listlist); 

  public void onNetworkCallCancel();

}
  • Subclass AsyncTask
public class NetworkCallTask extends AsyncTask> {

  NetworkCallListener listener = null;

  public NetworkCallTask(NetworkCallListener listener) {
    this.listener = listener;
  }

  public void onPreExecute() {
    // do something...
  }

  @Override
  protected List doInBackground(String... args) {
    // do something
    List list = doSomethingAndGetBackAnArrayList();
    return list;
  }

  protected void onPostExecute(final List list) {
    // do something

    // Now call the listener's onNetworkCallComplete method
    // and set the value.
    listener.onNetworkCallComplete(list);
  }
}
  • Implement the interface in the ListActivity and create an instance of the AsyncTask subclass just created.
public class MyListActivity extends ListActivity implements NetworkCallListener {

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // set your content view and other stuff here and finally call the async task.
    String url = "http://somwebservice.com/service";
    new NetworkCallTask(this).execute(url);
  }

  public void onNetworkCallComplete(List list) {
    // do something 

    // set the ListView's adapter. Below SomeAdapter is a subclass of ListAdapter.
    this.setListAdapter(new SomeAdapter(list));
  }
}

That is it. Now, I can just re-use the AsyncTask subclass that I wrote. I will upload a full working example to GitHub soon. Please feel free to email me or comment other ways of doing the same.

No comments: