On the Advantages and Disadvantages of Android AsyncTask

Keywords: Android github Google Mobile

Introduction: I was used to the development of Framework layer before. Today I am interviewing the development of APP client in Wuhan Dogfish Company. One of the questions is about the advantages and disadvantages of Asynctask. I depend on it. I just know that there is such a thing that I can use it. It seems that the life before is too comfortable, so basic things will not be deliberately summarized at home.

For reproducing, please indicate the link of the original text: http://www.cnblogs.com/yanyojun/p/6414919.html

1. Introduction to Asynctask

1.1 Brief Introduction of Usage Method

Asynctask is one of the foundations of Android. How to use it is not much explained. There are many tutorials on the Internet. It is recommended to check the official API documents of Android: https://developer.android.google.cn/reference/android/os/AsyncTask.html

Here we only implement a small Demo program for everyone to enjoy:

Interface:

In fact, this program is very simple, that is, two buttons, Click to test the implementation of AysncTask and Handler modes respectively, and the corresponding logs will be prompted after clicking.

Functional Profile:

Implementation of Asynctask:

   private class IAsyncTask 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 submission, it will be executed onProcessUpdate Method
            }
            Log.i(TAG, "doInBackground out");
            return "over";
        }

        /**
         * The cancel method is called and executed here.
         */
        protected void onCancelled() {
            Log.i(TAG, "onCancelled");
        }

        /**
         * After doInbackground Execution
         */
        protected void onPostExecute(String args3) {
            Log.i(TAG, "onPostExecute:" + args3);
        }

        /**
         * Execute before doInBackground
         */
        @Override
        protected void onPreExecute() {
            Log.i(TAG, "onPreExecute");
        }

        /**
         * In particular, this method of multiple parameters is very convenient.
         * @param args2
         */
        @Override
        protected void onProgressUpdate(Integer... args2) {
            Log.i(TAG, "onProgressUpdate:" + args2[0]);
        }
    }

 

Clicking the first button will execute here. Clicking the button is written as follows:

        mBtnSyncTask.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new IAsyncTask().execute("yanlog test");
            }
        });

 

The logs of the execution results are as follows:

 1 02-19 21:55:12.179 10824-11010/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in:yanlog test//DoInbackgroundis executed in 10824, 11010 threads
 2 02-19 21:55:12.179 10824-11010/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground out 
 3 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:0//The rest is executed in 10824 threads, and Android is particularly good that the thread number of the main thread is the same as the follow-up number.
 4 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:1
 5 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:2
 6 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:3
 7 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:4
 8 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:5
 9 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:6
10 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:7
11 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:8
12 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:9
13 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onPostExecute:over

 

 

 

Handler+Message implementation:

The main codes are as follows:

 1     private class IHandler extends Handler{
 2         @Override
 3         public void handleMessage(Message msg){
 4             switch(msg.what){
 5                 case 1:
 6                     Log.e(TAG,"handler:"+msg.obj);
 7                     break;
 8                 default:
 9                     break;
10             }
11         }
12     }

 

Among them, the calling place is as follows:

 1         mBtnHandlerTest.setOnClickListener(new View.OnClickListener() {
 2             @Override
 3             public void onClick(View v) {
 4                 final Handler handler = new IHandler();
 5                 new Thread(new Runnable() {
 6                     @Override
 7                     public void run() {
 8                         for (int i = 0; i < 10; i++) {
 9                             Message msg = new Message();
10                             msg.what = 1;
11                             msg.obj = new Integer(i);
12                             Log.e(TAG, "post message:" + i);
13                             handler.sendMessage(msg);
14                         }
15                     }
16                 }).start();
17             }
18         });

 

You can see the results of Log printing as follows:

 1 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:0 //You can see that the submission was made in sub-process 9319
 2 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:1
 3 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:2
 4 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:3
 5 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:4
 6 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:5
 7 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:6
 8 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:7
 9 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:8
