Simple use of AsyncTask and ProgressDialog in Android

image

The practice of creating applications that are responsive to user actions suggests that all heavy operations should be performed in a separate thread, informing the user in one way or another about their progress.

Android contains a lot of ways to organize this approach, but one of the most convenient is the use of AsyncTask and ProgressDialog.

This couple perfectly solves the problem, but begins to bring unbearable pain when the amount of Activity with this logic exceeds one, which leads to a repetition of the control code, and even greater pain when the application must support a change in screen orientation.

AsyncTask

For those who are not familiar with AsyncTask, I’ll explain that this is a special abstract class that provides a set of methods for implementation:
  • onPreExecute to host initialization code (UI thread)
  • doInBackground for placing heavy code that will be executed in another thread
  • onProgressUpdate for progress reporting (UI thread)
  • onPostExecute to handle the result returned by doInBackground (UI thread)
and auxiliary methods
  • isCancelled to find out if anyone canceled a task
  • publishProgress to translate the progress message into a UI thread followed by an onProgressUpdate call
The essence of the problem

Using the mentioned classes is not difficult, just a couple of snippets is enough for the code to work as it should and ProgressDialog starts informing about the progress of the task. But, as you know, the devil is in the details, so you just need to change the screen orientation, as the dialogue disappears, as well as the result of a long, but incredibly responsible operation.

The reason is the Activity life cycle: changing the screen orientation is interpreted as a configuration change, which leads to the re-creation of the Activity. You can, of course, disable this mechanism by setting a tag android:configChanges="orientation"for Activity and defining your own code, which, if necessary, will make the necessary changes. But this will be an unreasonable implementation.

The solution is to create a special class for managing the Activity-AsyncTask-ProgressDialog bundle, let's call it AsyncTaskManager.

Activity

So, ideally, our Activity should do only five things (the code from the example project):
  • Create AsyncTaskManager in onCreate Method
        mAsyncTaskManager = new AsyncTaskManager(this, this);
  • Delegate AsyncTaskManager task recovery from state
        mAsyncTaskManager.handleRetainedTask(getLastNonConfigurationInstance());
  • Create a specific task and give it to AsyncTaskManager
        mAsyncTaskManager.setupTask(new Task(getResources()));
  • Delegate AsyncTaskManager saving tasks to state
        return mAsyncTaskManager.retainTask();
  • Handle asynchronous task completion
For the last point and to reduce the connection between Activity and AsyncTaskManager, you can create an interface for notification of completion and implement it:


    public interface OnTaskCompleteListener {
        void onTaskComplete(Task task);
    }

In the parameter of the method, the task whose execution has been completed will be transferred.

AsyncTaskManager

AsyncTaskManager should be responsible for the correct operation of all components, which boils down to a list of the following tasks:
  • Create a dialog during initialization
  • When you receive a task in management, run it
  • Display task status in dialog
  • Disconnect from the task after saving it to the state and reconnect when restoring
  • Cancel the task when canceling the dialog
  • Close dialog at task completion
  • Notify Activity of task completion or cancellation
To communicate with the task, the following interface is sufficient, which should be implemented and transferred to the task:


    public interface IProgressTracker {
        void onProgress(String message);
        void onComplete();
    }

Implementation:


    @Override
    public void onProgress(String message) {
        if (!mProgressDialog.isShowing()) {
            mProgressDialog.show();
        }
        mProgressDialog.setMessage(message);
    }
    @Override
    public void onComplete() {
        mProgressDialog.dismiss();
        mAsyncTask.setProgressTracker(null); 
        mTaskCompleteListener.onTaskComplete(mAsyncTask);
        mAsyncTask = null;
    }

Joining the task:

    mAsyncTask.setProgressTracker(this);

Detach from task:


    mAsyncTask.setProgressTracker(null); 
    mAsyncTask = null;

Cancel dialog:


    @Override
    public void onCancel(DialogInterface dialog) {
        mAsyncTask.setProgressTracker(null); 
        mAsyncTask.cancel(true);
        mTaskCompleteListener.onTaskComplete(mAsyncTask);
        mAsyncTask = null;
    }

AsyncTaskManager acts as a kind of key that connects and disconnects a working task to a possibly recreated instance of Activity. In addition, he takes over and hides the logic of working with ProgressDialog.

AsyncTask


For the task, in addition to implementing the main methods, an implementation of a method is required that will help connect / disconnect it with AsyncTaskManager:


    public void setProgressTracker(IProgressTracker progressTracker) {
        mProgressTracker = progressTracker;
        if (mProgressTracker != null) {
            mProgressTracker.onProgress(mProgressMessage);
            if (mResult != null) {
                mProgressTracker.onComplete();
            }
        }
    }

As can be seen from the above code, the task saves the calculated result and the last progress message, and, depending on the state, calls one or another tracker method (AsyncTaskManager).

Thus, even if the task completes before the Activity is recreated, it will receive a notification about the completion of the task.

Result

Now you can fearlessly twist the phone in your hands - all tasks will be processed correctly.

Using such a manager significantly reduces the amount of code in Activity and allows you to reuse this functionality in a project. I developed and successfully applied this approach in my recent application.

References

Archive with a sample project
Description AsyncTask (en)
Simple work with streams (en)
Dialogs in Android (en)

Also popular now: