Android AsyncTask: deep understanding, simple encapsulation, task queue analysis, custom thread pool, and androidasynctask

Source: Internet
Author: User

Android AsyncTask: deep understanding, simple encapsulation, task queue analysis, custom thread pool, and androidasynctask
A thread pool needs to be designed for SDK functions recently. After reading a lot of information, I don't know where to start. I suddenly found that AsyncTask has an encapsulation of the thread pool. so, I will take it as an example. This article will explain the basic usage of AsyncTask, to simple encapsulation, to task queue analysis, and finally to customize the thread pool.

 

1. Concepts

In Android, asynchronous tasks are implemented by Handler + Thread combinations. Thread is responsible for the time-consuming operations of sub-threads, and Handler is responsible for the communication between threads. The most common Thread is the subthread and the main Thread.

To simplify operations, Android provides the AsyncTask class to Implement Asynchronous tasks and easily implement communication between sub-threads and main threads.

2. Simple AsyncTask Encapsulation

Meaning of the three parameters

  • Params: the first parameter is the parameter passed in by the startup task;
  • Progress: The second parameter is used to display the Progress bar;
  • Result: The third parameter is the type of the parameter returned after execution in the background.
Package com. app; import android. OS. asyncTask;/*** Created by $ {zyj} on 2016/8/2. */public class MyTask <T> extends AsyncTask <T, Integer, T> {private TaskListener taskListener; public MyTask () {}// executes preprocessing, which runs on the UI thread, you can make some preparations for background tasks, such as drawing a progress bar control. @ Override protected void onPreExecute () {if (taskListener! = Null) {taskListener. start () ;}// runs on the UI thread and can process the result of the background task. The result is the return value of doInBackground (Params. @ Override protected void onPostExecute (T t) {if (taskListener! = Null) {taskListener. result (t) ;}}/*** update sub-thread progress, run on UI thread * @ param values */@ Override protected void onProgressUpdate (Integer... values) {; if (taskListener! = Null) {taskListener. update (values [0]) ;}// run with background thread @ Override protected T doInBackground (T... ts) {if (taskListener! = Null) {return (T) taskListener. doInBackground (ts [0]);} return null;} public MyTask setTaskListener (TaskListener taskListener) {this. taskListener = taskListener; return this;}/***** update progress * @ param progress */public void updateProgress (int progress) {publishProgress (progress );} public interface TaskListener <T> {void start (); void update (int progress); T doInBackground (T t); voi D result (T t);}/*** cancel an ongoing task */public void cancle () {if (! IsCancelled () {cancel (true );}}}

 

3. Simple asynchronous task usage
Package com. app; import android. OS. bundle; import android. support. v7.app. appCompatActivity; import android. util. log; import wifi.app.wei.com. myapplication. r; public class MainActivity extends AppCompatActivity {@ Override protected void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. activity_main); new MyTask <String> (). setTaskListener (new MyTask. taskListener () {@ Override public void start () {Log. d ("task --", "start started, running in the main thread") ;}@ Override public void update (int progress) {}@ Override public Object doInBackground (Object o) {Log. d ("task --", "doInBackground, run in subthread"); return null ;}@ Override public void result (Object o) {Log. d ("task --", "result, running in the main thread") ;}}cmd.exe cute ("");}}

  