10 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:9
11 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:0 //As you can see, the submission is executed in the 9234 main thread.
12 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:1
13 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:2
14 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:3
15 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:4
16 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:5
17 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:6
18 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:7
19 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:8
20 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:9

 

Above, a simple comb of how to achieve the next, not to mention.

1.2 Android internal source implementation

The implementation of Handler+Message+Message Queue+Looper is not introduced. It's a cliche. So here's a look at the source implementation of AsyncTask:

The core approach of AsyncTask should be

public final AsyncTask<Params, Progress, Result> execute(Params... params)

Let's see what happens when the execute method is called. Here's the sequence diagram of the execution.

I know my drawing is not standard enough. Let's just look at it. Here are some explanations for this graph.

  • In step 4, execute, you can see that doInBackground has been transferred to the sub-thread to execute, which is a key point. I deliberately marked it with an asynchronous processing arrow.
  • In Step 9, when the doInbackground executes and finish es, the method of sendMessage returns to the main thread, so the onPost Execute and onCanceled are executed in the main thread.

Well, that's all. As for the source code of AsyncTask, I uploaded it to github. You can see more clearly by looking at the source code.

https://github.com/YanYoJun/AndroidSource/blob/master/AsyncTask.java

For the source code analysis of AsyncTask, there is another blog well written, see:

http://www.infocool.net/kb/OtherMobile/201610/197431.html

Remarks:

There are three other places in the source code that deserve further study, namely:

  • FutureTask is worth looking at. I went back to my blog and posted the link here.
  • The Serial Executor class in AsyncTask is so beautifully written that I'll go back and write a single blog to enjoy it.
  • I haven't actually studied the ThreadPoll Executor above... Back to blog research.

 

2, advantages

Simple and fast

This statement is close to bullshit, mainly depends on the use of habits, I like to use Handler.

But Android defines this thing, and you can see that all kinds of message encapsulation are still very good, very standard. You can follow this "beautiful framework" to write, the code will not be too out of line.

3, shortcomings

3.1 AsyncTask is actually the next thread behind the background!!!

Today, I carefully studied the lower source code, and found that most of the writing on the Internet is wrong. There is only one real background thread in AsyncTask!! Believe it or not, look at the following code:

We first define an IAsyncTAsk, where the doInBackground method writes as follows:

    private class IAsyncTask 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 submission, the onProcessUpdate method is executed
            }
            Log.i(TAG, "doInBackground out");*/

            Log.i(TAG, "doInBackground in thread:" + args1[0]);
            try {
                int times = 4;
                for (int i = 0; i < times; i++) {
                    Log.i(TAG, "thread alive:" + i + " for times"+args1[0]); //The doInBackground prints a Log and then sleeps for 20 milliseconds.
                    Thread.sleep(20);
                }
            } catch (Exception e) {

            }
            return "over";
        }

 

The place to call is as follows:

1                 int N = 5;
2                 for (int i = 0; i < N; i++) {
3                     Log.d(TAG,"asyncTask post Task:"+i);
4                     new IAsyncTask().execute("asyncTask times:"+i); //After clicking Button, five background sub-threads are created in the onClick method.
5                 }

 

Let's look at the print results:

 1 02-20 21:48:08.206 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:0 //Submitting in the main thread
 2 02-20 21:48:08.211 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:1
 3 02-20 21:48:08.211 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:2
 4 02-20 21:48:08.211 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:3
 5 02-20 21:48:08.211 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:4
 6 02-20 21:48:08.212 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:0 //As you can see, although the system started 18067, 18068,
 7 02-20 21:48:08.212 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:0//18069, 18070 these sub-threads, but these sub-threads
 8 02-20 21:48:08.232 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:0 //Serial execution!!! Shocked if there is!!!
 9 02-20 21:48:08.253 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:0 //It's not a coincidence. It's true several times!!
