AsyncTask implementation framework – to be independent from Activity –

Continuing from AsyncTask usage summary. Interest is how to establish independence between Activity and AsyncTask? The answer is well summarized in the article, Android AsyncTask はバックグラウンドの処理に集中すべし!”. I will just translate and re-summarize this article. 3 ways will be introduced. 

Advance – implementation framework of AsyncTask

A. Define as inner class of Activity

Most intuitive, easy and faster way is to implement your AsyncTask inside Activity which use it. It is nice choice when CustomAsyncTask is specific (highly dependent on) to the parent Activity.

Mock implementation is like below, here text of mEditText is updated after AsyncTask’s background process.

public class SomeActivity extends Activity {

    private static final String TAG = SomeActivity.class.getSimpleName();
    private EditText mEditText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mEditText = (EditText) findViewById(R.id.edit_text);

        (new CustomAsyncTask()).execute();
    }

    public class CustomAsyncTask extends AsyncTask<Void, Void, String> {

        @Override
        protected String doInBackground(Void... params) {
            String result = null;

            // background process

            return result;
        }

        protected void onPostExecute(String result) {
            // Update Activity UI here
            mEditText.setText(result);
        }
    }
}

The problem is that member of Activity, mEditText, is used inside onPostExecute() method of CustomAsyncTask. So this is basically only applicable for this Activity. To separate the work of common AsyncTask part and Activity’s UI update part, we can take following methods. One way is to override methods at Activity, the other way is to implement custom callback.

B. Override UI thread task at Activity side

Consider like this. AsyncTask should concentrate on background task, Activity side will do all UI update task. We can achieve it by overriding UI thread task (onPreExecute, onProgressUpdate and onPostExecute) at Activity. 

Mock implementation 

public class SomeActivity extends Activity {

    private static final String TAG = SomeActivity.class.getSimpleName();
    private EditText mEditText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mEditText = (EditText) findViewById(R.id.edit_text);

        new CustomAsyncTask(){
            // Override UI thread methods in Activity 
            @Override
            protected void onPostExecute(String result) {
                // Update Activity UI here
                mEditText.setText(result);
            }
        }.execute();
    }
}
    public class CustomAsyncTask extends AsyncTask<Void, Void, String> {

        @Override
        protected String doInBackground(Void... params) {
            String result = null;

            // background process

            return result;
        }
    }

As you can see, CustomAsyncTask only implements doInBackground() method. All other UI update method is implemented in Activity (caller) side. It is a tricky implementation, but it enables us to divide the role of AsyncTask and Activity. 

One concern is it has less encapsulation (basically, Activity side can do any UI update based on the result). No common behavior of onPostExecute() is implemented in AsyncTask side, This implementaton may differ among Activities.

C. Register callback to invoke Activity method

The last, most organized way to make AsyncTask independent from Activity is to implement callback. We can reduce dependency between Activity and AsyncTask, but you need to take care about designing callback method types. 

Callback methods are implemented as interface in below mock implementation.

import android.os.AsyncTask;

/**
 * AsyncTask<Params, Progress, Result>
 *     Params:   Input parameter type
 *        - arg of {@link #doInBackground}
 *
 *     Progress: Progress parameter type
 *        - arg of {@link #onProgressUpdate}
 *
 *     Result:   Return parameter from background process
 *        - return type of {@link #doInBackground}
 *        - arg of {@link #onPostExecute}
 */
public class CustomAsyncTask extends AsyncTask<Void, Void, String> {

    private static final String TAG = CustomAsyncTask.class.getSimpleName();
    private CustomAsyncTaskCallback mCallback = null;

    public CustomAsyncTask(CustomAsyncTaskCallback callback) {
        mCallback = callback;
    }

    @Override
    protected void onPreExecute() {
        // Initial UI set up here (if necessary)

        if(mCallback != null){
            mCallback.onPreExecute();
        }
    }

    @Override
    protected String doInBackground(Void... params) {
        String result = null;
        // Implement (maybe long) background process here!
        return result;
    }

    @Override
    protected void onProgressUpdate(Void... values) {
        // invoked when publishProgress() is called,
    }

    @Override
    protected void onPostExecute(String result) {
        // Finally, the result of doInBackground is handled on UI thread here.

        if(mCallback != null){
            mCallback.onPostExecute(result);
        }
    }

    /** Interface for Activity callback */
    public interface CustomAsyncTaskCallback {
        void onPreExecute();
        void onPostExecute(String result);
        /* Add other callback method if necessary */
        // void onProgressUpdate();
    }
}

To use this callbacks at Activity side, Activity can implement this callback interface. Then, we can implement the function of this callback at Activity side to update UI.  

public class SomeActivity extends Activity implements CustomAsyncTask.CustomAsyncTaskCallback {

    private static final String TAG = SomeActivity.class.getSimpleName();

    private EditText mEditText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mEditText = (EditText) findViewById(R.id.edit_text);

        new CustomAsyncTask(this).execute();
    }

    /** CustomAsyncTaskCallback interface implementation */
    @Override
    public void onPreExecute() {
        // This callback is invoked from CustomAsyncTask.onPreExecute()
    }

    /** CustomAsyncTaskCallback interface implementation */
    @Override
    public void onPostExecute(String result) {
        // This callback is invoked from CustomAsyncTask.onPostExecute()
        mEditText.setText(result);
    }
}

Summary table

 A. Inner class implementationB. Override UI thread methodsC. Callback implementation
Independent from Activity?
ProsEasyFastFasterFlexibleMost organized
ConsThe AsyncTask is only for parent ActivityTricky Haphazard Strict design is neededTroublesome

Conclusion – which way to use?

  • Implementation speed is more important. (First implementation, debugging etc)
  • It is ok that the CustomAsyncTask is only for specific Activity
    → A. Implement as inner class of Activity
  • Personal, small team project.
  • Faster, more flexible, unplanned implementation.
    → B. Override UI thread task from Activity side
  • Official, team project.
  • Specification is designed concretely.
    → C. Use callback at Activity side

* This is just a personal opinion. Please find yourself which method to adopt for your project.

Reference

Leave a Comment

Your email address will not be published. Required fields are marked *