4. Use asynchronous tasks with progress updates
Package com. app; import android. OS. bundle; import android. support. v7.app. appCompatActivity; import android. util. log; import android. widget. textView; import wifi.app.wei.com. myapplication. r; public class MainActivity extends AppCompatActivity {private TextView textView; private MyTask myTask; @ Override protected void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. activity_main); textView = (TextView) findViewById (R. id. tv1); myTask = new MyTask <String> (). setTaskListener (new MyTask. taskListener () {@ Override public void start () {Log. d ("task --", "start started, running in the main thread"); textView. setText ("task started") ;}@ Override public void update (int progress) {textView. setText ("progress" + progress) ;}@ Override public Object doInBackground (Object o) {Log. d ("task --", "doInBackground, run in subthread"); for (int I = 0; I <100; I ++) {try {Thread. sleep (100); myTask. updateProgress (I); // update the progress every 100 milliseconds} catch (InterruptedException e) {e. printStackTrace () ;}} return "ended" ;}@ Override public void result (Object o) {Log. d ("task --", "result, run in main thread"); textView. setText ("" + o) ;}}); // start the execution task myTask.exe cute ("");}}

Run

 

5. Details of AsyncTask task execution

(1) If an asynchronous task needs to be connected to IOT platform, you need to add the Internet permission.

<Uses-permission android: name = "android. permission. INTERNET"/>

(2) The AsyncTask instance must be created in the UI thread, execute (Params ...) The method must be called in the UI thread. You do not need to call onPreExecute () manually ().

(3) A task can only be executed once.

 

6. How to cancel a task

You can call myTask. cancle ();

However, this method does not really end the task, but sets a flag to interrupt the current thread.

Cancel task practice

Package com. app; import android. OS. bundle; import android. support. v7.app. appCompatActivity; import android. util. log; import android. view. view; import android. widget. textView; import wifi.app.wei.com. myapplication. r; public class MainActivity extends AppCompatActivity {private TextView textView; private MyTask myTask; @ Override protected void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. activity_main); textView = (TextView) findViewById (R. id. tv1); textView. setOnClickListener (new View. onClickListener () {@ Override public void onClick (View v) {// cancel the myTask task. cancle () ;}}); myTask = new MyTask <String> (). setTaskListener (new MyTask. taskListener () {@ Override public void start () {Log. d ("task --", "start started, running in the main thread"); textView. setText ("task started") ;}@ Override public void update (int progress) {textView. setText ("progress" + progress) ;}@ Override public Object doInBackground (Object o) {Log. d ("task --", "doInBackground, run in subthread"); for (int I = 0; I <100; I ++) {try {Thread. sleep (100); myTask. updateProgress (I); // update the progress every 100 milliseconds} catch (InterruptedException e) {e. printStackTrace () ;}} return "ended" ;}@ Override public void result (Object o) {Log. d ("task --", "result, run in main thread"); textView. setText ("" + o) ;}}); // start the execution task myTask.exe cute ("");}}

When you click textView, the Android studio console throws an exception after the myTask. cancle (); method is called.

 

Through this, we found that although AsyncTask provides the cancle (true) method to stop the task, this method only interrupts this thread, but cannot actually stop the task, this is also the drawback of AsyncTask. It can easily cause memory overflow.

 

Indirect implementation methods for task termination:

1. How to Determine the flag position:

We need to know that there is no way to stop a running thread in the java thread. The same applies to AsyncTask in Android. To stop a thread, we can use this thread to set a flag, then, the key step in the thread run method or the doInBackground method of AsyncTask is used to determine whether to continue the execution. Then, change the flag where the thread needs to be terminated to stop the thread.

2. Make Rational Use of Exception

The cancel method that calls AsyncTask externally cannot stop a started AsyncTask. The cancel method is similar to the thread's interrupt method. After a thread's interrupt method is called, the thread still runs, however, if the thread's run method is in sleep or wait status after calling sleep or wait, sleep and wait will immediately end and InterruptedException will be thrown. The cancel Method of AsyncTask is the same. If the sleep or wait method is called in the doInBackground method of the Task, after the cancel Method of the Task instance is called in the UI thread, sleep or wait ends immediately and an InterruptedException exception is thrown. However, if there is other code behind the code that captures the exception, the code will continue to be executed.

3. You can perform operations on the UI.

If the user cancels the content while the background thread is obtaining the content, we can immediately provide feedback on the UI based on this behavior. At this time, even if the thread completes the data Loading, we don't want the data to be displayed. It's a opportunistic approach.

7. AsyncTask: Serial processing tasks and Parallel Processing Tasks

In the preceding Code demonstration, all the execution tasks use myTask.exe cute (). By default, this task is executed serially. For example, if two tasks are to be processed at the same time, AsyncTask will first execute the first task and wait until the execution of the first task ends before the second task is executed.

In AsyncTask, there is another way to process tasks in parallel: executeOnExecutor (Executor exe, Params... params ).

 

The following is the source code of the serial execution task execute ().

 

By looking at the source code, we found that the serial execution task actually calls the parallel method executeOnExecutor (), but only enables a default sDefaultExecutor (sDefaultExecutor is a serial thread pool ).

If there is a serial thread pool, there is a parallel thread pool. In AsyncTask, the source code defines a parallel thread pool:THREAD_POOL_EXECUTOR.

We can see that the parallel THREAD_POOL_EXECUTOR is created through new ThreadPoolExecutor ().

    public ThreadPoolExecutor(int corePoolSize,                              int maximumPoolSize,                              long keepAliveTime,                              TimeUnit unit,                              BlockingQueue<Runnable> workQueue,                              ThreadFactory threadFactory) {        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,             threadFactory, defaultHandler);    }

Parameter description:

CorePoolSize: Minimum number of threads maintained by the thread pool
MaximumPoolSize: Maximum number of threads maintained by the thread pool
KeepAliveTime: the idle time allowed by the thread pool to maintain the thread
Unit: the unit of idle time allowed by the thread pool maintenance thread.
WorkQueue: The Buffer Queue used by the thread pool.
Handler: processing policy of the thread pool to reject tasks

 

We know that limited by hardware, memory, and performance, we cannot create any number of threads without limit, because the maximum thread allowed by each machine is a bounded value. That is to say, the number of threads managed by ThreadPoolExecutor is bounded. A thread pool uses a limited number of threads to execute submitted tasks. However, for multi-user and highly concurrent applications, the number of submitted tasks is huge, which is certainly much larger than the maximum number of threads allowed. To solve this problem, you must introduce the queuing mechanism, either in the memory, or in a storage medium with large disk capacity. The ThreadPoolExecutor provided by J. U. C only supports tasks queuing in the memory. The BlockingQueue can be used to temporarily store tasks that have not yet been executed.

Task Management is a relatively easy task. complicated tasks are thread management, which involves the number of threads, waiting/awakening, synchronization/lock, thread creation and death. ThreadPoolExecutor has several member variables related to threads: keepAliveTime, allowCoreThreadTimeOut, poolSize, corePoolSize, and maximumPoolSize. They are jointly responsible for thread creation and destruction.

CorePoolSize:

The basic size of the thread pool, that is, the size of the thread pool when no task needs to be executed. Only when the working queue is full can the thread pool be created. Note that the thread does not start immediately when the ThreadPoolExecutor is created, but does not start until a task is submitted, unless prestartCoreThread/prestartAllCoreThreads is called to start the Core Thread in advance. Considering the impact of keepAliveTime and allowCoreThreadTimeOut timeout parameters, the thread pool size is not necessarily corePoolSize when no task needs to be executed.

MaximumPoolSize:

The maximum number of threads allowed in the thread pool. The current number of threads in the thread pool does not exceed this value. If the task in the queue is full and the current number of threads is smaller than maximumPoolSize, a new thread is created to execute the task. It is worth mentioning that largestPoolSize records the maximum number of threads that the thread pool has seen throughout its lifecycle. Why did it happen? After the thread pool is created, you can call setMaximumPoolSize () to change the maximum number of running threads.

PoolSize:

The number of threads in the thread pool. When this value is 0, it means that no thread exists and the thread pool will terminate. At the same time, the poolSize will not exceed the maximumPoolSize.

KeepAliveTime

When the idle time of a thread reaches keepAliveTime, the thread exits until the number of threads equals to corePoolSize. If allowCoreThreadTimeout is set to true, all threads exit until the number of threads is 0.

After understanding the meaning of each parameter, let's take a look at the default parallel thread queue in AsyncTask.THREAD_POOL_EXECUTORValue of each item

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 ThreadFactory sThreadFactory = new ThreadFactory() {    private final AtomicInteger mCount = new AtomicInteger(1);    public Thread newThread(Runnable r) {        return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());    }};private static final BlockingQueue<Runnable> sPoolWorkQueue =            new LinkedBlockingQueue<Runnable>(128);
  • CorePoolSize is the number of CPUs plus 1;
  • MaximumPoolSize: 2 times of the number of CPUs plus 1
  • The survival time is 1 second.
  • The task cache queue is queue blockingqueue.

Small test: my mobile phone is Lenovo k50-t5, In the settings to see the processor for 8-core 1.7 GHZ, run Runtime. getRuntime (). availableProcessors (); the value of the method is 8.

In addition, we can conclude that:

  •  On the same mobile phone,The corePoolSize and maximumPoolSize values of THREAD_POOL_EXECUTOR are fixed.
  • On different mobile phones,The corePoolSize of THREAD_POOL_EXECUTOR is different from that of maximumPoolSize. This dynamic setting method is worth learning. The policies used on different devices are different. However, this method also has disadvantages. The number of concurrent tasks is limited by the cpu and cannot be manually modified.

 

Summary:

// Start executing the serial task myTask.exe cute (""); or myTask.exe cuteOnExecutor (AsyncTask. SERIAL_EXECUTOR, ""); // starts executing the parallel task myTask.exe cuteOnExecutor (AsyncTask. THREAD_POOL_EXECUTOR ,"");

 

8. Custom Thread Pool

In the previous section, we have learned that the default parallel thread pool THREAD_POOL_EXECUTOR of AsyncTask is created through new ThreadPoolExecutor (), so we can define a thread pool by ourselves.

First, let's look at the ThreadPoolExecutor constructor.

public ThreadPoolExecutor(int corePoolSize,                              int maximumPoolSize,                              long keepAliveTime,                              TimeUnit unit,                              BlockingQueue<Runnable> workQueue) {        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,                Executors.defaultThreadFactory(), defaultHandler);    }        public ThreadPoolExecutor(int corePoolSize,                              int maximumPoolSize,                              long keepAliveTime,                              TimeUnit unit,                              BlockingQueue<Runnable> workQueue,                              ThreadFactory threadFactory) {        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,                threadFactory, defaultHandler);    }        public ThreadPoolExecutor(int corePoolSize,                              int maximumPoolSize,                              long keepAliveTime,                              TimeUnit unit,                              BlockingQueue<Runnable> workQueue,                              RejectedExecutionHandler handler) {        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,                Executors.defaultThreadFactory(), handler);    }        public ThreadPoolExecutor(int corePoolSize,                              int maximumPoolSize,                              long keepAliveTime,                              TimeUnit unit,                              BlockingQueue<Runnable> workQueue,                              ThreadFactory threadFactory,                              RejectedExecutionHandler handler) {        if (corePoolSize < 0 ||                maximumPoolSize <= 0 ||                maximumPoolSize < corePoolSize ||                keepAliveTime < 0)            throw new IllegalArgumentException();        if (workQueue == null || threadFactory == null || handler == null)            throw new NullPointerException();        this.corePoolSize = corePoolSize;        this.maximumPoolSize = maximumPoolSize;        this.workQueue = workQueue;        this.keepAliveTime = unit.toNanos(keepAliveTime);        this.threadFactory = threadFactory;        this.handler = handler;    }

By looking at the constructor, it is found that corePoolSize, maximunPoolSize, keepAliveTime, unit, and workQueue must be written.

Analyze the last Structure

if (corePoolSize < 0 ||                maximumPoolSize <= 0 ||                maximumPoolSize < corePoolSize ||                keepAliveTime < 0)            throw new IllegalArgumentException();

CorePoolSize: Minimum value 0

MaximunPoolSize: Minimum value: 1

CorePoolSize must be smaller than or equal to maximunPoolSize

 

The main point is workQueue. This is the thread queue.

The following is the thread queue used in the parallel thread pool THREAD_POOL_EXECUTOR of AsyncTask. 128 represents the length of the thread queue.

    private static final BlockingQueue<Runnable> sPoolWorkQueue =            new LinkedBlockingQueue<Runnable>(128);

The following is a complete example:

 

// Create a Buffer Queue queue length: 100 BlockingQueue <Runnable> sPoolWorkQueue = new LinkedBlockingQueue <Runnable> (100); // create a thread pool Core Thread: 5 maximum threads: idle survival time of 10 threads: 1 second Executor executor = new ThreadPoolExecutor (5, 10, 1, TimeUnit. SECONDS, sPoolWorkQueue); // Add the task to the Buffer Queue myTask1.executeOnExecutor (executor ,"");

  

  

Thread Creation Rules

A task is added to the thread pool using the execute (Runnable) method. A task is a Runnable object. The execution method of a task is the run () method of a Runnable object.
When a task is added to the thread pool through the execute (Runnable) method:
1. If the number of threads in the thread pool is smaller than corePoolSize at this time, a new thread should be created to process the added tasks even if all threads in the thread pool are idle.
2. If the number in the thread pool is equal to corePoolSize, but the Buffer Queue workQueue is not full, the task is put into the buffer queue.
3. If the number in the thread pool is greater than corePoolSize, the Buffer Queue workQueue is full, and the number in the thread pool is smaller than maximumPoolSize, a new thread is created to process the added task.
4. If the number in the thread pool is greater than corePoolSize, the Buffer Queue workQueue is full, and the number in the thread pool is equal to maximumPoolSize, the handler policy is used to process the task. That is, the processing task priority is: Core Thread corePoolSize, task queue workQueue, maximumPoolSize. If all three are full, handler is used to process the rejected task.
5. When the number of threads in the thread pool is greater than corePoolSize, if the idle time of a thread exceeds keepAliveTime, the thread will be terminated. In this way, the thread pool can dynamically adjust the number of threads in the pool.

 

The thread pool executes tasks as follows:

 

Logic of task queue execution:

FIFO first-in-first-out

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.