Android Asynctask full parsing takes you from the source perspective to thoroughly understand _android

Source: Internet
Author: User
Tags static class throwable

As we all know, the Android UI is thread-safe, and if you want to do UI operations on the Chengri, you'll need to take advantage of the Android asynchronous message processing mechanism. I have also written an article from the source level analysis of the Android asynchronous message processing mechanism.

But in order to make it easier for us to update the UI elements in the child threads, Android introduced a Asynctask class from version 1.5, which makes it very flexible and easy to switch from child threads to UI threads, and this is the protagonist of this article.
Asynctask early on in the Android API, so I'm sure most of my friends are already familiar with its usage. But today I'm going to start with the basic usage of asynctask, and then we'll come back together and analyze the asynctask source, see how it's done, and finally I'll introduce some secrets you don't know about Asynctask.
basic usage of asynctask
First look at the basic usage of asynctask, because Asynctask is an abstract class, so if we want to use it, we have to create a subclass to inherit it. When inheriting, we can specify three generic parameters for the Asynctask class, and the three parameters are intended for the following purposes:
1. Params
Parameters that need to be passed in when executing asynctask can be used in background tasks.
2. Progress
When the background task executes, if you need to display the current progress on the interface, use the generics specified here as the progress unit.
3. Result
When the task completes, if the result needs to be returned, the generic specified here is used as the return value type.
Therefore, one of the simplest custom asynctask can be written in the following ways:

Class Downloadtask extends Asynctask<void, Integer, boolean> { 
  ... 

Here we specify the first generic parameter of Asynctask as void, which means that you do not need to pass parameters to the background task when executing asynctask. The second generic parameter, specified As Integer, represents the use of integer data as a progress display unit. The third generic parameter is specified as Boolean, which means that Boolean data is used to feed back execution results.
Of course, our custom downloadtask is still an empty task and does not do anything practical, and we need to rewrite several methods in Asynctask to get the task done. The following four methods are often required to rewrite:
1. OnPreExecute ()
This method is invoked between the background task's start execution, for some interface initialization, such as displaying a progress bar dialog box.
2. Doinbackground (Params ...)
All of the code in this method runs in child threads, and we should be here to handle all the time-consuming tasks. Once the task is complete, you can return the execution result of the task by returning statements, and if the third generic parameter of asynctask specifies void, the task execution result can not be returned. Note that there is no UI action in this method, and if you need to update the UI elements, such as feedback on the progress of the current task, you can call Publishprogress (Progress ...). method to complete.
3. Onprogressupdate (Progress ...)
when publishprogress (Progress ...) is invoked in a background task. Method, the method is quickly invoked, and the parameters carried in the method are passed over the background task. The UI can be manipulated in this way, and the interface elements can be updated appropriately using the values in the parameters.
4. OnPostExecute (Result)
This method is invoked quickly when a background task finishes executing and returns through a return statement. The returned data is passed as a parameter to this method, and you can use the returned data for some UI actions, such as reminding the results of the task execution, and closing the Progress Bar dialog box.
Therefore, a more complete custom asynctask can be written in the following way:

 class Downloadtask extends Asynctask<void, Integer, boolean> {@Override p 
  rotected void OnPreExecute () {progressdialog.show (); @Override protected Boolean doinbackground (Void ... params) {try {while (true) {int downl 
        Oadpercent = Dodownload (); 
        Publishprogress (downloadpercent); 
        if (Downloadpercent >=) {break; 
    The catch (Exception e) {return false; 
  return true; } @Override protected void Onprogressupdate (Integer ... values) {progressdialog.setmessage ("Current Download progress:" + values 
  [0] + "%"); 
    } @Override protected void OnPostExecute (Boolean result) {Progressdialog.dismiss (); 
    if (result) {Toast.maketext (context, "Download successful", Toast.length_short). Show (); 
    else {toast.maketext (context, "Download Failed", Toast.length_short). Show (); } 
  } 
} 

Here we simulate a download task, perform the specific download logic in the Doinbackground () method, display the current download progress in the Onprogressupdate () method, and prompt the execution result of the task in the OnPostExecute () method. If you want to start this task, simply call the following code:
New Downloadtask (). Execute (); 
The above is the basic usage of asynctask, how do you feel that switching between child threads and UI threads is a lot more flexible? We don't need to think about asynchronous message processing mechanisms, and we don't have to use a handler to send and receive messages, just call the Publishprogress () method to easily switch from a child thread to a UI thread.
parsing Asynctask's source code
Although Asynctask is so easy to use, but do you know how it is implemented? Then, we will analyze the source code of Asynctask, the realization of its principle of a probe. Note Here I choose the Android 4.0 source code, if you are looking at other versions of the source code, there may be some discrepancies.
You can see from previous Downloadtask code that before you start a task, you need to create an instance of it, so let's look at the source code in the Asynctask constructor as follows:

 public Asynctask () {mworker = new workerrunnable<params, result> () {public Resul 
      T call () throws Exception {Mtaskinvoked.set (true); 
      Process.setthreadpriority (Process.thread_priority_background); 
    Return Postresult (Doinbackground (mparams)); 
  } 
  }; Mfuture = new Futuretask<result> (mworker) {@Override protected void done () {try {final 
        Result is = get (); 
      postresultifnotinvoked (result); 
      catch (Interruptedexception e) {ANDROID.UTIL.LOG.W (Log_tag, E); 
            catch (Executionexception e) {throw new RuntimeException ("An error occured while executing doinbackground ()", 
      E.getcause ()); 
      catch (Cancellationexception e) {postresultifnotinvoked (null); The catch (Throwable t) {throw new RuntimeException ("An error occured while executing" + "doinbackgr 
      Ound () ", t); 
} 
    } 
  }; } 

Although this code looks a bit long, in fact there is no specific logic to be executed, only two variables are initialized, Mworker and Mfuture, and Mworker are passed as arguments when the mfuture is initialized. Mworker is a callable object, Mfuture is a Futuretask object that is temporarily saved in memory and will be used later.
Then, if you want to start a task, you need to invoke the Execute () method of the task, so let's take a look at the source code for the Execute () method, as follows:

Public final Asynctask<params, Progress, result> execute (Params ... Params) {return 
  executeonexecutor ( Sdefaultexecutor, params); 
 

Simple a bit too much, only a line of code, just call the Executeonexecutor () method, then the specific logic should be written in this method, quickly follow up to see:

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

Sure enough, the code here looks normal. As you can see, the OnPreExecute () method is invoked on line 15th, thus proving that the OnPreExecute () method is first executed. But the next code is not clear, how did not see where there is the call Doinbackground () method? Don't worry, you'll find it. We see that the executor execute () method is invoked on line 17th, and the Mfuture object that was previously initialized is passed in, so what is the executor object? Look at the Execute () method above, which is to pass in a sdefaultexecutor variable, and then find out where the sdefaultexecutor variable is defined, and the source code looks like this:

public static final Executor serial_executor = new Serialexecutor (); 
..... private static volatile Executor sdefaultexecutor = Serial_executor; 

As you can see, here we first create a Serial_executor constant and then assign the Sdefaultexecutor value to this constant, which means that the execute () method that you just called in the Executeonexecutor () method, In fact, the Execute () method in the Serialexecutor class that is invoked. Then we naturally have to look at the source code of Serialexecutor, as follows:

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 () { 
        try {
   r.run (); 
        } finally { 
          schedulenext (); 
        }}} 
    ); 
    if (mactive = = null) { 
      schedulenext (); 
    } 
  } 
 
  protected synchronized void Schedulenext () { 
    if (mactive = Mtasks.poll ())!= null) { 
      Thread_pool_ Executor.execute (mactive);}} 
 

There is also an execute () method in the Serialexecutor class, where all the logic in this method is executed in the child thread, and note that this method has a runnable parameter, so what is the current value of this parameter? Of course is the Mfuture object, that is, in line 9th we want to call the Futuretask class of the Run () method, and in this method will call the sync internal class Innerrun () method, so we directly look at the Innerrun () method of the source code:

void Innerrun () { 
  if (!compareandsetstate (READY, RUNNING)) return 
    ; 
  Runner = Thread.CurrentThread (); 
  if (getState () = = RUNNING) {//Recheck after setting thread 
    V result; 
    try {result 
      = Callable.call (); 
    } catch (Throwable ex) { 
      setexception (ex); 
      return; 
    } 
    Set (result); 
  } else { 
    releaseshared (0);//Cancel 
  } 
} 

As you can see, the call () method of the callable is called on line 8th, so what is this callable object? In fact, the Mworker object that was passed in when the Mfuture object was initialized, and the call () method invoked at the beginning, which was first specified in the Asynctask constructor, we took it out and looked at it, and the code looks like this:

Public result Call () throws Exception { 
  Mtaskinvoked.set (true); 
  Process.setthreadpriority (process.thread_priority_background); 
  Return Postresult (Doinbackground (Mparams)); 
} 

In the parameters of the Postresult () method, we finally found the call to the Doinbackground () method, which, though a lot of turnover, is still running in the child thread, so that's why we can Doinbackground () method to handle time-consuming logic. The results returned by the Doinbackground () method are then passed to the Postresult () method, which is the source code for the following example:

Private result postresult {message message 
  = Shandler.obtainmessage (Message_post_result, 
      new Asynctaskresult<result> (this, result)); 
  Message.sendtotarget (); 
  return result; 
} 

If you are already familiar with the asynchronous message processing mechanism, this code must be very simple for you. Here a message is sent using the Shandler object, which carries the Message_post_result constant and a Asynctaskresult object that represents the result of the task execution. This Shandler object is an instance of the Internalhandler class, and this message is bound to be processed in the Internalhandler handlemessage () method later. Internalhandler's source code looks like this:

private static class Internalhandler extends Handler { 
  @SuppressWarnings ({"Unchecked", "Rawuseofparameterizedtype "}" 
  @Override public 
  void handlemessage (Message msg) { 
    Asynctaskresult result = (Asynctaskresult) msg.obj; 
    switch (msg.what) {case 
      Message_post_result: 
        //There be only one result 
        result.mTask.finish ( Result.mdata[0]); 
        break; 
      Case message_post_progress: 
        result.mTask.onProgressUpdate (result.mdata); 
        break; 
    }}} 
 

The type of message is judged here, and if this is a message_post_result message, the Finish () method is executed, and if this is a message_post_progress message, the onprogressupdate is executed ( Method The source of the finish () method is as follows:

private void finish (result result) { 
  if (iscancelled ()) { 
    oncancelled (result); 
  } else { 
    OnPostExecute (result); 
  } 
  Mstatus = status.finished; 
} 

As you can see, if the current task is canceled, the oncancelled () method is invoked, and if it is not canceled, the OnPostExecute () method is invoked so that the execution of the current task is all over.
We note that in the Handlemessage () method just Internalhandler, there is also a message_post_progress message type that is used for the current progress, and it is the onprogressupdate ( method, so when will this message be sent? I believe you have guessed, check the Publishprogress () method of the source code, as follows:

Protected final void publishprogress (Progress ... values) { 
  if (!iscancelled ()) { 
    Shandler.obtainmessage ( Message_post_progress, 
        New asynctaskresult<progress> (this, values)). Sendtotarget (); 
  } 
 

It's very clear! For this reason, the publishprogress () method is invoked in the Doinbackground () method to switch from a child thread to a UI thread to complete the update of the UI element. In fact, there is no mystery, because in the final analysis, Asynctask is also the use of asynchronous message processing mechanism, just do a very good encapsulation.
Read here and believe that you have understood the role, principle, and timing of each callback method in Asynctask.
About Asynctask Secrets you don't know
I have to say that when we analyzed serialexecutor, we did not analyze it very carefully, just concerned that it would call the run () method in Mfuture, But as to when it will be invoked we have not studied further. In fact, Serialexecutor is also asynctask the most important modification after version 3.0, which is used as a constant in asynctask, so all asynctask instances in the entire application share the same serialexecutor. Let's do a more detailed analysis of this class, and I'll post the code again for easy reading:

 private static class Serialexecutor implements Executor {final arraydeque<runnabl 
  e> mtasks = new arraydeque<runnable> (); 
 
  Runnable mactive; 
        Public synchronized void Execute (final Runnable R) {mtasks.offer-new Runnable () {public void run () { 
        try {r.run (); 
        finally {schedulenext (); 
    } 
      } 
    }); 
    if (mactive = = null) {Schedulenext (); } protected synchronized void Schedulenext () {if (mactive = Mtasks.poll ())!= null) {Thread_pool_ 
    Executor.execute (mactive); } 
  } 
} 

You can see that serialexecutor is using the Arraydeque queue to manage runnable objects, and if we start a number of tasks at once, the first time we run the Execute () method, The Arraydeque offer () method is invoked to add the incoming Runnable object to the end of the queue, and then to determine if the Mactive object is equal to NULL, and the first run is, of course, NULL, and the Schedulenext () method is invoked. In this method, a value is taken from the header of the queue, assigned to the Mactive object, and then called Thread_pool_executor to perform the removed Runnable object. Then there is a new task to be executed, and the offer () method is also invoked to add the incoming runnable to the end of the queue, but when you do a non-empty check on the Mactive object, you will find that the Mactive object is no longer null. The Schedulenext () method is no longer invoked.
so the tasks that are added later are never dealt with? Of course not, look at the incoming Runnable anonymous class in the Offer () method, where a try finally code block is used, and the Schedulenext () method is invoked in finally, to ensure that this method is invoked regardless of what happens. That is, each time a task is executed, the next task will be executed, serialexecutor imitate the effect of a single thread pool, if we quickly start a lot of tasks, at the same time only one thread is executing, the rest is in the waiting state. Android Photo Wall application implementation, no more pictures are not afraid to crash the results of the example in this article also confirm this conclusion.
But you may not yet know that this class was not serialexecutor before Android 3.0, when a Sexecutor constant was built directly in Asynctask, and the number of threads that were able to run at the same time was defined for the total size of the thread pool. , the code looks like this:

private static final int core_pool_size = 5; 
private static final int maximum_pool_size = 128; 
private static final int keep_alive = ten; 
..... private static final Threadpoolexecutor Sexecutor = new Threadpoolexecutor (core_pool_size, 

Maximum_pool_size, Keep_alive, Timeunit.seconds, Sworkqueue, sthreadfactory);
As you can see, the number of threads that can run at the same time is 5, and the total thread pool size is 128. That is, when we start 10 tasks, only 5 tasks can be executed immediately, while the other 5 tasks wait, and when a task completes, the 6th task starts, and so on. The maximum number of threads in the thread pool is 128, and when we try to add a 129th task, the program crashes.
As a result, the Asynctask changes in version 3.0 are quite large, asynctask can have 5 tasks to perform at the same time before 3.0, and Asynctask can only have one task in the execution after 3.0. Why does the number of tasks that can be performed concurrently after an upgrade become less? This is because the updated asynctask has become more flexible and can be configured freely if you do not want to use the default thread pool. For example, use the following code to start a task:

Executor exec = new Threadpoolexecutor (A, 
    timeunit.seconds, New linkedblockingqueue<runnable> ()); C8/>new Downloadtask (). Executeonexecutor (exec); 

This allows you to use one of our custom executor to perform tasks, instead of using Serialexecutor. The effect of the above code allows 15 tasks to be performed at the same time, and can store up to 200 tasks.
Well, here we have all the important things about asynctask in a simple and understandable way, and I believe we will be more handy when we use it in the future.

Related Article

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.