Androidasynctask Source Code Analysis
Reprint please indicate source: http://blog.csdn.net/lmj623565791/article/details/38614699, this article from: "Zhang Hongyang's Blog"
1. Overview
I believe that everyone is not unfamiliar with the asynctask, for the implementation of time-consuming tasks, and then update the UI is a tool, of course, is also a way to replace the thread + Handler. If you are not aware of the Handler mechanism, see: The Android Asynchronous message processing mechanism gives you a deep understanding of Looper, Handler, and message relationships.
2. Simple example
I believe everyone has written this code:
Package Com.example.zhy_asynctask_demo01;import Android.app.activity;import Android.app.progressdialog;import Android.os.asynctask;import Android.os.bundle;import Android.util.log;import Android.widget.textview;public class Mainactivity extends activity{private static final String TAG = "mainactivity";p rivate progressdialog mdialog;private Tex TView Mtextview; @Overrideprotected void OnCreate (Bundle savedinstancestate) {super.oncreate (savedinstancestate); Setcontentview (r.layout.activity_main); Mtextview = (TextView) Findviewbyid (R.ID.ID_TV); mdialog = new ProgressDialog ( this); Mdialog.setmax (+); Mdialog.setprogressstyle (progressdialog.style_horizontal); Mdialog.setcancelable ( FALSE); new Myasynctask (). Execute ();} Private class Myasynctask extends Asynctask<void, Integer, void>{@Overrideprotected Void OnPreExecute () { Mdialog.show (); LOG.E (TAG, Thread.CurrentThread (). GetName () + "OnPreExecute");} @Overrideprotected void Doinbackground (void ... params) {//load of simulated data, time-consuming task for (int i = 0; i < 100; i++) {try{thread.sleep;} catch (Interruptedexception e) {e.printstacktrace ();} Publishprogress (i);} LOG.E (TAG, Thread.CurrentThread (). GetName () + "Doinbackground"); return null;} @Overrideprotected void Onprogressupdate (Integer ... values) {mdialog.setprogress (values[0]); LOG.E (TAG, Thread.CurrentThread (). GetName () + "Onprogressupdate");} @Overrideprotected void OnPostExecute (void result) {//UI operation after data load is complete Mdialog.dismiss (); Mtextview.settext ("Load data SUCCESS "); LOG.E (TAG, Thread.CurrentThread (). GetName () + "OnPostExecute");}}}
The data required to enter a activity,activity from the network or other time-consuming operations, you can onpreexecute in Asynctask to complete some preparation operations, such as the previous example shows the progress dialog box And then in the Doinbackground time-consuming operation, in the time-consuming operation can be passed through publishprogress to onprogressupdate in the parameters, and then in the onprogressupdate can be UI operations, For example, the previous example updates the progress of the progress bar, and when the time-consuming task is completed, finally, the control data update UI is set in OnPostExecute, such as hiding the progress dialog box.
:
3. Source Code Analysis Note: This article is based on the source ANDORID-17, because and before 3.0 version changes, it is necessary to mark.
Then everyone must be curious, how asynctask is implemented in Android, the following source analysis: From the beginning of our execution of the asynchronous task, enter the Execute method:
Public final Asynctask<params, Progress, result> execute (params ... params) {return Executeonexecutor (sdefaul Texecutor, params);} Public final Asynctask<params, Progress, result> executeonexecutor (Executor exec, params ... params) { if (mstatus! = status.pending) {switch (mstatus) {case Running:throw n EW 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 is executed only once)"); }} mstatus = status.running; OnPreExecute (); Mworker.mparams = params; Exec.execute (mfuture); return this; }
18 Line: Set the current asynctask state to running, the above switch can also be seen, each asynchronous task can only be executed once before completion.
20 Rows: Executed OnPreExecute (), currently still in the UI thread, so we can do some preparatory work in it.
22 rows: Assigns the parameters we passed to the Mworker.mparams
23 Line: Exec.execute (mfuture)
I believe you will be confused about the mworker of 22 rows and the mfuture that appear in the 23 rows.
Mworker found this class:
private static abstract class Workerrunnable<params, Result> implements callable<result> { params[] Mparams;}
You can see that it is a subclass of callable and contains a mparams to hold the arguments we passed in, and see the code that initializes the Mworker:
Public Asynctask () { mworker = new Workerrunnable<params, result> () {public Result call () throws Exception { Mtaskinvoked.set (true); Process.setthreadpriority (process.thread_priority_background); Noinspection unchecked return Postresult (Doinbackground (Mparams)); } };/ /.... }
You can see that Mworker is initialized in the constructor method, and because it is an abstract class, a new implementation class is implemented here, the call method is set up, the calling method is Mtaskinvoked=true, and finally the Doinbackground ( Mparams) method, and returns the result value as a parameter to the Postresult method. You can see our doinbackground appear, below continue to see:
Private result Postresult (result result) { @SuppressWarnings ("unchecked") message message = Shandler.obtainmessage (Message_post_result, new asynctaskresult<result> (this, RESULT)); Message.sendtotarget (); return result;}
You can see that we are familiar with the asynchronous message mechanism in Postresult, passing a message, message.what to message_post_result;message.object= new Asynctaskresult (This,result);
private static class Asynctaskresult<data> { final asynctask mtask; Final data[] Mdata; Asynctaskresult (asynctask task, data ... data) { mtask = task; Mdata = data; } }
Asynctaskresult is a simple object that carries parameters.
Seeing this, I'm sure you will think that there must be a shandler somewhere, and that the Handlemessage method waits for incoming messages and the processing of messages.
private static final Internalhandler Shandler = new Internalhandler (); 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 are only one RESULT result.mTask.finish ( Result.mdata[0]); break; Case message_post_progress: result.mTask.onProgressUpdate (result.mdata); Break;}}}
Haha, appeared our handlemessage, can see, in received Message_post_result message, executed the Result.mTask.finish (Result.mdata[0]); Actually is our AsyncTask.this.finish (result), so look at the finish method
private void finish (result result) { if (iscancelled ()) { oncancelled (result); } else { OnPostExecute ( result); } Mstatus = status.finished; }
As you can see, if we call Cancel () then execute the oncancelled callback; call our OnPostExecute (result) in normal execution; The main call here is in Handler Handlemessage, So it's in the UI thread. If you don't understand the asynchronous messaging mechanism, see: The Android Asynchronous message processing mechanism gives you a deep understanding of Looper, Handler, and message relationships
Finally, the status is set to Finished.
Mwoker See, should be to our mfuture, still really the construction method to complete the Mfuture initialization, mworker as a parameter, the copy of its done method.
Public Asynctask () {... Mfuture = new Futuretask<result> (mworker) { @Override protected void Done () { try { Postresultifnotinvoked (Get ()); } 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);}} ;}
16 rows: The end of the task execution is called: postresultifnotinvoked (Get ()), and get () represents the return value of the call that gets mworker, which is result. And then look at the Postresultifnotinvoked method
private void postresultifnotinvoked (result result) { final Boolean wastaskinvoked = Mtaskinvoked.get (); if (!wastaskinvoked) { postresult (result);} }
If mtaskinvoked is not true, Postresult is executed, but mtaskinvoked is true at Mworker initialization, so this postresult is generally not performed.
All right, here we go. The Execute method appears in Mworker and Mfurture, but this is always the code that initializes the two objects and is not actually executed. Let's see where the real call executes.
In the Execute method:
Remember the 23 lines in the above execute: Exec.execute (mfuture)
exec is a sdefaultexecutor in Executeonexecutor (Sdefaultexecutor, params)
Let's see this sdefaultexecutor.
private static volatile Executor Sdefaultexecutor = serial_executor;public static final Executor serial_executor = new Serialexecu Tor ();p rivate static class Serialexecutor implements Executor {final arraydeque<runnable> mtasks = new Array Deque<runnable> (); Runnable mactive; Public synchronized void Execute (final Runnable R) {Mtasks.offer (new Runnable () {public void R Un () {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 sdefaultexecutor is actually an instance of Serialexecutor, which maintains a task queue internally, and looks directly at its execute (Runnable Runnable) method, putting Runnable into the mtasks team tail;
16-17 rows: Determine whether the current mactive is empty, or null to call the Schedulenext method
20 Line: Schedulenext, the team first task in the task queue is fetched directly, and if not NULL, the incoming thread_pool_executor is executed.
See why Thread_pool_executor is sacred:
public static final Executor thread_pool_executor =new threadpoolexecutor (core_pool_size, Maximum_pool_size, KEEP _alive, timeunit.seconds, Spoolworkqueue, sthreadfactory);
You can see that it is a thread pool that sets its own parameters, with the following parameters:
private static final int core_pool_size = 5;private static final int maximum_pool_size = 128;private static final int KEEP _alive = 1;private static final threadfactory sthreadfactory = new Threadfactory () {private final Atomicinteger MCount = n EW Atomicinteger (1);p ublic thread Newthread (Runnable r) { return new thread (R, "Asynctask #" + mcount.getandincrement ()); } };p rivate static final blockingqueue<runnable> spoolworkqueue = new Linkedblockingqueue<runnable> (10 );
See here, you might think that there is a thread pool behind, and the maximum support for 128 of threads concurrency, plus a 10-length blocking queue, you may feel that the fast call of 138 Asynctask subclass of the Execute method does not have problems, and greater than 138 throws an exception.
Not really, let's take a closer look at the code, review the Sdefaultexecutor, and actually call the Sdefaultexecutor.execute in Execute ():
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);}}}
As you can see, if there are 10 tasks calling the Execute (s synchronized) method at this time, the first task is queued, then mactive = Mtasks.poll ()) = NULL is fetched and assigned to MACTIVTE, Then hand it over to the thread pool for execution. Then the second task is queued, but at this point the mactive is not NULL, and the Schedulenext () is not executed; So if the first task is slow, 10 tasks will go into the queue; the time to really do the next task is when the thread pool executes the first task, Call the Schedulenext in the finally code block in runnable, so although there is a thread pool inside, the process of calling is still linear. The execution of one after another is equivalent to a single thread.
4. Summary
To this source code interpretation is complete, due to the relatively large coding span, we will review:
Public final Asynctask<params, Progress, result> execute (params ... params) {return Executeonexecutor (sdefaul Texecutor, params);} Public final Asynctask<params, Progress, result> executeonexecutor (Executor exec, params ... params) { if (mstatus! = status.pending) {switch (mstatus) {case Running:throw n EW 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 is executed only once)"); }} mstatus = status.running; OnPreExecute (); Mworker.mparams = params; Exec.execute (mfuture); return this; }
18 Line: Set the current asynctask state to running, the above switch can also be seen, each asynchronous task can only be executed once before completion.
20 Rows: Executed OnPreExecute (), currently still in the UI thread, so we can do some preparatory work in it.
22 rows: Assigns the parameters we passed in to Mworker.mparams, Mworker to a subclass of callable, and in the internal call () method, calls Doinbackground (Mparams), The resulting return value is executed as a postresult parameter, and the Handlemessage call is completed in Postresult by Shandler sending the message, and finally Shandler OnPostExecute.
Line 23: Exec.execute (mfuture), Mfuture is the real unit that executes the task, encapsulates the mworker, and then gives it to the thread pool for execution.
5, Publishprogress
Having said so much, we seem to have forgotten one way: publishprogress
Protected final void publishprogress (Progress ... values) { if (!iscancelled ()) { Shandler.obtainmessage ( Message_post_progress, New asynctaskresult<progress> (this, values)). Sendtotarget (); }}
It is also very simple to send a message directly using Shandler, and carry the value we passed in;
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 are only one RESULT result.mTask.finish ( Result.mdata[0]); break; Case message_post_progress: result.mTask.onProgressUpdate (result.mdata); Break;}}}
The invocation of our Onprogressupdate (Result.mdata) was carried out in Handlemessage.
6, Asynctask once defects
Remember before a face question often ask: Asynctask running principle is what? What's the flaw?
The previous answer to the flaw might be: Asynctask An exception occurred while executing multiple tasks concurrently. In fact, it's still there, In the previous 3.0 system will be implemented in support of multi-threaded concurrency, support concurrency is the number we calculated above 128, the blocking queue can hold 10, that is, to perform 138 tasks at the same time is no problem, and more than 138 will appear immediately java.util.concurrent.RejectedExe Cutionexception;
In the system above 3.0 including 3.0 will be single-threaded execution (that is, our above code analysis);
Empty say no: See the test code below:
Package Com.example.zhy_asynctask_demo01;import Android.app.activity;import Android.app.progressdialog;import Android.os.asynctask;import Android.os.bundle;import Android.util.log;import Android.widget.textview;public class Mainactivity extends activity{private static final String TAG = "mainactivity";p rivate progressdialog mdialog;private Tex TView Mtextview; @Overrideprotected void OnCreate (Bundle savedinstancestate) {super.oncreate (savedinstancestate); Setcontentview (r.layout.activity_main); Mtextview = (TextView) Findviewbyid (R.ID.ID_TV); mdialog = new ProgressDialog ( this); Mdialog.setmax (+); Mdialog.setprogressstyle (progressdialog.style_horizontal); Mdialog.setcancelable ( FALSE); for (int i = 1; I <= 138; i++) {new MyAsyncTask2 (). Execute ();} New Myasynctask (). Execute ();} Private class MyAsyncTask2 extends Asynctask<void,void, void>{@Overrideprotected void Doinbackground (void ... params) {TRY{LOG.E (TAG, Thread.CurrentThread (). GetName ()); Thread.Sleep (10000);} catch (Interruptedexception e) {e.printstacktrace ();} return null;}}}
You can see that I perform 138 asynchronous tasks in the For loop, and each asynchronous task needs to be executed in 10s; The following is tested using the 2.2 emulator:
The output is:
Asynctask#1-asynctask #128同时输出
Then after 10s, another 10 tasks are output.
Can analyze the result, get conclusion: Asynctask in 2.2 of the system support 128 tasks concurrently, at least support 10 task wait;
The following 138 tasks are changed to 139 tasks:
for (int i = 1; I <= 139; i++) {new MyAsyncTask2 (). Execute ();}
Run Result: Exception occurs: java.util.concurrent.RejectedExecutionException; So you can be sure that only 10 task waits are supported, and that more than 10 will immediately be an exception.
Simply say the reason for the exception: it is now 139 tasks, almost simultaneously committed, the thread pool supports 128 concurrency, and then the number of blocked queues is 10, when the 11th task commits the exception.
Simply look at the source code:
public static final Executor thread_pool_executor = new Threadpoolexecutor (core_pool_size, Maximum_pool_size, KEEP _alive, Timeunit.seconds, Spoolworkqueue, sthreadfactory);
See Threadpoolexecutor's Execute Method:
if (IsRunning (c) && workqueue.offer (command)) { int recheck = Ctl.get (); if (! isrunning (Recheck) && Remove (command)) reject (command); else if (workercountof (recheck) = = 0) Addworker (null, FALSE); } else if (!addworker (command, false)) reject (command);
The Workqueue.offer (command) returns False when the blocking queue is full, then executes the Addworker (Command,false) method and executes The Reject () method if False is returned.
Private Boolean Addworker (Runnable Firsttask, Boolean core) {... int WC = Workercountof (c); if (WC >= capacity | | WC >= (Core corepoolsize:maximumpoolsize)) return false; ...}
You can see that when the number of tasks is greater than capacity returns false and eventually throws an exception in reject ().
Above is the result of using the 2.2 simulator test;
The following changes the system to 4.1.1, which is my test machine Xiaomi 2s
To change the number of threads to 139 or even 1000, you can see the task one by one in the slow execution, will not throw anything abnormal, but the thread is a 1 of the execution at that;
Well, if you go to the interview now, asked the Asynctask flaw, can be divided into two parts, said, before 3.0, the maximum support 128 threads of concurrency, 10 tasks waiting. After 3.0, no matter how many tasks, will be in its internal single-threaded execution;
At this point, Asynctask source analysis is complete, I believe we have a deeper understanding of Asynctask ~ ~ ~
Test code click to download
Original link This article by Bean John Blog Backup expert remote One click release
Androidasynctask Source Code Analysis (reprint)