preface

Handler: Post (Runnable R) : Handler: Post (Runnable R) : Post (Runnable R) However, in the use of Handler, ignoring the problem of memory leakage, both the amount of code and the level of understanding are not satisfactory, so Google officially helped us to encapsulate AsyncTask on the basis of Handler. However, there are many details to pay attention to when using AsyncTask. What are its advantages? Or take a look at the source code to find out.

How to use

For a simple example of how to use AsyncTask asynchronously with a UI thread, start by creating a class that inherits AsyncTask and passing in the constructor to the components we want to operate on (ProgressBar and TextView).

class MAsyncTask extends AsyncTask<Void, Integer, String>{
        private ProgressBar mProgressBar;
        private TextView mTextView;

        public MAsyncTask(ProgressBar mProgressBar, TextView mTextView) {
            this.mProgressBar = mProgressBar;
            this.mTextView = mTextView;
        }

        @Override
        protected void onPreExecute() {
            mTextView.setText("Commence execution");
            super.onPreExecute();
        }

        @Override
        protected String doInBackground(Void... params) {
            for(int i = 0; i <= 100; i++){ publishProgress(i); // This line corresponds to the following onProgressUpdate method try {thread.sleep (100); } catch (InterruptedException e) {e.printStackTrace(); }}return "Executed"; } @Override protected void onProgressUpdate(Integer... values) { mProgressBar.setProgress(values[0]); super.onProgressUpdate(values); } @Override protected void onPostExecute(String s) { mTextView.setText(s); super.onPostExecute(s); }}Copy the code

Create our new MAsyncTask instance in the Activity and execute (extraneous code omitted) :

public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { ...  MAsyncTask asyncTask = new MAsyncTask(mTestPB, mTestTV); asyncTask.execute(); // Start executing... }}Copy the code

Look at the principle of

In the above code, we opened a single thread to perform a simple asynchronous UI update. Now let’s see how AsyncTask is implemented, starting with the constructor:

public abstract class AsyncTask<Params, Progress, Result> 
Copy the code

AsyncTask is an abstract class and has three generics. I think these three generics are the root of many users’ confusion:

  • Params: argument, passed in execute(), variable length argument, followed by doInBackground(Void… Params) params are of the same type here, and I don’t have a parameter here, so I can set this generic to Void
  • Progress: indicates the Progress of the execution, similar to onProgressUpdate(Integer… Values are of the same type, generally Integer
  • Result: The returned value is the same as the parameter type returned by String doInBackground and the s parameter of onPostExecute(String s). The value is called after the time-consuming operation is completed. I’m doing this and I’m returning a String, so String

AsyncTask: AsyncTask: AsyncTask: AsyncTask: AsyncTask: AsyncTask: AsyncTask: AsyncTask: AsyncTask: AsyncTask: AsyncTask: AsyncTask: AsyncTask: AsyncTask

@WorkerThread
protected abstract Result doInBackground(Params... params);
Copy the code

As the name implies, this method is executed in the background, i.e. in a child thread, which needs to be subclassed. In this method we can call publishProgress to send progress to the UI thread and receive it in the onProgressUpdate method.

Depending on the order of call, we generally override these methods:

/ / indoCall before InBackground, @mainThread protected void inside the UI threadonPreExecute() {} @mainThread protected void onProgressUpdate(Progress...) {} @mainThread protected void onProgressUpdate(Progress... Values) {} // Yesdo@mainThread protected void onPostExecute(Result Result) {}Copy the code

Let’s see how the publishProgress method calls the onProgressUpdate method:

@WorkerThread
protected final void publishProgress(Progress... values) {
    if (!isCancelled()) {
        getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult<Progress>(this, values)).sendToTarget();
    }
}
Copy the code

Using obtainMessage to avoid duplicate message creation, call getHandler() and send the message, here is a singleton

    private static Handler getHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler();
            }
            returnsHandler; }}Copy the code

An InternalHandler is returned:

    private static class InternalHandler extends Handler {
        public InternalHandler() {
            super(Looper.getMainLooper());
        }

        @SuppressWarnings({"unchecked"."RawUseOfParameterizedType"}) @Override public void handleMessage(Message msg) { AsyncTaskResult<? > result = (AsyncTaskResult<? >) msg.obj; switch (msg.what) {case MESSAGE_POST_RESULT:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break; }}}Copy the code

After identifying the message as MESSAGE_POST_PROGRESS, we realize that the Handler is called internally to implement all of this, including the finish method at the end of execution, which we’ll discuss later. To take a look at the AsyncTask execution process from the beginning, go to the execute method:

/** This method must be invoked on the UI thread. */ @mainThread public final AsyncTask<Params, Progress, Result> execute(Params... params) {return executeOnExecutor(sDefaultExecutor, params);
}
Copy the code

Attention! This method must be called in the UI thread, so we won’t test it here. Here we call executeOnExecutor again:

@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params) {
    ......

    onPreExecute();

    mWorker.mParams = params;
    exec.execute(mFuture);

    return this;
}
Copy the code

