Migrated from Jan 2017.09.23 21:23:39

Brief description of working process

AsyncTask involves Handler, Thread, Callable, and FutureTask.

The general flow chart is as follows:

A brief introduction: The AsyncTask constructor creates an mFuture (FutureTask) and an mWorker (WorkerRunnable). MFuture overrides the done method, mWorker implements the call method, Pass the mWorker to the mFuture. After building AsyncTask, you can execute the execute method, which calls executeOnExecutor and passes in the default sDefaultExecutor, SDefaultExecutor emulates a single thread by default (more on how below) and AsyncTask tasks are executed one at a time. You can also pass in custom threads to execute tasks concurrently. In the executeOnExecutor method, execute the pre-download preparation method onPreExecute(); . The Executor then executes the mFuture (FutureTask), which calls the mFuture’s run(); . The run() method calls the mWorker’s call(); , doInBackground() is called in the child thread, where our time-consuming task is implemented. After the time-consuming task is complete, call postResult() with the return value. This creates a Message (what = MESSAGE_POST_RESULT) with the result sent to InternalHandler object that processes the Message. If what is MESSAGE_POST_RESULT after receiving the message, sHandler will determine whether the task isCancelled isCancelled(). OnPostExecute (result) is called if cancelled but onPostExecute(result) is not. In doInBackground(), we can also call the publishProcess() method to refresh the progress in the main thread, which creates a Message (what = MESSAGE_POST_PROGRESS) with the progress value, SHandler processes the message. If what is MESSAGE_POST_PROGRESS, onProgressUpdate(Progress… Values) method. onPreExecute(); , onCancelled(result), onPostExecute(result), onProgressUpdate(Progress… Values) are executed in the main thread, and only doInBackground() is executed in the child thread.

AsyncTask concept

AsyncTask can use UI threads correctly and easily. This class allows you to perform background operations and render the results to the UI thread without having to handle threads and Handlers.

AsyncTask is designed to be a helper class for threads and handlers, not a framework for building threads. Ideally asyncTasks are used for short duration operations (most of them in seconds). If you need to keep threads running for a long time, it is highly recommended that you use some of the APIs of the java.util.concurrent package, such as Executor, ThreadPoolExecutor and FutureTask.

Asynchronous tasks are defined by computations that run in background threads and render results in UI threads. Asynchronous tasks are defined by three common types, Params, Progress, Result, and four steps, onPreExecute, doInBackground, onProgressUpdate, and onPostExecute.

Code examples:

 private class DownloadFilesTask extends AsyncTask<URL.Integer.Long> {
 
     @Override
     protected void onPreExecute(a) {
        // Prepare the work in the main thread.
     }
 
     protected Long doInBackground(URL... urls) {
         // Time consuming operation in child thread.
         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;
     }

     @Override
     protected void onProgressUpdate(Integer... progress) {
         // Displays progress in the main thread
         setProgressPercent(progress[0]);
     }
     
     @Override
     protected void onCancelled(Float result) {
         // This method is called when the task is cancelled, but onPostExecute is not called on the main thread.
         showDialog("Cancelled " + result + " bytes");
     }

     @Override
     protected void onPostExecute(Long result) {
         // Task complete, not cancelled, call this method, in the main thread.
         showDialog("Downloaded " + result + " bytes"); }}/ / use
 new DownloadFilesTask().execute(url1, url2, url3);
Copy the code

Next, with this code example, the previous workflow diagram, and the source code, take a look at how AsyncTask switches between UI threads and child threads, how to use the three paradigms, and how to invoke the four steps. Let’s look at the AsyncTask constructor:

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

            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            //noinspection unchecked
            Result result = doInBackground(mParams);
            Binder.flushPendingCommands();
            returnpostResult(result); }}; mFuture =new FutureTask<Result>(mWorker) {
        @Override
        protected void done(a) {
            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 constructor creates a mWorker (WorkerRunnable), which implements the Callable interface. The difference with the Runnable interface is that the implementation method returns V call() throws Exception; The call() method is explained later in the code. An mFuture (FutureTask) is also created and passed in mWorker as a parameter. MFuture overrides the done() method, which will be called later.

Public final AsyncTask execute(Params… The params) method is here to perform this task. This method calls executeOnExecutor(sDefaultExecutor, params); , passing in the parameters corresponding to sDefaultExecutor and params. Let’s start with what is 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(new Runnable() {
            public void run(a) {
                try {
                    r.run();
                } finally{ scheduleNext(); }}});if (mActive == null) { scheduleNext(); }}protected synchronized void scheduleNext(a) {
        if((mActive = mTasks.poll()) ! =null) { THREAD_POOL_EXECUTOR.execute(mActive); }}}Copy the code

