Leopik Leopik - 1 month ago 10
Android Question

Getting result from asynctask and handling it

I saw many questions about getting results from async task, but none of them solved my problem. The thing I want to do is get from async task array of MyClass objects and use them in UI, but in different situations I have different UI. Sometimes I fill recyclerview with my array, sometimes I fill endless tabbed activity with my array. This means that I cant pass my UI element to Async task in order to handle it in

onPostExecute()
. The result I want to get from Async task is exactly array of MyClass objects and then in different situations work with it in different ways. And also one important thing is that I should fill my UI only after AsyncTask executes (because if it's still executing I have no data to fill UI with), so I can't simply write something like

MyClass[] result = new MyAsyncTask().execute().getResult()
MyUI.fillWith(result)


because most likely I will get NPE.

So how can I get my result back as array of MyClass objects and fill my UI with it only after I get the result (So in UI thread I should get info that my asynctask did all the work and only after that I can fill UI with my data. And of course I dont want to block my UI thread)?

Handling UI in
onPostExecute()
won't solve my problem because of reasons I wrote earlier. Covering AsyncTask with interface also doesnt seem like something I need. And I saw solution with unique token for AsyncTask but I didnt get how that works.

Answer

Rather than passing your UI element directly to the AsyncTask, create an interface representing the action to take once the task is complete:

public interface MyTaskHandler {
  void onComplete(MyClass[] results);
}

And make your task accept this interface and call it in onPostExecute:

public class MyTask extends AsyncTask<..., ..., MyClass> {
  private MyTaskHandler myTaskHandler;

  public MyTask(MyTaskHandler myTaskHandler) { 
    this.myTaskHandler = myTaskHandler;
  }

  @Override
  public void onPostExecute(MyClass[] results) {
    myTaskHandler.onComplete(results);
  }
}

(in the real implementation you will need to use WeakReferences or null out the reference when your activity is destroyed, just as you would do with views since your handler will generate have a reference to some view)

Then you can pass different implementations of MyTaskHandler based on what you want to do:

public class MyActivity extends Activity {
  @Override
  public void onCreate(Bundle savedInstanceState) {
    new MyAsyncTask(new MyTaskHandler() {
      @Override
      public void onComplete(MyClass[] results) {
        // load into list view
      }
    }).execute();

    new MyAsyncTask(new MyTaskHandler() {
      @Override
      public void onComplete(MyClass[] results) {
        // load into tabs
      }
    }).execute();
  }
}
Comments