To start an AsyncTask, first create an AsyncTask object and call the execute method. Such as:

new DownloadFilesTask().execute();

Copy the code

DownloadFilesTask inherits from AsyncTask and needs to specify three generic parameters:

private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> { protected void onPreExecute() { super.onPreExecute(); } protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0; for (int i = 0; i < count; i++) { totalSize += Downloader.downloadFile(urls[i]); publishProgress((int) ((i / (float) count) * 100)); // Escape early if cancel() is called if (isCancelled()) break; } return totalSize; } protected void onProgressUpdate(Integer... progress) { setProgressPercent(progress[0]); } protected void onPostExecute(Long result) { showDialog("Downloaded " + result + " bytes"); }}Copy the code

AsyncTask constructor

Let’s start with the AsyncTask constructor:

Public AsyncTask() {mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); Result result = doInBackground(mParams); Binder.flushPendingCommands(); return postResult(result); }}; mFuture = new FutureTask<Result>(mWorker) { @Override protected void done() { try { postResultIfNotInvoked(get()); } catch (InterruptedException e) { android.util.Log.w(LOG_TAG, e); } catch (ExecutionException e) { ... e.getCause()); } catch (CancellationException e) { postResultIfNotInvoked(null); }}}; }Copy the code

Two objects are initialized in the AsyncTask constructor: WorkerRunnable and FutureTask, with values of mWorker and mFuture, respectively. Both object initializations implement two callback methods that are triggered when the user executes the execute method under certain circumstances. Let’s examine these two objects in detail.

Conclusion: doInBackground is executed by callback in WorkerRunnable’s call method

WorkerRunnable mWorker 】 【

Because WorkerRunnable implements the Callable interface, the Call () method must be implemented when the WorkerRunnable is instantiated in the AsyncTask constructor. You also need to specify the generic parameters Params and Result. This will return Result after the call method is called back.

/** WorkerRunable class **/ private static abstrat class WorkerRunable<Params, Result> implements Callable<Result> { Params[] mParams; } /** Callable interface **/ public interface Callable<V> {V call() throws Exception; }Copy the code

For now, we just need to know that the call method calls back to WorkerRunnable when execute.

FutureTask mFuture 】 【

As you can see from the AsyncTask constructor source above, mWorker is passed in as an argument when the FutureTask object is constructed and initialized. The FutureTask constructor also shows that FutureTask requires a Callable object.

Public FutureTask(Callable<V> Callable) {if (Callable == null) throw new NullPointerException();  this.callable = callable; this.state = NEW; // ensure visibility of callable }Copy the code

The WorkerRunnable callback must be called in FutureTask if the WorkerRunnable callback is passed in by FutureTask.

Consider the FutureTask class:

As you can see, FutureTask implements the RunnableFuture interface, which inherits Runnable and Future.

Using the above two classes, you can see that the get method in FutureTask blocks Runnable and returns data that has been executed (generic V).

When we look at FutureTask’s run method, it becomes clear that this is where the WorkerRunnable callback call is called.

Conclusion: The callback method call() in WorkerRunnable is called back after FutureTask’s run method is executed. The WorkerRunnable call method returns data to FutureTask when it completes execution.

Question: Who calls FutureTask’s run method?


Execute (

Obviously, FutureTask’s run method must be executed after AsyncTask executes the execute method.

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); Public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }Copy the code

When you execute the execute method, you’re actually calling the executeOnExecutor method. Two arguments are passed, sDefaultExecutor and params. As you can see from the source code above, sDefaultExecutor is actually a SerialExecutor object. Params actually ends up assigning the doInBackground method, which is how the user implements the callback, as you’ll see later.

executeOnExecutor

So let’s look at the executeOnExecutor method first.

/** Public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) { ... mStatus = Status.RUNNING; onPreExecute(); mWorker.mParams = params; exec.execute(mFuture); return this; }Copy the code

See? In this method, the onPreExecute() method is first executed, which calls back to the implementation method onPreExecute for user-defined AsyncTask (see the code for the first example at the top of this article). OnPreExecute can be used to prepare background tasks for execution.


As you can see from the above code, exec. Execute (mFuture) passes in an mFuture, which is the FutureTask that the AsyncTask construct initialized with the assignment. Exec is simply the argument sDefaultExecutor passed in the asynctask.execute () method, which is the SerialExecutor object. Well, exec is the SerialExecutor object.

SerialExecutor

We can see that when exec.execute(mFuture) was executed earlier, SerialExecutor executed the execute method with FutureTask as an argument. In SerialExecutor’s execute method, FutureTask is inserted into the queue by a task queue called mTasks. Run is actually FutureTask’s run method, because the r argument passed in is the mFuture.

/** Private static class SerialExecutor implements Executor {final; /** Private static class SerialExecutor implements Executor 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); }}}Copy the code

Conclusion: FutureTask’s run method is executed in SerialExecutor’s Execute method.

In this case, SerialExecutor implements a queueing algorithm for tasks. When an AsyncTask task starts execute, the Run method of FutureTask is executed first, then the scheduleNext method is finally executed, and then the Execute method of THREAD_POOL_EXECUTOR is executed. If mActive is empty, the scheduleNext method is called to execute the next AsyncTask. Thread_pool_executor.execute (mActive) continues. Thus SerialExecutor implements a serial task queue.

Conclusion: SerialExecutor only implements the task queue; THREAD_POOL_EXECUTOR actually executes the thread pool.


As we have already seen, FutureTask’s run method is executed in SerialExecutor’s execute method, which is then encapsulated by mtasks. offer as a Runnable(mActive) to THREAD_POOL_EXECUTOR.

public static final Executor THREAD_POOL_EXECUTOR
        = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

Copy the code

At this point, FutureTask’s Run method starts calling back to WorkerRunable’s call method. Are you starting to faint? 2333. Shored up!!!!!!!!!! Next we’ll look at WorkerRunable’s callback call method: Oh my God! It’s finally time to call back to the doInBackground method. See, it’s now clear that doInBackground is not executed in the main thread.

mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); Result result = doInBackground(mParams); Binder.flushPendingCommands(); return postResult(result); }};Copy the code

DoInBackground (mParams) returns result, and continues with the postResult method, passing the result as an argument. Now let’s look at the postResult method, bam, bam, bam, Handler. So we’re sending a message to this Handler right after doInBackground, which Handler is that?

private Result postResult(Result result) {

    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}

Copy the code

Continue with the getHandler method:

private static Handler getHandler() {
    synchronized (AsyncTask.class) {
        if (sHandler == null) {
            sHandler = new InternalHandler();
        }
        return sHandler;
    }
}

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

    @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

When postResult was executed, obtainMessage passed the following argument: MESSAGE_POST_RESULT and AsyncTaskResult(this, result)), then message.sendtoTarget () starts sending the message and processing the message in InternalHandler’s handleMessage.

Conclusion: doInBackground sends a message to InternalHandler immediately after execution

private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; } private static class AsyncTaskResult<Data> { final AsyncTask mTask; final Data[] mData; AsyncTaskResult(AsyncTask task, Data... data) { mTask = task; mData = data; }}Copy the code

Since MESSAGE_POST_RESULT is passed, the mtask.finish method is executed. At this point, the doInBackground is done and results are returned, so the onPostExecute method is executed and the result is passed in. Since InternalHandler is obtained via looper.getMainLooper (), it is on the main thread, so finally the onPostExecute method returns to the main thread. Now onPostExecute can get the result that doInBackground asynchronous execution returns and update the UI directly.

Conclusion: An AsyncTask task ends when InternalHandler executes the onPostExecute callback after receiving the message