android multithreading - AsyncTask

    the first part analyzes some basic uses of AsyncTask and the differences between different android versions. Then in this article, we will comprehensively analyze the working principle of AsyncTask. Before we begin, let's take a look at a knowledge point of multithreading -- callable < V >, future < V > and FutureTask classes

1, Understand the callable < V >, future < V > and FutureTask classes

Callable<V>

The interface of Callable is defined as follows:

public interface Callable<V> {   
      V   call()   throws Exception;   
}   

   the callable interface declares a method named call(), which can have a return value V or throw an exception. Callable is also a thread interface. The main difference between callable and Runnable is that callable can have a return value after thread execution, but Runnable has no return value. The Runnable interface is declared as follows:

public interface Runnable {
    public abstract void run();
}

   how to use the callable interface? Callable needs to be used in combination with ExcutorService. ExecutorService is also a thread pool object inherited from the Executor interface. I won't go into it here. Next, let's look at the methods provided by ExecutorService for us to use:

<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
  • submit(Callable task), which passes a task that implements the Callable interface and returns the Future that encapsulates the asynchronous calculation results.
  • submit(Runnable task, T result), which passes a task implementing the Runnable interface and specifies the result object returned when calling the get method of Future.
  • submit(Runnable task), which passes a task that implements the Runnable interface and returns the Future that encapsulates the asynchronous calculation results.

  therefore, we just need to create our thread object (implement the Callable interface or Runnable interface) and submit it to the thread pool for execution through the above three methods. That's all for the introduction of the Callable interface. Let's see what's going on in the Future.

Future<V>

   the Future interface is used to obtain asynchronous calculation results. In other words, it is used to obtain (get()) and cancel () the execution results of specific Runnable or Callable object tasks, and judge whether they are completed. The method is as follows:

public interface Future<V> {
    //Cancel task
    boolean cancel(boolean mayInterruptIfRunning);

    //Returns true if the task is cancelled before completion.
    boolean isCancelled();

    //If the task execution ends, whether it ends normally, cancels halfway, or an exception occurs, true is returned.
    boolean isDone();

    //Get the result of asynchronous execution. If no result is available, this method will block until the asynchronous calculation is completed.
    V get() throws InterruptedException, ExecutionException;

    // Get asynchronous execution results. If no results are available, this method will block, but there will be a time limit,
    //If the blocking time exceeds the set timeout time, the method will return null.
    V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;

}

In general, Future has the following three functions:

  • Ability to interrupt ongoing tasks
  • Judge whether the task is completed
  • Get the amount result after task execution is completed.

    but Future is just an interface, and we can't create it as an object at all. Yu officially provided us with its implementation class FutureTask. Here we should know that the introduction of the first two interfaces only paves the way for this kind. After all, the object used in AsncyTask is FutureTask.

FutureTask

Let's take a look at the implementation of FutureTask:

public class FutureTask<V> implements RunnableFuture<V> {  

Obviously, the FutureTask class implements the RunnableFuture interface. Let's take another look at the implementation of the RunnableFuture interface:

public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}

   from the interface implementation, FutureTask implements not only the Future interface, but also the Runnable interface. Therefore, FutureTask can be regarded as either a Future object or a Runnable object. Of course, FutureTask can also be directly submitted to the thread pool for execution. Next, we are most concerned about how to create FutureTask objects. In fact, FutureTask can be constructed through the following two construction methods

public FutureTask(Callable<V> callable) {  
}  
public FutureTask(Runnable runnable, V result) {  
}  

  from the construction method, we can encapsulate an object that implements the Callable or Runnable interface into a FutureTask object, and then execute it through the thread pool. How to use it? Simple case, callabledemo The Java code is as follows:

package com.zejian.Executor;
import java.util.concurrent.Callable;
/**
 * Callable The interface instance calculates the size of the accumulated value and returns it
 */
public class CallableDemo implements Callable<Integer> {