The value of sDefaultExecutor is an object of an inner class, SERIAL_EXECUTOR, which implements the Executor interface. Why is it emulated as a single thread? At first I thought it was a single-threaded thread pool, but when I looked at the code it wasn’t. The task was performed by a custom thread pool called THREAD_POOL_EXECUTOR. Tasks are maintained in the ArrayDeque queue, where the SERIAL_EXECUTOR execute method creates a task. When mActive == null, there are no tasks in the queue and scheduleNext() is executed. THREAD_POOL_EXECUTOR executes the task. The run() method executes the task. The run() method executes the final Runnable r. ScheduleNext () is also called; Go to the next task, and so on until there are no more tasks in the queue. THREAD_POOL_EXECUTOR creates a custom thread using the following code:

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

After introducing sDefaultExecutor, back to the main line, Public final AsyncTask executeOnExecutor(Executor exec, Params… Params). ,>

@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) {
    if(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();

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

    return this;
}
Copy the code

The RUNNING or FINISHED task status is checked and an exception is thrown. Then, change the task status to RUNNING. OnPreExecute () is then executed; Method, where the first of the four important steps is performed. We then pass the parameter params to the mWorker and execute the mFuture. At this point, the sDefaultExecutor execution procedure and AsyncTask constructor are related. Call the exec. Execute (mFuture); The mFuture.run() method wraps the mFuture as a task and places it on a queue (as mentioned earlier), which is then executed. Because FutureTask implements the Runnable interface, there is a corresponding run() method.

Let’s go to the FutureTask class and see how its run() method is implemented.

public class FutureTask<V> implements RunnableFuture<V> {...public void run(a) {
        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); }}protected void set(V v) {
        if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
            outcome = v;
            U.putOrderedInt(this, STATE, NORMAL); // final statefinishCompletion(); }}private void finishCompletion(a) {
        // assert state > COMPLETING;
        for(WaitNode q; (q = waiters) ! =null;) {
            if (U.compareAndSwapObject(this, WAITERS, q, null)) {
                for (;;) {
                    Thread t = q.thread;
                    if(t ! =null) {
                        q.thread = null;
                        LockSupport.unpark(t);
                    }
                    WaitNode next = q.next;
                    if (next == null)
                        break;
                    q.next = null; // unlink to help gc
                    q = next;
                }
                break;
            }
        }
    
        done();
    
        callable = null;        // to reduce footprint}... }Copy the code

The callable in the code is the mWorker passed in the mFuture from the previous constructor. Callable

c = callable; result = c.call(); You can see that mWorker is called and then set(result) is called; , assigns the result to the outcome, calls finishCompletion(), and finally calls done(). The done method mainly invokes the postResultIfNotInvoked(Get ()); Invoked to check (mTaskInvoked identifies the bit). If the task is not executed, postResult(result) is guaranteed to be executed. Method to return the result to the main thread.

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

Both mWorker’s call() and mFuture’s done() methods are called.

Let’s scroll back to mWorker’s call() method.

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

        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        //noinspection unchecked
        Result result = doInBackground(mParams);
        Binder.flushPendingCommands();
        returnpostResult(result); }};Copy the code

First, set the identity of the task to be called to true, set the thread priority THREAD_PRIORITY_BACKGROUND, and then call the method doInBackground(mParams). This is the second step of the four steps, the method is called, and the time-consuming task starts to execute. Call postResult(result) after getting the returned result; Methods. The Binder. FlushPendingCommands (); The effect is not clear. Let’s look at postResult first.

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

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

Create a Message, what is MESSAGE_POST_RESULT, use the returned value to create an AsyncTaskResult object as a Message obj, and send it to sHandler for processing. Let’s look at the code in which the Handler processes the message.

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

private static class InternalHandler extends Handler {
    public InternalHandler(a) {
        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

The received message is processed in the handleMessage method, and if the message is MESSAGE_POST_RESULT, the finish method is called. The Finish method executes different methods depending on whether the task is cancelled or onCancelled(result); , onPostExecute(result) is not cancelled; Step 4 of AsyncTask is completed. The mission is complete. As you can see, only one onCancelled and onPostExecute will be executed. If the message is MESSAGE_POST_PROGRESS, the onProgressUpdate(result.mdata) method is executed. This is the third step.

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

It turns out that we can call this method from the worker thread, in doInBackground. If the task is not cancelled, a what is MESSAGE_POST_PROGRESS is created, encapsulating the progress value into an AsyncTaskResult object for processing by the Handler as an OBJ Message.

Finally, to see how the task is cancelled, call the following method:

public final boolean cancel(boolean mayInterruptIfRunning) {
    mCancelled.set(true);
    return mFuture.cancel(mayInterruptIfRunning);
}
Copy the code

To modify the identity bit for whether to cancel, call mFuture’s Cancel method. In the mFuture’s Cancel method, if there is a thread of the current task, the interrupt method is called and finishCompletion() is also called; To check whether the task is called, send postResult.

conclusion

AsyncTask not only helps us to complete asynchronous tasks easily, but also demonstrates how to use handlers and threads more accurately, from which we can learn a lot of useful knowledge.