Android AsyncTask

Keywords: Mobile Android Java Google Programming

Android AsyncTask

Content partition

  • Introduction to AsyncTask
  • Simple use
  • Analysis of Complex Parts and Source Codes
  • Some pits

Introduction to AsyncTask

AsyncTask enables proper and easy use of the UI thread. This class allows you to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.

AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent package such as Executor, ThreadPoolExecutor and FutureTask.
This is an introduction to AsyncTask in Google Android development documents. It probably means that AsyncTask is designed as an auxiliary class for Thread and Handle. It mainly makes it easy for developers to use UI Thread and background Thread operations (such as downloading files in background threads and updating download progress in UI threads). At the same time, this is not a general multi-threaded programming framework. It is designed for tasks that can return results in up to a few seconds.

Simple use

Here we simulate a background download of some files and display a Progress Dialog in the user interface to show the download progress.

/**
 * author: zyinux
 * date: on 2018/10/26
 */
public class DownloadTask extends AsyncTask<String,Integer,Boolean> {

    ProgressDialog progressDialog;

    Context context;

    public DownloadTask(Context context) {
        this.context=context;

    }

    @Override
    protected void onPreExecute() {
        progressDialog=new ProgressDialog(context);
        progressDialog.setMax(100);
    // progressDialog.setCancelable(false);
    //Notice here that I commented out the previous line of code so that dialog can be cancelled, and why I did so is explained later.
        progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        progressDialog.show();
    }

    @Override
    protected Boolean doInBackground(String... strings) {
        int pro=0;

        while (true){
            pro++;
            publishProgress(pro);
            if (pro>=100){
                break;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        return true;
    }

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

    @Override
    protected void onPostExecute(Boolean aBoolean) {
        progressDialog.dismiss();
        if (aBoolean){
            Toast.makeText(context,"Download successful",Toast.LENGTH_SHORT).show();
        }else {
            Toast.makeText(context,"Download failed",Toast.LENGTH_SHORT).show();
        }
    }
}

Here is the main code invoked in Activity

new DownloadTask(this).execute("testurl");
//It's very simple to use. After new, execute is executed to pass in the execution parameters.

The operation effect is shown in the figure.

Very common effect, the following analysis of the above code
First is

public class DownloadTask extends AsyncTask<String,Integer,Boolean>

The three types in this line of generic brackets correspond to three

  • Types of parameters sent to tasks when Params executes
  • Types of progress in the Progress background execution process
  • Type of Result Return Value
    When these parameters are not required, they can be set to <Void, Void, Void>
    Then there are several main ways to achieve it.
protected void onPreExecute() {}
protected Boolean doInBackground(String... strings) {}
protected void onProgressUpdate(Integer... values) {}
protected void onPostExecute(Boolean aBoolean) { }
  • The onPreExecute method executes on the UI thread and is called before doing background tasks, where some initialization operations can be performed, such as the display Dialog above.
  • The doInBackground change method executes in the background thread, where the time-consuming operations in the task should be executed. A thread pool is maintained in AsyncTask to optimize the method call. The parameter type of this method is the Params set above, which is the parameter passed from execute in the execution call code. Within this method, the publishProgress method can be called to pass the current progress.
  • The onProgressUpdate method is called after the publishProgress method, which runs on the UI Thread, so you can do UI updates here, such as updating the progress of the ProgressDialog. The type of parameter passed here is Progress above.
  • The onPostExecute executes the doInBackground method after it is executed, also running on UI Thread. The incoming parameter is the return value of the doInBackground method, which is specified by the Result above.

Analysis of Complex Parts and Source Codes

The above explains the use of AsyncTask. Careful little buddies may have noticed these two lines of code above me.

// progressDialog.setCancelable(false);
//Notice here that I commented out the previous line of code so that dialog can be cancelled, and why I did so is explained later.

Now let's explain why this is so. Suppose we run app and run Download Task, and then a progress box pops up on the screen. So far, nothing has gone wrong. At this point, when we click on the rest of the screen, the progress box will be cancelled, and then we execute Download Task again. Guess what happens now, buddies?

Because it's not easy to record and transfer gif diagrams, I'll just say what happens here: the progress bar will pop up again, which is no problem, but the progress bar will stay at 0%, until after a period of time, the Toast display shows that the download is complete, then the progress bar starts to increase slowly, and when it reaches 100%, the Toast prompt will pop up again to complete the download.

Why is that?

First of all, we know that canceling dialog does not cancel AsyncTask, so when download Task is executed again, two AsyncTask tasks are executed. This raises the question of whether multiple AsyncTasks are executed serially or in parallel.

Serial or parallel?

First of all, the default is serial. Why? Let's look at the source code.
When executing new Download Task (this). execute ("testurl"); after:

    @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

    //Then continue to look.

    @MainThread
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        //Look here until the onPreExecute method must be called before the doInBackground method, and it's in UI Thread
        onPreExecute();
        /**
        *Specific execution method here until this exec is the sDefaultExecutor passed in from the previous step 
        *This is a framework for managing thread pools
        */
        exec.execute(mFuture);
        return this;
    }

    //Where initialization occurs, the emphasis here is that initialization to static final is a class static variable that is shared by class instances.
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();    

    //Keep looking at this Serial Executor
    private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

    //Save the task in Array Deque, which is a FIFO queue, and finally execute each task in the queue. So when multiple AsyncTask s are executed, they are executed serially.
As mentioned above, the general situation at this time, what about the special situation?
DownloadTask task=new DownloadTask(this);
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

At this time, multiple tasks are not necessarily queued in order to execute, the specific order of execution depends on the system's scheduling of threads, small partners can test and see for themselves. However, this is not recommended unless you have special needs.

Some pits

On cancel method

public final boolean cancel(boolean mayInterruptIfRunning) {}

The incoming parameters indicate whether the current task can be cancelled when it is executed. But when your doInBackground method performs a loop or an IO stream read-write task, even if you pass in true, the change cannot cancel the task. The difference is that after calling this method, the onCancelled method will be called when the doInBackground execution is completed, not the onPostExecute method, so cancel cannot guarantee that the task can be cancelled.

Memory leak

The above listing code passes in a context from Activity. AsyncTask's life cycle is irrelevant to Activeness, so when Activity is finish ed, AsyncTask still exists, and he holds a reference to Activity, which makes Activity unable to be garbage collected.
At the same time, if AsyncTask is implemented by using non-static anonymous inner classes, because of the characteristics of Java inner classes, it will also hold the reference of current Activity, resulting in memory leaks.

Posted by etoast on Mon, 21 Jan 2019 13:12:12 -0800