Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to manage the ProgressDialog from AsyncTask

I would like to use the AsyncTask for manage some business logic in my application. What is the best pattern for using the onProgressUpdate(...) method of AsyncTask defined in separed files (not as innter class of Activity) I have two ideas:
1. The simplest way: create ProgressDialog in Activity (using onCreateDialog(...) method) and pass reference to my subclass of AsyncTask by constructor (override onProgressUpdate(...) inside my AsyncTask subclass). The disadvantage of this solution is usage of UI components inside business logic code.

FooTask1.java:

public class FooTask1 extends AsyncTask<Void, Integer, Void> {
private ProgressDialog mProgressDialog;

public FooTask1(ProgressDialog progressDialog) {
    super();
    mProgressDialog = progressDialog;
}

@Override
protected Void doInBackground(Void... unused) {
    // time consuming operation
    for (int i=0; i<=100; i++) {
        this.publishProgress(i);
        try {
            Thread.sleep(100);
        } catch (Exception e) {}
    }
    return null;
}

@Override
protected void onProgressUpdate(Integer... progress) {
    mProgressDialog.setProgress(progress[0]);
}

@Override
protected void onPostExecute(Void result) {
    mProgressDialog.dismiss();
}
}

FooActivity1.java:

public class FooActivity1 extends Activity {

  private static final int DIALOG_PROGRESS_ID = 0;
  private ProgressDialog mProgressDialog;

  @Override
  public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);

      showDialog(DIALOG_PROGRESS_ID);
      new FooTask(mProgressDialog).execute();
  }

  @Override
  protected Dialog onCreateDialog(int id) {
      switch(id) {
          case DIALOG_PROGRESS_ID:
             mProgressDialog = new ProgressDialog(this);
             mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
             mProgressDialog.setMessage("Loading...");
             mProgressDialog.setCancelable(false);
             return mProgressDialog;
          default:
             return null;
      }
  }
}

2.The more sophisticated way: override the onProgressUpdate(...) method of AsyncTask inside the Activity class:

FooTask2.java:

public class FooTask2 extends AsyncTask<Void, Integer, Void> {
@Override
protected Void doInBackground(Void... unused) {
    // time consuming operation
    for (int i=0; i<=100; i++) {
        this.publishProgress(i);
        try {
            Thread.sleep(100);
        } catch (Exception e) {}
    }
    return null;
}
}

FooActivity2.java

public class FooActivity2 extends Activity {

private static final int DIALOG_PROGRESS_ID = 0;
private ProgressDialog mProgressDialog;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    showDialog(DIALOG_PROGRESS_ID);
    new FooTaskLoader().execute();
}

@Override
protected Dialog onCreateDialog(int id) {
    switch(id) {
        case DIALOG_PROGRESS_ID:
            mProgressDialog = new ProgressDialog(this);
            mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
            mProgressDialog.setMessage("Loading...");
            mProgressDialog.setCancelable(false);
            return mProgressDialog;
        default:
            return null;
    }
}

private class FooTaskLoader extends FooTask2 {
    @Override
    protected void onProgressUpdate(Integer... progress) {
        mProgressDialog.setProgress(progress[0]);
    }

    @Override
    protected void onPostExecute(Void result) {
        dismissDialog(DIALOG_PROGRESS_ID);
    }
}
}
like image 320
tludek Avatar asked Nov 28 '11 11:11

tludek


2 Answers

I would rather isolate business logic stuff from AsyncTask than isolate AsyncTask from Activity.

In general, AsyncTask has a very specific design and use case in Android application life cycle, that is, run some time consuming task in the background thread, once done, update Activity's view in UI thread. This is why it is always recommended to use it as a inner class of Activity.

A more OO design IMO is isolating and centralizing your business logic into a POJO (for reusability). For testability, you can do something like this:
1. Define an interface IBusinessDAO
2. Define RealBusinessDAO implements IBusinessDAO
3. Define MockBusinessDAO implements IBusinessDAO
4. Call IBusinessDAO.foo(); inside AsyncTask.doInBackground()

For unit-test your business logic, as it is a POJO, you can use purely JUnit write your test case. Sometimes we want to test UI component and we don't really care how underlying business logic is implemented, for instance, my business logic connect to remote http server download some json data, I don't want to do this every time when I just want to test the UI layout, for this situation, I can easily change my Activity use MockBusinessDAO (sort of Spring's DI concept) like this:

public class MyActivity extends Activity {
  IBusinessDAO businessDAO;

  ... ...

  private class MyAsyncTask extends AsyncTask<Void, Void, Void> {
    ... ...        

    protected void doInBackground(Void... params) {
      businessDAO.foo();
    }
  }

  ... ...

  public void onCreate(Bundle savedInstanceState) {
    if (runInTest)
      businessDAO = new MockBusinessDAO();
    else
      businessDAO = new RealBusinessDAO();

    new myAsyncTask().execute();
  }


}

Some advantages of doing these are:
1. AsyncTask implementation is easy and clean (several lines of code in doInBacnground())
2. Business logic implementation is purely POJO, improve reusability.
3. Isolation test business logic and UI component, improve testability.

Hope that help.

like image 121
yorkw Avatar answered Oct 28 '22 05:10

yorkw


  1. Solution number one is probably how I would handle it - That is the way of the Android framework. A twist to this solution (and probably how I would handle it, if the AsyncTask couldn't fit into the Activity-class) I would pass a Context as the parameter instead, and then instantiate and show the ProgressDialog in onPreExecute.

  2. Solution number 2 is basicly the same as creating the dialog as an inner class - so you might aswell do that if you go for this one.

like image 40
kaspermoerch Avatar answered Oct 28 '22 05:10

kaspermoerch