Android AsyncTask source code parsing, androidasynctask
Reprinted please indicate the source: bytes]
1. Overview
I believe everyone is familiar with AsyncTask. It is a powerful tool to execute time-consuming tasks and then update the UI. Of course, it is also a method to replace Thread + Handler. If you do not know about the Handler mechanism, please refer to: Android asynchronous Message processing mechanism allows you to have a deep understanding of the relationship among logoff, Handler, and Message.
2. Simple Example
I believe everyone has written such 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"; private ProgressDialog mDialog; private TextView 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 (100); mDialog. setProgressStyle (ProgressDialog. STYLE_HORIZONTAL); mDialog. setCancelable (false); new myasynctask(cmd.exe cute ();} private class MyAsyncTask extends AsyncTask <Void, Integer, Void >{@ Overrideprotected void onPreExecute () {mdidialog. show (); Log. e (TAG, Thread. currentThread (). getName () + "onPreExecute") ;}@ Overrideprotected Void doInBackground (Void... params) {// simulate data loading. time-consuming task for (int I = 0; I <100; I ++) {try {Thread. sleep (80);} 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 mDialog after data loading is complete. dismiss (); mTextView. setText ("load data success"); Log. e (TAG, Thread. currentThread (). getName () + "onPostExecute ");}}}
Enter an Activity. The data required by the Activity comes from the network or other time-consuming operations. You can complete the preparation operations in onPreExecute in AsyncTask, such as displaying the progress dialog box in the previous example, and then perform time-consuming operations in doInBackground, during time-consuming operations, you can pass parameters to onProgressUpdate through publishProgress from time to time, and then perform UI operations in onProgressUpdate, such as updating the progress of the progress bar in the previous example. After the time-consuming task is completed, at last, set the control data update UI in onPostExecute, for example, hide the progress dialog box.
:
3, source code analysis Note: This article source code analysis based on Andorid-17, because and before 3.0 version changes large, it is necessary to mark.
So everyone must be curious about how AsyncTask is implemented in Android. The following is a source code analysis: Starting from the starting point of executing an asynchronous task, we will go to the execute method:
public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params);}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; }Row 18: set the current AsyncTask status to RUNNING. The switch above also shows that each asynchronous task can be executed only once before completion.
Row 20: The onPreExecute () is executed, and the UI thread is still running, so we can do some preparation work in it.
Row 22: The input parameter is assigned to mWorker. mParams.
Row 23: exec.exe cute (mFuture)
I believe you will be confused about the mWorker in line 22 and the mFuture in line 23.
MWorker finds this class:
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { Params[] mParams;}
We can see that it is a subclass of Callable and contains a mParams used to save the input parameters. The code for initializing mWorker is as follows:
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)); } };//…. }
We can see that mWorker has completed initialization in the constructor. Because it is an abstract class, an implementation class is added here, and the call method is implemented. mTaskInvoked = true is set in the call method, finally, the doInBackground (mParams) method is called, and the Result value is returned as a parameter to the postResult method. we can see that our doInBackground has appeared, and we will continue to look at it below:
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result;}
We can see that the familiar asynchronous message mechanism is displayed in postResult, and a message is passed. message. what is 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 carrying parameters.
Seeing this, I believe everyone will think that there must be a sHandler somewhere, And the handleMessage method is rewritten to wait for the message to be passed in and the message processing.
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 is only one result result.mTask.finish(result.mData[0]); break; case MESSAGE_POST_PROGRESS: result.mTask.onProgressUpdate(result.mData); break; } }}
Haha, our handleMessage is displayed. We can see that the result is executed when the MESSAGE_POST_RESULT message is received. mTask. finish (result. mData [0]); it is actually our AsyncTask. this. finish (result), so check the finish method.
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }
We can see that if cancel () is called, The onCancelled callback will be executed. If normal execution is performed, the onPostExecute (result) will be called. The call here is in the handleMessage of handler, so it is in the UI thread. If you do not understand the asynchronous Message mechanism, see: Android asynchronous Message processing mechanism allows you to have a deep understanding of the relationship among logoff, Handler, and Message.
Finally, set the status to FINISHED.
After mWoker is finished, it should be our mFuture. The mFuture Initialization is still completed in the constructor. mWorker is used as a parameter and its done method is rewritten.
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); } } };}Row 16: postResultIfNotInvoked (get () is called at the end of the task execution. get () indicates obtaining the return value of the call of the mWorker, that is, Result. Then, check 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. However, mTaskInvoked is set to true during mWorker initialization. Therefore, this postResult cannot be executed.
Now, mWorker and mFurture appear in the execute method, but the code for initializing these two objects has not been actually executed. Next, let's look at the real call execution.
In the execute method:
Remember the 23 rows in the above execute: exec.exe cute (mFuture)
Exec is the sDefaultExecutor in executeOnExecutor (sDefaultExecutor, params)
The following describes the sDefaultExecutor.
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;public static final Executor SERIAL_EXECUTOR = new SerialExecutor();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); } }}We can see that sDefaultExecutor is actually an instance of SerialExecutor, which maintains a task queue internally; directly view its execute (Runnable runnable) method and put runnable at the end of mTasks;
Line 16-17: determines whether the current mActive is null. If it is null, The scheduleNext method is called.
Row 20: scheduleNext. The first task of the queue is taken out. If it is not null, THREAD_POOL_EXECUTOR is input for execution.
Let's see where 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);
We can see that it is a thread pool with self-set parameters. The parameters are:
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 = new AtomicInteger(1);public Thread newThread(Runnable r) { return new Thread(r, "AsyncTask #" + mCount.getAndIncrement()); } };private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(10);
Here, we may think that there is a thread pool behind it, and a maximum of 128 concurrent threads are supported, and a blocking queue with a length of 10 is added, it may be thought that the execute method of AsyncTask sub-classes within 138 can be quickly called without any problem, and an exception will be thrown if it is greater than 138.
Export cute:
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 that call the execute (s synchronized) method at the same time, the first task is queued and then in mActive = mTasks. poll ())! = Null is taken out and assigned to mActivte for execution by the thread pool. Then, the second task enters the queue, but mActive is not null and scheduleNext () is not executed. Therefore, if the first task is slow, all 10 tasks will enter the queue for waiting; when the next task is actually executed, after the thread pool completes the first task, it calls scheduleNext in the finally code block in Runnable. Therefore, although there is a thread pool, in fact, the call process is linear. One execution after another is equivalent to a single thread.
4. Summary
After the source code is explained, the Code span is large. Let's review it again:
public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params);}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; }
Row 18: set the current AsyncTask status to RUNNING. The switch above also shows that each asynchronous task can be executed only once before completion.
Row 20: The onPreExecute () is executed, and the UI thread is still running, so we can do some preparation work in it.
Row 22: The input parameter is assigned to mWorker. mParams: mWorker is a subclass of Callable. In the Internal call () method, doInBackground (mParams) is called, and the returned value is executed as the parameter of postResult; postResult sends messages through sHandler, and finally calls onPostExecute in the handleMessage of sHandler.
Row 23: exec.exe cute (mFuture). mFuture is the unit of the real execution task. mWorker is encapsulated and then handed over to the thread pool for execution by sDefaultExecutor.
5. publishProgress
Having said so much, we seem to have forgotten a method: 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. Send a message directly using sHandler and carry the input value;
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 is only one result result.mTask.finish(result.mData[0]); break; case MESSAGE_POST_PROGRESS: result.mTask.onProgressUpdate(result.mData); break; } }}
In handleMessage, we call onProgressUpdate (result. mData.
6. AsyncTask was defective
I remember a previous interview question that often asked: What is the operating principle of AsyncTask? What are the defects?
In the past, the possible answer to the defect was that AsyncTask encountered an exception when executing multiple concurrent tasks. In fact, it still exists. In systems earlier than 3.0, it will still be executed in a way that supports multi-thread concurrency. The number of concurrent threads is also 128 calculated above. Blocking queues can store 10; that is to say, it is no problem to execute 138 tasks at the same time, and more than 138 will immediately display java. util. concurrent. rejectedExecutionException;
In more than 3.0 of systems, including 3.0, It will be executed for a single thread (that is, the analysis of the above Code );
Let's look at 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";private ProgressDialog mDialog;private TextView 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(100);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;}}}We can see that 138 asynchronous tasks are executed in my for loop, and the execution of each asynchronous task takes 10 s. The following uses a simulator of 2.2 for testing:
Output result:
AsyncTask #1-AsyncTask #128 simultaneous output
Then 10 seconds later, the other 10 tasks are output.
The analysis result shows that AsyncTask supports 2.2 concurrent tasks and at least 10 waiting tasks in 128 of systems;
Change the number of 138 tasks to 139:
for(int i = 1 ;i <= 139 ; i++){new MyAsyncTask2().execute();}Running result: An exception occurs: java. util. concurrent. RejectedExecutionException. Therefore, you can determine that only 10 tasks can be waited. If more than 10 tasks are supported, an exception occurs immediately.
To put it simply, the cause of the exception is as follows: Now there are 139 tasks, almost simultaneously submitted. The thread pool supports 128 concurrent tasks, and the number of blocked queues is 10, at this time, an exception occurs when 11th tasks are submitted.
Let's take a 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);
Check the execute method of ThreadPoolExecutor:
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);
When the blocking queue is full, workQueue. offer (command) returns false; then the addWorker (command, false) method is executed. If false is returned, the reject () method is executed.
private boolean addWorker(Runnable firstTask, boolean core) {…int wc = workerCountOf(c); if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize)) return false;…}
If the number of tasks is greater than the capacity, false is returned, and an exception is thrown in reject.
The above is the result of testing with the 2.2 simulator;
Change the system to 4.1.1, that is, my testing machine Xiaomi 2 s.
Change the number of threads to 139 or even 1000. You can see that the slow execution of the task one by one will not throw any exceptions, but the thread will execute one by one;
Now, if you go to the interview and are asked about the defects of AsyncTask, we can divide them into two parts: Before 3.0, we can support a maximum of 128 concurrent threads and wait for 10 tasks. After 3.0, no matter how many tasks there are, they will be executed in a single thread;
Now, the source code analysis of AsyncTask is complete. I believe you have a deeper understanding of AsyncTask ~~~
Test code click to download
The Android AsyncTask dialog box appears in the onPreExecute method.
Check whether your AsyncTask calls the get () method. If you call the get () method and delete it, you can.
Who can parse the source code of the android home sample program?
Take a look at the program !!~ :)
Reference: nothing