preface

The basic knowledge of AsyncTask has been introduced in the previous section in-depth Interpretation of AsyncTask (part 1). This section will analyze the working principle of AsyncTask in depth from the source code.


1 AsyncTask object creation

When AsyncTask is used, it inherits the AsyncTask class based on the specific task, for example:

private class MyAsyncTask extends AsyncTask<String, Integer, String> {

        protected String doInBackground(String... args1) {
		
            Log.i(TAG, "doInBackground in:" + args1[0]);
            int times = 10;
            for (int i = 0; i < times; i++) { publishProgress(i); // After the commit, the onProcessUpdate method is executed} log. I (TAG,"doInBackground out");
            return "over"; } /** * is executed here after calling the cancel method */ protected voidonCancelled() {
            Log.i(TAG, "onCancelled"); * * *} / indo*/ protected void onPostExecute(String args3) {log. I (TAG,"onPostExecute:"+ args3); * * *} / indo*/ @override protected void before InBackgroundonPreExecute() {
            Log.i(TAG, "onPreExecute");
        }

        /**       
         * @param args2
         */
        @Override
        protected void onProgressUpdate(Integer... args2) {
            Log.i(TAG, "onProgressUpdate:"+ args2[0]); }}Copy the code

The MyAsyncTask object must be created in the main thread as follows:

new MyAsyncTask().execute("AsyncTask Test");
Copy the code

2 the execute method

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

The execute method calls the executeOnExecutor method and passes the parameters sDefaultExecutor and params. Now look at the executeOnExecutor method:

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... Params) {// Determine the current stateif(mStatus ! = Status.PENDING) { switch (mStatus) {case RUNNING:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task is already running.");
                case FINISHED:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task has already been executed "
                            + "(a task can be executed only once)"); }} mStatus = status.running; OnPreExecute (); onPreExecute(); // Pass the argument to mWorker mworker. mParams = params; // Call the thread pool to execute exec. Execute (mFuture);return this;
}
Copy the code

The executeOnExecutor method first determines the state, and if it is executable, sets the state to RUNNING. Then the onPreExecute method is called to prepare the task for the user. The core is exec. Execute (mFuture). The exec sDefaultExecutor namely.


3 sDefaultExecutor Serial thread pool

Check out the sDefaultExecutor definition:

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
Copy the code

SDefaultExecutor is a serial thread pool for queueing tasks. The source code is as follows:

	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); }}}Copy the code

As you can see from the SerialExecutor source code, an mFuture is an object inserted into the mTasks task queue. When there are no active AsyncTask tasks in mTasks, the scheduleNext method is called to execute the next task. If one AsyncTask task is complete, the next AsyncTask task is continued until all tasks are complete. Analysis shows that it is the thread pool THREAD_POOL_EXECUTOR that actually performs the background tasks.


4 Thread pool THREAD_POOL_EXECUTOR

THREAD_POOL_EXECUTOR is defined as follows:

	public static final Executor THREAD_POOL_EXECUTOR;

    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }
Copy the code

5 mFuture concurrent execution object

The Runnable object executed in thread pool THREAD_POOL_EXECUTOR is mFuture.

Definition of mFuture:

private final FutureTask<Result> mFuture;
Copy the code

What does the mFuture actually do for the thread pool?

Let’s look at the construction of AsyncTask:

public AsyncTask(@Nullable Looper callbackLooper) { mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper() ? getMainHandler() : new Handler(callbackLooper); Mworker = new WorkerRunnable<Params, Result>() {public Result Call () throws Exception {mTaskInvoked. Set (true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);
                }
                returnresult; }}; 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

It can be seen from the construction of AsyncTask that mWorker is the constructor parameter of FutureTask, so the FutureTask object holds a reference to mWorker.

Constructors for FutureTask:

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

FutureTask’s run method:

 public void run() {
        if(state ! = NEW || ! U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))return;
        try {
            Callable<V> c = callable;
            if(c ! = null && state == NEW) { V result; boolean ran; try { result = c.call(); ran =true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if(s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); }}Copy the code

FutureTask’s run method calls result = c.call(); That is, mWorker’s call method is called.


6 mWorker object

MWorker’s call method:

MWorker = new WorkerRunnable<Params, Result>() {// Call method public Result Call () throws Exception {mTaskInvoked. Set (true); Result result = null; try { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); // NoInspection unchecked // Execute tasks result =doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);
                }
                returnresult; }};Copy the code

It can be seen from the code that mWorker’s call method is finally executed in the thread pool, and doInBackground method is called in the call method, so it can be seen that doInBackground method is called in the thread pool. The postResult method is called when the task is complete:

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

The postResult method sends MESSAGE_POST_RESULT message and result.


7 InternalHandler Receives processing messages

InternalHandler code:

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

        @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

InternalHandler calls result.mtask. finish(result.mdata [0]) when it receives MESSAGE_POST_RESULT;


8 Finish

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

The Finish method calls onPostExecute to send the result if the task is not cancelled, and onCancelled if the task is cancelled. The Finish method is executed in the main thread.


9 onProgressUpdate

The onPreExecute, doInBackground, and onPostExecute methods are found in sequence. First look at the publishProgress method:

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

The MyAsyncTask example calls the publishProgress method in doInBackground, which sends the MESSAGE_POST_PROGRESS message and progress values, InternalHandler calls the onProgressUpdate method on receipt of the MESSAGE_POST_PROGRESS message. So onProgressUpdate is also called in the main thread.


10 summary

Through the above step by step source code analysis process, you have mastered the execution process of AsyncTask task. AsyncTask has two thread pools serial thread pool sDefaultExecutor and thread pool THREAD_POOL_EXECUTOR. SDefaultExecutor is used to queue tasks, and THREAD_POOL_EXECUTOR actually performs the task. Thread switching is implemented using InternalHandler.


11 Answers to questions

1) Why are asyncTasks created and executed on the main thread? Because AsyncTask needs to create InternalHandler in the main thread, onProgressUpdate, onPostExecute, onCancelled can update UI normally.

2) Why AsyncTask is not suitable for very time-consuming tasks? AsyncTask is actually a thread pool. If a thread is occupied for a long time and is not idle, other threads can only wait, which causes blocking.

If an AsyncTask is declared as a non-static inner class of an Activity, then an AsyncTask retains a reference to the Activity that created the AsyncTask. If the Activity has been destroyed and the background AsyncTask thread is still executing, it will continue to hold the reference in memory, making the Activity unable to be reclaimed and causing a memory leak.