In the UI line, we call onPreExecute() and assign the parameter passed to mworker.mparams, then call exec’s execute method and pass mFuture as the parameter. Here we have three objects: SDefaultExecutor (passed in executeOnExecutor), mWorker, and mFuture to see where their assignments are:

SDefaultExecutor:

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; . public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); . private static class SerialExecutor implements Executor { final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>(); Runnable mActive; public synchronized void execute(final Runnable r) { mTasks.offer(newRunnable() {
           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); }}}Copy the code

We found that the default assignment for sDefaultExecutor is SERIAL_EXECUTOR, which is a sequential thread pool with an internal implementation of a task queue.

mWorker

private final WorkerRunnable<Params, Result> mWorker;

public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                Result result = doInBackground(mParams);
                Binder.flushPendingCommands();
                returnpostResult(result); }}; . } private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { Params[] mParams; }Copy the code

In the constructor of AsyncTask, the mWorker is assigned a Callable (a thread with a return parameter), the doInBackground method is executed in the call method, and the postResult method is called

mFuture

private final FutureTask<Result> mFuture;
public AsyncTask() {... mFuture = new FutureTask<Result>(mWorker) { @Override protected voiddone() {
                try {
                    postResultIfNotInvoked(get());
                } catch (InterruptedException e) {
                    android.util.Log.w(LOG_TAG, e);
                } catch (ExecutionException e) {
                    throw new RuntimeException("An error occurred while executing doInBackground()", e.getCause()); } catch (CancellationException e) { postResultIfNotInvoked(null); }}}; }Copy the code

The mFuture is of type FutureTask and the mWorker is passed in. After the mWorker is executed, the postResultIfNotInvoked method is invoked.

private void postResultIfNotInvoked(Result result) {
    final boolean wasTaskInvoked = mTaskInvoked.get();
    if (!wasTaskInvoked) {
        postResult(result);
    }
}
Copy the code

PostResult: postResult: postResult: postResult: postResult: postResult: postResult: postResult: postResult: postResult: postResult: postResult: postResult: postResult Take a look at the postResult method:

    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }
Copy the code

Again, the familiar obtainMessage and sendToTarget messages are sent, this time with MESSAGE_POST_RESULT. Let’s look at InternalHandler’s handleMessage method we mentioned earlier:

public void handleMessage(Message msg) { AsyncTaskResult<? > result = (AsyncTaskResult<? >) msg.obj; switch (msg.what) {case MESSAGE_POST_RESULT:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break; }}Copy the code

Finally, result.mtask. finish is called based on the message type, and the result type is AsyncTaskResult:

private static class AsyncTaskResult<Data> { final AsyncTask mTask; final Data[] mData; AsyncTaskResult(AsyncTask task, Data... data) { mTask = task; mData = data; }}Copy the code

MTask is of type AsyncTask, find the AsyncTask finish method:

    private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }
Copy the code

And then finally if it’s not canceled it calls onPostExecute, which is the method that we overwrote before, after it’s done executing, and it’s also in the child thread.

Multithreaded concurrency

As mentioned in the beginning, AsyncTask is essentially the encapsulation of Handler. There are corresponding methods before, during and after execution, and it is easy to use. However, this is not the biggest advantage of AsyncTask. You’ve already seen in the code that AsyncTask has its own thread pool maintained within it, which is SERIAL_EXECUTOR by default

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
Copy the code

Sequential execution, where one task is executed before the next, also provides a thread pool that supports concurrency:

Private static final int CPU_COUNT = Runtime.getruntime (). AvailableProcessors (); Private static final int CORE_POOL_SIZE = CPU_COUNT + 1; Private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; Private static final int KEEP_ALIVE = 1; private static final int KEEP_ALIVE = 1; Private static Final ThreadFactory sThreadFactory = newThreadFactory() {
    private final AtomicInteger mCount = new AtomicInteger(1);

    public Thread newThread(Runnable r) {
        return new Thread(r, "AsyncTask #"+ mCount.getAndIncrement()); }}; // Block queue, Private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128);  public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);Copy the code

A thread pool that is optimized for the number of cpus in the pool is declared static.

for(int i = 0; i < 100; MAsyncTask asyncTask = new MAsyncTask(mTestPB, mTestTV); asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }Copy the code

In executeOnExecutor we can also pass in our own custom thread pool:

/ / the same as the default in order to execute asyncTask. ExecuteOnExecutor (Executors. NewSingleThreadExecutor ()); / / unlimited Executor asyncTask. ExecuteOnExecutor (Executors. NewCachedThreadPool ()); / / at the same time perform the Executor of the 10 asyncTask. ExecuteOnExecutor (Executors. NewFixedThreadPool (10));Copy the code

conclusion

AsyncTask is indeed very simple and convenient to use, and it is also the internal message mechanism of Android, and it quickly realizes asynchronous UI update. Especially, it can also perform well when multi-threading is used, which is not available when we use Handler alone. However, pay attention to the call order and call timing of internal methods in the process of use. For example asynctask.execute () is called in the main UI thread, not in child threads, and is used to determine which thread pool to use.