    private int sum;
    @Override
    public Integer call() throws Exception {
        System.out.println("Callable The sub thread begins to calculate!");
        Thread.sleep(2000);

        for(int i=0 ;i<5000;i++){
            sum=sum+i;
        }
        System.out.println("Callable End of sub thread calculation!");
        return sum;
    }
}

CallableTest. The java test code is as follows:

package com.zejian.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class CallableTest {

public static void main(String[] args) {
//The first way to use
//      //Create thread pool
//      ExecutorService es = Executors.newSingleThreadExecutor();
//      //Create Callable object task
//      CallableDemo calTask=new CallableDemo();
//      //Submit the task and obtain the execution results
//      Future<Integer> future =es.submit(calTask);
//      //Close thread pool
//      es.shutdown();

    //Second, how to use

    //Create thread pool
    ExecutorService es = Executors.newSingleThreadExecutor();
    //Create Callable object task
    CallableDemo calTask=new CallableDemo();
    //Create FutureTask
    FutureTask<Integer> futureTask=new FutureTask<>(calTask);
    //Perform tasks
    es.submit(futureTask);
    //Close thread pool
    es.shutdown();
    try {
        Thread.sleep(2000);
    System.out.println("The main thread is performing other tasks");

    if(futureTask.get()!=null){
        //Output the obtained results
        System.out.println("futureTask.get()-->"+futureTask.get());
    }else{
        //Output the obtained results
        System.out.println("futureTask.get()No results were obtained");
    }

    } catch (Exception e) {
        e.printStackTrace();
    }
    System.out.println("The main thread finished executing");
}
}

   the code is very simple and the comments are very clear. Here, we analyze the second execution method. We previously declared a CallableDemo class, which implements the Callable interface, and then calculate the sum total value through the call method and return it. Then, in the test class CallableTest, the CallableDemo instance class is encapsulated into a FutureTask object and handed over to the thread pool for execution. The final execution result will be encapsulated in FutureTask, and the execution result can be obtained through FutureTask#get(). The first method is to directly throw the Callable implementation class to the thread pool for execution, and the results are encapsulated in the Future instance. The execution results of the second method are as follows:

Callable The sub thread begins to calculate!
The main thread is performing other tasks
Callable End of sub thread calculation!
futureTask.get()-->12497500
 The main thread finished executing

   ok ~, here we introduce Callable, Future and FutureTask. With this knowledge, we can happily get rid of the internal working principle of AsyncTask.

2, The working principle of AsyncTask is fully analyzed

  in the previous article, the following code is used to execute the asynchronous task of AsyncTask:

new AysnTaskDiff("AysnTaskDiff-1").execute("");

  from the code, the entry is the execute method. Let's first look at the source code of execute:

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

    obviously, the execute method is just a shell. It directly calls executeonexecutor (sdefaultexecution, params), where sdefaultexecution is a serial thread pool. Then look at the internal implementation of sdefaultexecution:

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
/**
 * An {@link Executor} that executes tasks one at a time in serial
 * order.  This serialization is global to a particular process.
 */
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

//The serial thread pool class implements the Executor interface
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() { //Insert a Runnble task
            public void run() {
                try {
                    r.run();
                } finally {
                    scheduleNext();
                }
            }
        });
        //Judge whether Runnable is executing, and call scheduleNext method if not
        if (mActive == null) {
            scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
      //Take the task from the task queue mTasks and put it in THREAD_POOL_EXECUTOR executes in the thread pool
      //It can also be seen that the task is carried out in series.
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

    as can be seen from the source code, ArrayDeque is a container for storing task queues (mTasks). After the task Runnable is passed in, it is handed over to the execute method of the SerialExecutor. The SerialExecutor will insert the task Runnable into the tail of the task queue mTasks, and then judge whether there is a Runnable executing. If not, it will call the scheduleNext method to execute the next task, Then give it to THREAD\_POOL\_EXECUTOR executes in the thread pool. It can be seen that the SerialExecutor is not a real thread executor. It just ensures that the transmitted task Runnable (the instance is a FutureTask) is executed serially, and thread is the one that actually executes the task\_ POOL\_ Executor thread pool. Of course, this logic also reflects that tasks within AsyncTask are performed serially by default. By the way, thread\_ POOL\_ Declaration of executor thread pool:

//CUP kernel
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
//Number of core threads
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
//Maximum number of threads
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
//The survival time of non core thread is 1s
private static final int KEEP_ALIVE = 1;
//Thread factory class
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
    private final AtomicInteger mCount = new AtomicInteger(1);

    public Thread newThread(Runnable r) {
        return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
    }
};
//Thread queue. When the core thread is not enough, the task will be added to the queue. When the queue is full, the non core thread will be called to execute the task
private static final BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(128);