10 02-20 21:48:08.273 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:0
11 02-20 21:48:08.294 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:1
12 02-20 21:48:08.294 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:1
13 02-20 21:48:08.315 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:1
14 02-20 21:48:08.335 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:1
15 02-20 21:48:08.356 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:1
16 02-20 21:48:08.377 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:2
17 02-20 21:48:08.377 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:2
18 02-20 21:48:08.397 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:2
19 02-20 21:48:08.417 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:2
20 02-20 21:48:08.438 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:2
21 02-20 21:48:08.462 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:3
22 02-20 21:48:08.462 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:3
23 02-20 21:48:08.483 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:3
24 02-20 21:48:08.504 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:3
25 02-20 21:48:08.524 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:3
26 02-20 21:48:08.545 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:4
27 02-20 21:48:08.545 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:4
28 02-20 21:48:08.565 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:4
29 02-20 21:48:08.585 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:4
30 02-20 21:48:08.606 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:4

 

You had hoped that the system would work this way.

But in fact the system works like this:

So look at the source code and see why.

The default Exector in AsyncTask is this private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; see how SERIAL_EXECUTOR is defined

 1 public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); //This is an Executor for serial processing
 2 .........................
 3     private static class SerialExecutor implements Executor {
 4         final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
 5         Runnable mActive;
 6 
 7         public synchronized void execute(final Runnable r) {
 8             mTasks.offer(new Runnable() { //First drop the subthreads to be executed into the mTasks queue, which encapsulates Runnable once
 9                 public void run() {
10                     try {
11                         r.run();
12                     } finally {
13                         scheduleNext(); //The current sub-thread is processed, and the next one is processed.
14                     }
15                 }
16             });
17             if (mActive == null) {
18                 scheduleNext();
19             }
20         }
21 
22         protected synchronized void scheduleNext() { //Take the next element from the queue in turn and execute it serially
23             if ((mActive = mTasks.poll()) != null) {
24                 THREAD_POOL_EXECUTOR.execute(mActive); 
25             }
26         }
27     }

 

Ha-ha, I see now.

I don't know why Google is going to achieve it. I guess there are some benefits I don't understand. So is there any way to avoid it?

You can see that there is a THREAD_POOL_EXECUTOR above, which is also defined by an executor.

1     static {
2         ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
3                 CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
4                 sPoolWorkQueue, sThreadFactory);
5         threadPoolExecutor.allowCoreThreadTimeOut(true);
6         THREAD_POOL_EXECUTOR = threadPoolExecutor;
7     }

 

You can see that this allows a certain number of sub-threads to be processed in parallel.

The parameters are defined in this way.

    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    // We want at least 2 threads and at most 4 threads in the core pool,
    // preferring to have 1 less than the CPU count to avoid saturating
    // the CPU with background work
    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final int KEEP_ALIVE_SECONDS = 30;

Generally speaking, there are four CORE processes and 17 MAXIMUM_POOL_SIZE processes that allow simultaneous operation. (Note: This number is from my Glory 8 mobile phone. Other mobile phones may be different.)

And AsyncTask in Android provides a way:

1     public static void setDefaultExecutor(Executor exec) {
2         sDefaultExecutor = exec;
3     }

 

So the evasion method is that the default Exector can be set by this method, but this method is hide, that is, Google's hiding method, which is estimated to need a reflection to deal with. I'm lazy and I don't want to make it happen.

4, summary

Originally, I just wanted to write some knowledge about AsyncTask and Copy about the content on the Internet, but I didn't expect to find that most of the things on the Internet were wrong or missed the point. It seems that in the future, you still have to read the code by yourself. If you feel shallow on paper, you will know that you have to practice this matter.

The engineering code used in this article can be viewed in my github. Path: https://github.com/YanYoJun/AsyncTaskTest

Okay, that's it. If you think it's OK after you see it, please give me a compliment.

Posted by merck_delmoro on Sun, 31 Mar 2019 21:27:30 -0700