Android source code series five: AsyncTask source code analysis

Android source code series five: AsyncTask source code analysis

AsyncTask is designed to be a helper class around Threadand Handlerand does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.)

AsyncTaskIt is used to help us better use ThreadandHandler , best treatment for a short time asynchronous tasks.

Use AsyncTask to solve asynchronous problems

// AsyncTask 
class MyAsyncTask(val name: String) : AsyncTask<Params, Progress, Result>() {

   // Visible 
    override fun onPreExecute() {
        Log.d("async", "onPreExecute")
    }

   // onPreExecute() 
    override fun doInBackground(vararg params: String?): Any? {
        Log.d("async", "$name execute")
        Thread.sleep(1000)
        publishProgress(1)
        return null
    }

   // publishProgress() 
    override fun onProgressUpdate(vararg values: Int?) {
        Log.d("async", "progress is: $values")
    }
    
   // 
    override fun onPostExecute(result: Any?) {
        Log.d("async", "onPostExecute")
    }
}

// 
MyAsyncTask("one").execute("")
MyAsyncTask("two").execute("")
MyAsyncTask("three").execute("")
MyAsyncTask("four").execute("")

// 
19:29:01.472 1786-1805/com.taonce D/async: one execute
19:29:02.514 1786-1832/com.taonce D/async: two execute
19:29:03.868 1786-1833/com.taonce D/async: three execute
19:29:04.871 1786-1833/com.taonce D/async: four execute
 

AsyncTaskIt must be instantiated by a subclass before it can be used. The methods that must be implemented are:, The doInBackground(vararg params: String?): Any?other three methods are optional. The specific explanations are explained in the code above. There are three generic parameters left, which are explained one by one below:

  • Params: Information needed to perform the task;
  • Progress: The progress released to the outside world during the execution of the task;
  • Result: The result after the task is completed.

So far, have you found a phenomenon? In the information printed in the result, each log is separated by 1s. Take a closer look to see if it is not. I will leave a pit for the time being. I will analyze it for everyone later. Start to enter the source code analysis below:

Constructor

1. simply analyze the code in the constructor:

public AsyncTask(@Nullable Looper callbackLooper) {
   // mHandler callbackLooper sMainLooper getMainHandler() Handler
   // callbackLooper
    mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
        ? getMainHandler()
        : new Handler(callbackLooper);
   // WorkerRunnable Callable call() 
    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
            ...
        }
    };
   // FutureTask mWorker 
    mFuture = new FutureTask<Result>(mWorker) {
        @Override
        protected void done() {
            ...
        }
    };
}
 

mWorkerAnd mFuturetemporarily analysis, until the call of our combined source, at present, we need to know initialize these objects is enough.

execute()

Then we take a look at the entry point to perform a task execute():

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

@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params) {
    if (mStatus != Status.PENDING) {
        switch (mStatus) {
           // execute() 
            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)");
        }
    }
   // Running
    mStatus = Status.RUNNING;
   // onPreExecute(), 
    onPreExecute();
   // params mWorker
    mWorker.mParams = params;
   // sDefaultExecutor.execute(runnable) 
    exec.execute(mFuture);
   // 
    return this;
}
 

We call the AsyncTask.execute(parasm)method is actually invoked sDefaultExecutor.execute(mFuture)method, we follow it down.

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

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

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

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

SerialExecutorIs a realization Executorof a static inner class, it execute(runnable)receives the Runnableobject, and then executeOnExecutor(Executor exec, Params... params)pass the method is mFuturean object, so the execution scheduleNext() time, it will call mFuturethe run()method.

FutureTask.run():

public void run() {
    ...
    try {
       // Callable 
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
               // Callable.call() 
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                setException(ex);
            }
            if (ran)
               // Callable.call() set() get() 
                set(result);
        }
    }...
}
 

FutureTask.run()Its method is to call Callablethe variable call()method, AsyncTaskin the passed Callableis mWorkeran object, this time we can put in the constructor mWorkerinitialization code to read out the.

mWorker = new WorkerRunnable<Params, Result>() {
    public Result call() throws Exception {
        mTaskInvoked.set(true);
        Result result = null;
        try {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
           // doInBackground(params) 
            result = doInBackground(mParams);
            Binder.flushPendingCommands();
        } catch (Throwable tr) {
           // mCancelled true
            mCancelled.set(true);
            throw tr;
        } finally {
           // 
            postResult(result);
        }
        return result;
    }
};

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
    Params[] mParams;
}
 

mWorkerActually very simple, is to get doInBackground(params)the return value, whether or not abnormal, the result will be a callback to the main thread, the callback operations appears below to seepostResult(result)

private Result postResult(Result result) {
   //getHandler() mHandler Looper
   // mHandler sHandler
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}
 

By getHandler().obtainMessage()obtaining an Messageobject, the message whatto MESSAGE_POST_RESULTmark is not the result of progress, objto AsyncTaskResultthe object, which is an internal class that contains AsyncTaskand Data[] two parameters. By sHandlersending out a message, and then we will see how itonPostExecute(result) link up.

sHandlerThey are InternalHandlerexamples from our InternalHandlersource code will be able to get the answer you want:

    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;
            }
        }
    }
 

InternalHandlerJudgment message types, one is the result, one is progress, if it is the result, then it calls AsyncTaskthe finish()method; if it is progress, then calls the onProgressUpdate()method, which in front has been introduced, and is updated overall the method of progress, as long as we look at finish()methods to ok:

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

Source code analysis to this case, roughly process has been completed, the execute()method adds the tasks to be performed to SerialExecutorthe double-ended queue, and then let the THREAD_POOL_EXECUTORthread pool to order execution queue FutureTask.run()to note here, mission is a an executive, After the execution is over, another one is executed. This thread pool is equivalent to a single-threaded mode, which executes tasks in serial rather than parallel execution. Only one core thread is actually working in the entire thread pool. By performing FutureTask.run()the method to perform the indirect mWorker.call()methods, the call()method for obtaining the doInBackground()results of the method returned by the last postResult()and InternalHandlerto the results of the callback onPostExecute()method.

The article on source code analysis is still being updated. If you find any errors or deficiencies in this article, you are welcome to leave a message below or scan the QR code below to leave a message!