/**
 * An {@link Executor} that can be used to execute tasks in parallel.
 * Create thread pool
 */
public static final Executor THREAD_POOL_EXECUTOR
        = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

   ok ~, let's learn about sdefaultexecution first. Let's go back to the steps of the executeOnExecutor method called internally in the execute method. First, let's see what the executeOnExecutor has done? Its source code is as follows:

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
   //Judge in that state
   if (mStatus != Status.PENDING) {
       switch (mStatus) {
           case RUNNING:
               throw new IllegalStateException("Cannot execute task:"
                       + " the task is already running.");
           case FINISHED://Can only be executed once!
               throw new IllegalStateException("Cannot execute task:"
                       + " the task has already been executed "
                       + "(a task can be executed only once)");
       }
   }

   mStatus = Status.RUNNING;
   //onPreExecute() is executed here!!!
   onPreExecute();
   //Parameter passed to mworker mParams
   mWorker.mParams = params;
   //Execute the mFuture task, where exec is the sdefaultexecution passed in
   //Give mFuture to the thread pool to perform tasks
   exec.execute(mFuture);

   return this;
    }

    according to the source code analysis of the executeOnExecutor method, the status of the current AsyncTask will be judged before executing the task. If it is in the RUNNING and FINISHED status, it can no longer be executed. An exception will be thrown directly, only in the status AsyncTask will execute when pending. Then onPreExecute() is executed, which can be used to do some preparatory work before the thread starts. Then we will assign the parameters we passed in to mWorker Mparams, and start to execute the mFuture task, so what are mWorker and mFuture? Let's take a look at the declared source code of mWorker, that is, WorkerRunnable:

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

   the WorkerRunnable abstract class implements the Callable interface, so WorkerRunnable is also a Callable object in essence, and it also encapsulates an array parameter of mparms. Therefore, the variable parameters passed when we execute the execute method externally will eventually be assigned to the internal array mparms of WorkerRunnable, and these parameters will finally be passed to the doInBackground method for processing, At this time, we find that the doInBackground method is also called in the call method of WorkerRunnable. See its source code as follows:

public AsyncTask() {
   //Creating a WorkerRunnable mWorker is essentially an object that implements the Callable interface
    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
            //Set flag
            mTaskInvoked.set(true);

         Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            //Execute doInBackground and pass the mParams parameter
            Result result = doInBackground(mParams);
            Binder.flushPendingCommands();
            //After execution, call the postResult method to update the result
            return postResult(result);
        }
    };
//Encapsulate the mWorker (that is, the Callable implementation class) into a FutureTask instance
//The final execution result is encapsulated in FutureTask
    mFuture = new FutureTask<Result>(mWorker) {
        //Called after the task is completed
        @Override
        protected void done() {
            try {
             //If the result notification has not been updated, execute postResultIfNotInvoked
                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) {
                //Throwing anomaly
                postResultIfNotInvoked(null);
            }
        }
    };
}

  you can see that when initializing AsyncTask, not only the mWorker (which essentially implements the instance class of the Callable interface) but also the FutureTask object is created, and the mWorker object is encapsulated in the FutureTask object. Finally, the FutureTask object will be executed through the thread pool in the executeOnExecutor method. Give the following figure to help understand:

   AsynTask will create mWorker instance object and FutureTask instance object during initialization. mWorker is an instance object that implements the Callable thread interface and encapsulates the transfer parameters, and then the mWorker instance will be encapsulated into FutureTask instance. After AsynTask is created, we call the execute method to execute the asynchronous thread. The executeOnExecutor method is directly called internally, and the thread pool exec object and execution parameters are passed. The mFuture instance is executed internally through the thread pool exec object. At this time, the call method inside mWorker will be executed and the doInBackground method will be called, Finally, the update result is notified through postResult. The source code of the postResult method is as follows:

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

  obviously, the result update is performed through the Handler. After the execution result is returned, the result will be encapsulated into an AsyncTaskResult object, and finally Message\_ POST\_ The result flag and AsyncTaskResult are stored in the Message and sent to the Handler for processing. Here, let's take a look at the source code of AsyncTaskResult:

private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
        }
    }

  obviously, AsyncTask result encapsulates the array of execution results and AsyncTask itself. There's nothing to say about this. Next, let's see how AsyncTask result is processed after it is sent to the handler.

private static class InternalHandler extends Handler {
    public InternalHandler() {
        //Get the Looper of the main thread and pass it to the current Handler, which is why AsyncTask can only be created and executed in the main thread
        super(Looper.getMainLooper());
    }

    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
    @Override
    public void handleMessage(Message msg) {
    //Get asynctask result
        AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
        switch (msg.what) {
            //Execution complete
            case MESSAGE_POST_RESULT:
                // There is only one result
                result.mTask.finish(result.mData[0]);
                break;
                //Flag to update progress bar
            case MESSAGE_POST_PROGRESS:
            //Execute the onProgressUpdate method and implement it yourself.
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}

  according to the analysis of the handler's source code, the handler's bound thread is the main thread, which is why AsyncTask must be created and executed in the main thread. Then, determine the execution result through different flags sent by the handler. If it is marked as MESSAGE\_POST\_RESULT executes the finish method of AsyncTask and passes the execution result to the method. The source code of the finish method is as follows:

private void finish(Result result) {
        if (isCancelled()) {//Determine whether the task is cancelled
            onCancelled(result);
        } else {//Execute onpost execute (result) and pass the result
            onPostExecute(result);
        }
        //Change the status of AsyncTask to completed
        mStatus = Status.FINISHED;
    }

   this method first determines whether the task is cancelled. If it is not cancelled, execute the onPostExecute(result) method, and update relevant information externally through the onPostExecute method, such as UI, message notification, etc. Finally, change the status of AsyncTask to completed. All processes of this AsyncTask have been executed.
  here is another sign MESSAGE\_POST\_PROGRESS, which is issued when we call the publishProgress method in the doInBackground method. The prototype is as follows:

protected final void publishProgress(Progress... values) {
    if (!isCancelled()) {
    //Send MESSAGE_POST_PROGRESS to notify the update progress bar
        getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult<Progress>(this, values)).sendToTarget();
    }
}

   ok ~, the overall process of AsyncTask is basically analyzed. Finally, let's make a summary: when we call the execute(Params... params) method, the executeOnExecutor method is directly called internally, and then onPreExecute() is called. The workerrunnable object (essentially Callable object) executing asynchronous tasks is finally encapsulated into a futuretask instance, The FutureTask instance will be executed by the thread pool sExecutor. In the process, doInBackground(Params... params) will be invoked (called in the call method of the WorkerRunnable object). If we override the doInBackground(Params... params) method, we call the publishProgress (Params...) method. POST\_ Progress message, update progress. When sHandler processes the message, onProgressUpdate(Progress... values) method will be called; Finally, if the futuretask task executes successfully and returns a result, a message is sent through the postResult method\_ POST\_ The result message executes the finish method of AsyncTask. Within the finish method, the onPostExecute(Result result) method is called. In the onPostExecute method, we can update the UI or release resources. This is the internal workflow of AsyncTask, which can be said to be
Callable+FutureTask+Executor+Handler internal encapsulation. At the end, we present an implementation process to help you understand the whole process:

This article is transferred from https://blog.csdn.net/javazejian/article/details/52464139 , in case of infringement, please contact to delete.

Keywords: Android

Added by narch31 on Thu, 09 Dec 2021 20:36:08 +0200