Preface:Beginner Asynctask, just want to study the realization of its source code, Zennai source read several times have not read, so shelved. Recent whim, and saw some source code, such as Handlerthread,intentservice,asyncqueryhandler, harvest quite deep, so I want to go back to study under Asynctask, did not expect this time incredibly easy to read ...
Text:Note: 1. Before reading this article, readers must have an understanding of the handler mechanism of Android and the thread pool in j.u.c, and the 2.AsyncTask usage will not be described. 3. Different versions of Asynctask content are somewhat different.
First, it is clear that Asynctask is an abstract class that accepts three generic parameters, a table representing the type of parameter required for a task, a task progress type, and a result type.
Developers must override the Doinbackground method after inheriting Asynctask, and other methods such as OnPostExecute are rewritten on demand.
Inside it is a static global thread pool variable thread_pool_executor,The task in Asynctask's doinbackground is performed by this thread pool.
public static final Executor thread_pool_executor = new Threadpoolexecutor (core_pool_size, Maximum_pool_size, KEEP _alive, timeunit.seconds, Spoolworkqueue, sthreadfactory);
threadpoolexecutor each parameter means the following, Core_pool_size is the number of core threads, Maximum_pool_size is the maximum number of threads in the thread pool, keep_ The Alive parameter indicates that when the thread pool's number of threads is greater than the number of core threads, the excess thread will be reclaimed when the idle time exceeds keep_alive, Spoolworkqueue is the task queue, and a runnable is stored. Sthreadfactory is a thread factory that is used to create threads in the thread pool. All of these parameters are already defined inside the Asynctask:
private static final int core_pool_size = 5; private static final int maximum_pool_size =; private static final int keep_alive = 1; private static final Threadfactory sthreadfactory = new Threadfactory () { private final atomicinteger mCount = new Ato Micinteger (1); Public Thread Newthread (Runnable r) { return new Thread (R, "Asynctask #" + mcount.getandincrement ()); } }; private static final blockingqueue<runnable> Spoolworkqueue = new linkedblockingqueue<runnable> (10);
Asynctask does not use the thread pool directly, but instead carries out a layer of "wrapper", which is the Serialexecutor, a serial thread pool.
/** * An {@link Executor} This executes tasks one at a time in serial * order. This serialization are global to a particular process. * /public static final Executor serial_executor = new Serialexecutor ();
look at its specific implementation:
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);}}}
When calling the Execute method, first wrap the runnable (that is, add the try-finally block) and then join the queue, then determine whether the current mactive is empty, the first call, and this value is empty, so the Schedulenext method is called. The task of removing the head from the queue is given to the thread pool thread_pool_executor processing, and the processing is done by first executing the previous runnable run method, then performing schedulenext out of the queue runnable, so loop, Until the queue is empty, mactive is empty again. We have found that, after such treatment,
All tasks will be executed serially。
so we need to note that if all two asynctask call execute, if one of the Asynctask tasks takes a very long time, this will cause another Asynctask task to be queued, unable to execute, Because they share the same thread pool and the pool is a serial execution task.
Then look at the other Members:
private static final int message_post_result = 0x1;//Current Message type---> Task completion message private static final int message_post_ PROGRESS = 0x2;//Current Message type --Progress message private static final Internalhandler Shandler = new Internalhandler (); private static volatile Executor Sdefaultexecutor = serial_executor;//default thread pool private final Workerrunnable<params , result> Mworker; Private final futuretask<result> mfuture; Private volatile status Mstatus = status.pending;//Current state private final Atomicboolean mcancelled = new Atomicboolean (); private final Atomicboolean mtaskinvoked = new Atomicboolean ();
Sdefaultexecutor indicates that the default thread pool is a serial pool, mstatus indicates the current task status, and status is an enumeration type:
public enum Status { /** * Indicates, the task has not been executed yet. */ pending,//waiting to execute /** * Indicates that task is running. */ running,//execute /** * indicates that {@link Asynctask#onpostexecute} have finished. */ finished,//end of Execution }
Focus on Internalhandler implementation:
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;}}}
Internalhandler inherits from Handler, and Handlemessage is replicated, and the method is processed according to the message type .If the task completes, the finish method is called, and the finish method calls oncancelled or OnPostExecute based on the task state (cancel or complete), which is the callback method, Typically we update the UI in OnPostExecute based on task execution results, which is why the document requires Asynctask to be created in the UI thread, and our handler must be bound to the looper of the UI thread to update the UI. And the child threads are not looper by default.
private void finish (result result) { if (iscancelled ()) { oncancelled (result); } else { OnPostExecute ( result); } Mstatus = status.finished; }
if it is a message (message_post_progress) that updates the progress, call the Onprogressupdate method.
The next issue we're concerned with IS
where is the Doinbackground method called ? Lenovo Asynctask Use, when we create the good one Asynctask instance, we call its Execute method, so the Doinbackground method should be executed in the Execute method to find its implementation:
Public final Asynctask<params, Progress, result> execute (params ... params) { return Executeonexecutor ( Sdefaultexecutor, params); }
instead of calling Doinbackground directly, the Executeonexecutor method is called:
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; }
The role of the Executeonexecutor method is to perform tasks using the specified thread pool, which of course uses sdefaultexecutor, which is serialexecutor, but we notice that this method is public, that is to say
We can manually configure the thread pool so that asynctask is executed in parallel, and the simplest way is to use internally defined Thread_pool_executor. The Executeonexecutor method first checks the current state, if not the pending state throws an exception, immediately after the modified state is running, and then calls the OnPreExecute method (preprocessing, I believe everyone is not unfamiliar).
The key code is Exec.execute (mfuture), the function of this line of code is to perform mfuture defined tasks, mfuture for the Futuretask type, Futuretask is a runnable implementation class (J.U.C), so as a parameter to the thread pool Execute method, we find its definition:
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);}} ;
As we all know, Futuretask constructs must pass in a callable implementation class, the thread finally executes is the callable call method (does not understand the Java thread Concurrency Library), so mworker must be callable's implementation class:
private static abstract class Workerrunnable<params, Result> implements callable<result> { params[] Mparams; } 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)); } ;
Finally, we found the Doinbackground method in the Workrunnable method, after the hardships Ah!! The last question left:
How is the message sent to handler?
1. The message that the task was completed was sent through the Postresult method:
Private result Postresult (result result) { @SuppressWarnings ("unchecked") message message = Shandler.obtainmessage (Message_post_result, new asynctaskresult<result> (this, RESULT)); Message.sendtotarget (); return result; }
This asynctaskresult encapsulates the data field and the object itself:
private static class Asynctaskresult<data> { final asynctask mtask; Final data[] Mdata; Asynctaskresult (asynctask task, data ... data) { mtask = task; Mdata = data; } }
2. Task progress update messages are sent via the Publishprogress method:
Protected final void publishprogress (Progress ... values) { if (!iscancelled ()) { Shandler.obtainmessage ( Message_post_progress, New asynctaskresult<progress> (this, values)). Sendtotarget (); } }
At this point, Asynctask source analysis finished!
A little bit.
Summary:
1.AsyncTask is the encapsulation of the thread pool and handler, but the task is executed serially by default, and it is important to note that multiple Asynctask instances share the same thread pool (the thread pool is static);
2. High concurrency tasks do not recommend the use of Asynctask, but should be used instead of a thread pool;
3. Be sure to create asynctask in the main thread, because the handler inside the Asynctask must be bound to the looper of the main thread when it is created;
4. Only time-consuming tasks can be performed in the Doinbackground method, other methods such as OnPreExecute, OnPostExecute, and so on are run on the main thread.
"Android Notes" Asynctask source analysis