Async Task : Can you wait for AsyncTask to complete

Recently for one of my projects, i was using AsyncTask to fetch data from server using JSON and use the data to populate an arraylist. I wanted to use this arraylist once data fetch is over to render on screen.

To illustrate, I wanted to fetch the list of categories from Server using AsyncTask and then use this list of categories to populate a ListView where each category was a different row in the ListView.

To achieve this, my UI had to wait till the AsyncTask was complete. I read a lot and I got to know that using ProgressDialog is the way to go. So I designed my code like this.

In my Fragment Class, i defined

ListView searchResults;


In OnCreateView, I had the following code.
            

searchResults = (ListView) myFragmentView.findViewById(R.id.listview_search);

myAsyncTask m= (myAsyncTask) new myAsyncTask().execute(newText);

// The AsyncTask populates the ArrayList arrayCategory with the list of 
//categories that we received from the server

searchResults.setAdapter(new SearchResultsAdapter(getActivity(),arrayCategory));


In SearchResultsAdapter, I put the code to populate the ListView with data from arrayCategory.

In myAsyncTask, I initialized a ProgressDialog

I defined

ProgressDialog pd; 

in the myAsyncTask.

In onPreExecute
                 
pd= new ProgressDialog(getActivity());
pd.setCancelable(false);
pd.getWindow().setGravity(Gravity.CENTER);
pd.show();

            
In onPostExecute
                  

pd.dismiss();

This is where I was wrong. ProgressDialog in AsyncTask does stop the UI thread, which means that the users cannot do anything till AyncTask is complete. However, ProgressDialog doesn't stop processing in the thread.  So immediately after AsyncTask is called, while doInBackground is running, the system will move on to execute searchResults.setAdapter, even through the ProgressDialog is visible on the screen.

Note, arrayCategory has not yet been populated with data by AsyncTask which is still running, hence searchResults.setAdapter either does not populate all the data, or throws IndexOutOfBound exception.

There are 2 ways to handle this.

1. Using Get statement - This converts the AsyncTask into a Synchronous task. The Get statement waits for the AsyncTask to complete and returns what you want to be returned, like arrayCategory, for example. Through Get, you can return anything from the AsyncTask.

The code for the same would be something like this. In onCreateView, the code should look like this.

searchResults = (ListView) myFragmentView.findViewById(R.id.listview_search);

myAsyncTask m= (myAsyncTask) new myAsyncTask().execute(newText);

// The AsyncTask populates the ArrayList arrayCategory with the list of categories that we

// received from the server

try {

     String temp = m.get();

    } catch (InterruptedException e) {

          // TODO Auto-generated catch block

          e.printStackTrace();

    } catch (ExecutionException e) {

         // TODO Auto-generated catch block

         e.printStackTrace();
   }


searchResults.setAdapter(new SearchResultsAdapter(getActivity(),arrayCategory)); 

This way, the thread waits and only proceeds once some value is returned. Note: This is not the recommended way of handling this.

2. Calling searchResults.setAdapter from onPostExecute of AsyncTask.  It is recommended, that instead of using Get, we should put what ever we expect to be done after AsyncTask is complete inside doPostExecute. In this case, we should actually put the call the ListAdapter from within doPostExecute. That way, ListAdapter will only be called once doInBackground is complete and hence all data is populated from JSON into arrayCategory.

No comments:

Post a Comment