Asynctask believe that everyone is not unfamiliar, it is to simplify the asynchronous request, update the UI operation was born. Using it not only completes our network time-consuming operations, but it also allows us to update the UI components we need directly after the time-consuming operation. This makes it a hot Web request tool class in Android development.
And today we will be in the form of source analysis to thoroughly learn the process of its implementation.
First, let's take a look at Asynctask's definition form:
public abstract class AsyncTask<Params, Progress, Result> {}
First Asynctask It is an abstract class, consisting of three generic types, with the following meanings:
- Params: It represents the type of the request parameter
- Progress: The type of progress performed on a task
- Result: Returns the type of the result
However, the above three parameters must not necessarily be set to void when not required, and no return type.
Then we look at its execution process, including the following methods:
Execute (params ... params), which is called when we perform an asynchronous operation, indicating that the task is beginning to execute.
protected void OnPreExecute () {}, after invoking the Execute method, the method is executed, it executes in the UI thread, used to initialize some UI space, etc.
Protected abstract Result Doinbackground (params): The method is executed after the execution of the OnPreExecute, it executes in the background, and accepts an array parameter of type Params, is used to request a network, and it returns the result of a type. The method can update the request progress while executing the network request, calling Publishprogress (Progress ... values).
protected void Onprogressupdate (Progress ... values), if the Publishprogress method is called in the Doinbackground method, then the method will be executed, It is performed on the UI thread, constantly changing the progress according to values, to achieve the desired effect.
protected void OnPostExecute (result result), which is executed after the Doinbackground method has been executed, and can be followed by subsequent UI operations based on the results returned by the Doinbackground. This shows that it is working in the UI thread.
After a series of methods above, a complete Aysnctask request formally completes the task. Not only does the UI component, which is also updated when the time-consuming operation is completed, is its charm. But this time you have a question, the above method is that you say the execution of which is executed, which is how to execute it?
Then the official unveiling of its Lushan is the next.
Before formally introducing its source code, you must know the process that the new class performs:
In the process of new, it first loads the member variables of the parent class it inherits and constructs the method.
Then load your own member variables and construct the method.
The order is not to become.
So, what exactly does it load in our execution of new Asynctask ()?
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 ); public static final Executor thread_pool_executor = new Threadpoolexecutor (core_pool_size, Maximum_pool_size, K Eep_alive, Timeunit.seconds, Spoolworkqueue, sthreadfactory); public static final Executor serial_executor = new Serialexecutor (); private static final int message_post_result = 0X1; private static final int message_post_progress = 0x2; private static volatile Executor sdefaultexecutor = Serial_executor; private static Internalhandler Shandler; Private final Workerrunnable<params, result> Mworker; Private final futuretask<result> mfuture; Private volatile Status mstatus = status.pending; Private final Atomicboolean mcancelled = new Atomicboolean (); Private final Atomicboolean mtaskinvoked = new Atomicboolean (); private static class Serialexecutor implements executor{...} public enum Status {PENDING, RUNNING, finished,}
See such a big pile is not very hemp scalp, in fact carefully split down, you mainly see a few variables can.
Thread_pool_executor: This member variable from its thread_pool_executor = new threadpoolexecutor can be seen as a thread pool, Several parameters are required in the threadpoolexecutor thread pool, such as corepoolsize (number of core threads), Maximumpoolsize (maximum number of threads), WorkQueue (Task queue), Threadfactory (thread engineering) And so on, so like Core_pool_size,spoolworkqueue, sthreadfactory and other member variables, just to configure this thread pool.
Sdefaultexecutor This member variable is the default thread scheduling task, from the above can be seen Serial_executor is a serialized task scheduling, from sdefaultexecutor = Serial_executor; It is clear that the Sdefaultexecutor Task Scheduler is executed in sequence.
Shandler as the name implies is a handler,mworker is a worker thread, Mfuture is a futuretask,futuretask is dedicated to manage runnable thread, Mstatus is an enumeration, there are three kinds of states , which are not executed, executed, and executed, the default state is the state of execution.
So we just have to understand that the above several variables can not be afraid of it a bunch of initialized members.
And then we're looking at Aysnctask's construction methods that specifically do those things:
Public Asynctask () {mworker = new workerrunnable<params, result> () {public Result call () throws Exception {Mtaskinvoked.set (true); Process.setthreadpriority (Process.thread_priority_background); Noinspection unchecked result = Doinbackground (mparams); Binder.flushpendingcommands (); return Postresult (Result); } }; Mfuture = new Futuretask<result> (mworker) {@Override protected void done () {T ry {postresultifnotinvoked (get ()); } catch (Interruptedexception e) {ANDROID.UTIL.LOG.W (Log_tag, E); } catch (Executionexception e) {throw new RuntimeException ("An error occurred while executing doinback Ground () ", E.getcause ()); } catch (Cancellationexception e) { Postresultifnotinvoked (NULL); } } }; }
Simply put, the Asynctask member variable only initializes two variables, Mworker and mfuture. These two variables are very important, and all subsequent executions are composed or guided by these two variables.
First Mworker is an abstract inner class instance, which is a task thread that implements callable
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { Params[] mParams; }
Then Mfuture is a management class for the task thread. Specifically for managing task threads, we can get better control of our tasks and see how it's constructed:
public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable }
is to accept our Mworker object and set our own state to new.
The above is all that is done in the new asynctask, nothing more than initializing some data and variables.
Let's take a look at the official implementation of Aysnctask.
All we know is that the method invoked to open a Asynctask task is the Execute method, which must be called in the main thread.
public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }
After invoking the Execute method, the method does nothing but passes the initialized default sequence task thread Sdefaultexecutor and the data params passed in to Executeonexecutor (). So we're looking at what this method does:
Public Final asynctask<params, Progress, result> executeonexecutor (Executor exec, Params ... pa Rams) {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 is executed only once)"); }} mstatus = status.running; OnPreExecute (); Mworker.mparams = params; Exec.execute (mfuture); return this; }
The Executeonexecutor () method first determines the execution state of the asynctask, and if it is executing or has finished executing, it will report an illegalstateexception exception, Tells you that the thread is either executing or has been executed.
The Asynctask request task can be performed only if it is not executed, and then it changes the execution state of the asynctask directly to Status.running, telling other tasks that the asynctask is executing and maintaining consistency of execution results. The OnPreExecute () is then executed, since the Execute method must be executed in the main thread, so far it has been run in the main thread, proving that the OnPreExecute () method is running in the main thread.
protected void onPreExecute() { }
OnPreExecute source does not do anything, this for us, only need to rewrite the method can be in the main thread to do some initialization of the UI components and other operations.
- The next step is to assign the data we pass to the mparams variable of mworker, and then call the Exec.execute (Mfuture) method, which we know through the Execute method that exec is actually a sdefaultexecutor, Sdefaultexecutor is actually a serialexecutor sequence thread, and mfuture we know very well in the construction method that it is a manageable task thread that encapsulates the mworker thread, So when we call Sdefaultexecutor's Execute method and pass it into the mfuture task thread, what exactly does it do, we look at its source code:
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); } }
Source code We know very clearly that the ultimate goal in the Execute method is to assign the Mfuture task thread to a runnable thread and put it in the thread_pool_executor thread pool, by Thread_pool_ Executor thread pool to perform mfuture threading tasks.
Then let's look at the main things that are done in the Execute method in the Thread_pool_executor thread pool:
public void execute(Runnable command) { if (command == null) throw new NullPointerException(); int c = ctl.get(); if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) return; c = ctl.get(); } 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 thread_pool_executor thread pool primarily determines whether the passed thread is empty, is less than the number of core threads held in the current thread pool, and executes the Addworker (command, True) method directly if it is less than Here's a look at the implementation in the Addworker method:
Private Boolean Addworker (Runnable Firsttask, Boolean core) {... (The preceding code is omitted) Boolean workerstarted = false; Boolean workeradded = false; Worker w = null; try {w = new Worker (firsttask); Final Thread t = w.thread; if (t! = null) {final Reentrantlock mainlock = This.mainlock; Mainlock.lock (); try {int rs = runstateof (Ctl.get ()); if (Rs < SHUTDOWN | | (rs = = SHUTDOWN && Firsttask = = null)) {if (t.isalive ()) throw new Illegalthreadstateexception (); Workers.add (w); int s = workers.size (); if (S > largestpoolsize) largestpoolsize = s; Workeradded = true; }} finally {Mainlock.unlock (); } if (workeradded) {T.start (); Workerstarted = true; }}} finally {if (! workerstarted) addworkerfailed (w); } return workerstarted; }
We only look at the main logic, the first is to store our mfuture task thread in the object of a worker, and then get the mfuture thread from the Worker object and assign the value to thread. The Worker object is then placed in the HashSet data collection object of the workers, after obtaining the size of the hashset and making some judgments, setting the workeradded to True, and finally opening the T.start () thread, which enters the child thread .
So what happens next in the thread that's turned on?
We start with the above Analysis guide T.start () is a mfuture asynchronous task thread, then where does it execute?
Careful friends can find that the original is in the Serialexecutor in the Execute method of our mfuture run () is already waiting for the thread to start, then, I now go to see Mfuture Run () method of what to do
public void run() { if (state != NEW || !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread())) return; try { Callable<V> c = callable; if (c != null && state == NEW) { V result; boolean ran; try { result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result); } } finally { runner = null; int s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } }
This code is simple enough to see at a glance is to take advantage of the Mworker object instance that we passed at the time of initialization for Mfuture and invoke its call () method, first of all, let's see what the follow-up is in this method, regardless of how it was implemented.
It then obtains an execution result, sets a Boolean type of ran to true, and then invokes set (result) according to Ran, and passes the result into the following to see the source of the set:
protected void set(V v) { if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) { outcome = v; U.putOrderedInt(this, STATE, NORMAL); // final state finishCompletion(); } }
It mainly calls the finishcompletion (); Look at the source code of Finishcompletion:
private void finishCompletion() { // assert state > COMPLETING; for (WaitNode q; (q = waiters) != null;) { if (U.compareAndSwapObject(this, WAITERS, q, null)) { for (;;) { Thread t = q.thread; if (t != null) { q.thread = null; LockSupport.unpark(t); } WaitNode next = q.next; if (next == null) break; q.next = null; // unlink to help gc q = next; } break; } } done(); callable = null; // to reduce footprint }
After executing call, some objects are restored, and done () is called, which is the implementation we see in the Asynctask constructor method:
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 occurred while executing doInBackground()", e.getCause()); } catch (CancellationException e) { postResultIfNotInvoked(null); } } };
We also said that Futuretask is mainly used to manage the asynchronous thread task, then there is a good embodiment in the done method, in this method, it will determine whether the results of the execution is successful, if there is no sent after the success, if there is no message sent it, if the result is executed successfully, But without being sent it will send the final execution result:
private void postResultIfNotInvoked(Result result) { final boolean wasTaskInvoked = mTaskInvoked.get(); if (!wasTaskInvoked) { postResult(result); } }
The contents of the Postresult method we're going to go a little further, so now let's look at how call () in Mworker is implemented:
mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked Result result = doInBackground(mParams); Binder.flushPendingCommands(); return postResult(result); } };
Here we finally see a method that we are familiar with Doinbackground (), which can also be known that it is actually running on a child thread, whereas the Doinbackground () method is an abstract method in the Asynctask class:
protected abstract Result doInBackground(Params... params);
Then we can do some time-consuming network and IO operations directly in the rewrite Doinbackground ().
Here, if you call the Publishprogress method in Doinbackground () to update the progress, let's see how it's done:
protected final void publishProgress(Progress... values) { if (!isCancelled()) { getHandler().obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget(); } }
The Publishprogress method is to update the progress by sending a flag that updates the progress through Hangler. The Hangler here accepts the message below and executes the result together.
Finally Doinbackground () execution of the resulting result will also be passed to postresult (result); In the method, now let's look at its source code implementation:
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result; } private static Handler getHandler() { synchronized (AsyncTask.class) { if (sHandler == null) { sHandler = new InternalHandler(); } return sHandler; } } private static class AsyncTaskResult<Data> { final AsyncTask mTask; final Data[] mData; AsyncTaskResult(AsyncTask task, Data... data) { mTask = task; mData = data; } }
Postresult first encapsulates the Asynctaskresult object Doinbackground The result of the asynchronous execution, and then gets to a handler that sends a message through the messaging mechanism to switch to the main thread to replace the UI interface. The message processing mechanism is not part of this blog post, so don't dwell on it, so let's see how the handler handles the message.
private static class InternalHandler extends Handler { public InternalHandler() { super(Looper.getMainLooper()); } @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"}) @Override public void handleMessage(Message msg) { AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj; switch (msg.what) { case MESSAGE_POST_RESULT: result.mTask.finish(result.mData[0]); break; case MESSAGE_POST_PROGRESS: result.mTask.onProgressUpdate(result.mData); break; } } }
Handler is mainly based on the message flag to distinguish whether to update the progress or execution results:
If it is the update progress, call Asynctask's Onprogressupdate method to update the content, because through handler has been transformed into the main thread, so we can override the method when the UI component is updated directly.
If it is the execution result then Asynctask's finish (Result.mdata[0]), and pass the result data in the past, to see how the finish () is implemented:
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }
The finish () method is also very simple, first of all to determine whether to cancel the thread, otherwise the OnPostExecute (result) method is executed, so we can update our UI component directly after rewriting the OnPostExecute method.
Finally, the state of the asynctask is changed to completion, and the entire Asynctask life cycle is completed.
Well, so far asynctask the entire implementation process is completely finished, I believe that we have learned a lot of things, we suggest that we have free to the source in a comb over, after all, their own summed up the impression is more profound.
Come here today and wish you all a happy life.
For more information please pay attention to the platform, blog updates will be timely notification. Love to learn love technology.
Asynctask of Android Source code parsing