AsyncTask是安卓爲簡化子線程訪問ui操做提供的一個線程類工具類,可是AsncTask在Android系統的不斷升級中,通過了屢次修改,致使不一樣的API版本上使用AsyncTask具備不一樣的表現,接下來主要分析AsyncTask的使用與注意事項。android
AsyncTask是一個輕量級的一部任務類,它能夠在子線程執行任務,而後把執行結果分發到主線程進行處理,原理上來說,AsyncTask是對線程池和Handler的封裝,並且AsyncTask**並不適合執行特別耗時的後臺任務**,不然頗有可能形成內存泄漏。併發
AsyncTask的定義以下:異步
AsyncTask定義了三個泛型參數,若是不須要傳遞具體的參數可使用Void代替,這三個泛型參數與AsyncTask定義的幾個核心方法有關:async
//主線程執行,異步任務執行前被調用 protected void onPreExecute() { } //工做線程執行,用於執行任務,方法參數由泛型參考Params肯定,返回參數有Result參數規定 protected abstract Result doInBackground(Params... params); //主線程執行,用於處理doInBackground的返回值 oprotected void onPostExecute(Result result) { } //須要在doInBackground方法中調用,用於更新任務的執行進度,方法參數有Progress泛型參數規定 protected final void publishProgress(Progress... values) { //在主線程被調用,當在doInBackground調用publishProgress分發任務進度是,此方法就會被調用 protected void onProgressUpdate(Progress... values) { }
其次,AsyncTask還提供了onCancel方法,它一樣在主線程執行,當調用AsyncTask的cancel方法時,任務被取消,onCancel被調用,而onPostExecute不會被調用ide
AsyncTask的類必須在主線程被加載,這意味着第一次訪問AsycnTask必須在主線程,固然這個過程在Android4.1以上版本系統已經自動完成,在android5.0的源碼中ActivityThread的main方法中就掉用了AsyncTask.init()方法(具體爲何必須在主線程被加載,稍後分析)工具
AsyncTask對象必須在住下次被建立oop
execute方法必須在主線程被調用post
不要主動調用onPreExecute等方法ui
一個AsyncTask只能被執行一次,不然會有異常this
AsyncTask在不一樣系統版本的表現
瞭解AsyncTask的工做原理,先從它的execute方法開始分析:
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; }
execute調用了executeOnExecutor方法,executeOnExecutor方法會把mFuture交給sDefaultExecutor去執行,看到這裏,咱們須要先了解一下executeOnExecutor中的幾個變量:
- mStatus: 用於記錄一個AsyncTask的運行狀態
- mWorker:它的類型是WorkRunnbale在AsyncTask構造方法被建立,WorkRunnbale實現了Callable接口,mWorker做爲WorkRunnbale的子類,實現了來自Callable的call方法,並在call方法中調用了doInBackground方法。
- mFuture:它的實際類型是FutureTask,是一個併發相關類,它的父類實現了Runnable和Future接口,它自己接收一個Callable類型對象,當它被提交給一個任務執行器是,它的run(來自Runnable)方法被調用,而在run中會調用Callable的call方法。
具體咱們能夠看AsyncTask的構造方法:
public AsyncTask() { 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); } }; 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); } } }; }
能夠看到mFuture接收的Callable對象正是mWorker。
接着分析executeOnExecutor方法,任務執行的參數params賦值給了mWorker.mParams,而後把mFuture叫給了sDefaultExecutor執行,下面來分析一些這個sDefaultExecutor:
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); } } }
sDefaultExecutor就是一個SerialExecutor,它內部維護着一個mTasks隊列,用於存儲進程內AsyncTask提交的任務,看SerialExecutor的execute方法,接收的mFuture並非直接運行,而是封裝成爲一個Runnable插入到隊列中去,而後判斷mActive是否爲null,若是mActive=null,則會調用scheduleNext取出一個任務交給THREAD_POOL_EXECUTOR去真的執行任務,只有當一個任務執行完畢,纔會再一次調用scheduleNext方法,由此分析AsyncTask確實是串行執行任務的。
可見AsyncTask有兩個線程池:
- SerialExecutor 用於對任務進行串行調度,而不是真的執行任務
- THREAD_POOL_EXECUTOR真正的任務執行器,
上面分析到當mFutrue被提交到線程池中,他的run方法被調用,run方法調用Callable發call,這裏對應的就是mWorker的call方法:
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); } };
在call方法中,調用了AsyncTask的doInBackground方法,而後調用postResult(result)方法分發執行結果:
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result; }
這裏調用getHandler()獲取的Handler發送了消息,消息中封裝了任務的執行結果。先來看一下
getHandler(),
private static Handler getHandler() { synchronized (AsyncTask.class) { if (sHandler == null) { sHandler = new InternalHandler(); } return sHandler; } } 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: // There is only one result result.mTask.finish(result.mData[0]); break; case MESSAGE_POST_PROGRESS: result.mTask.onProgressUpdate(result.mData); break; } } }
它是Handler的子類,在調用getHandler()方法若是沒有初始化,則它會被初始化,這裏也說明了爲何AsyncTask必須在主線程調用的緣由,
各個說到postResult方法把執行結果封裝成AsyncTaskResult對象,而後發送了一個MESSAGE_POST_RESULT類型的消息,很顯然這個消息會在InternalHandler中被處理,處理代碼以下:
result.mTask.finish(result.mData[0]);
AsyncTask的finish被調用:
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED;
方法很簡單,若是任務被取消,則調用onCancelled方法,不然調用onPostExecute方法,最後把任務的狀態置爲FINISHED。
public final boolean cancel(boolean mayInterruptIfRunning) { mCancelled.set(true); return mFuture.cancel(mayInterruptIfRunning); }
mCancelled一個保證原子操做的併發類,mCancelled被設置爲true,而AsyncTask中使用到它的就是isCancelled方法,而isCancelled方法被調用的地方有
finish方法 publishProgress方法
可見mCancelled的做用只是任務取消的時候onPostExecute和onProgressUpdate被調用,因此關在在於mFuture.cancel(mayInterruptIfRunning)方法,其實它的內部也只是調用了interrupt方法,而咱們知道這個interrupt用來中止線程並不可靠,也就是說AsyncTask的cancel方法內部並不能真正的讓任務中止執行。
通常AsyncTask做用一個內部類被建立並調用,而內部類持有外部類的引用,讓AsyncTask去執行一個長時間任務時,這是即便退出當前界面,調用他的cancel方法,並不見得就能夠當即中止
AsyncTask所執行的任務,只要任務在執行AsyncTask對象就不會被回收,而AsyncTask所持有的外部類應用也確定不會被回收,因此頗有可能就形成了內存泄漏,在Android中,AsyncTask也只被推薦用於短耗時的異步任務。
爲了解決系統版本的差別性,在Support V4包中提供了AsyncTaskCompat類,實現以下:
public class AsyncTaskCompat { public static <Params, Progress, Result> AsyncTask<Params, Progress, Result> executeParallel( AsyncTask<Params, Progress, Result> task, Params... params) { if (task == null) { throw new IllegalArgumentException("task can not be null"); } if (Build.VERSION.SDK_INT >= 11) { // From API 11 onwards, we need to manually select the THREAD_POOL_EXECUTOR AsyncTaskCompatHoneycomb.executeParallel(task, params); } else { // Before API 11, all tasks were run in parallel task.execute(params); } return task; } } class AsyncTaskCompatHoneycomb { static <Params, Progress, Result> void executeParallel( AsyncTask<Params, Progress, Result> task, Params... params) { task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params); } }
而THREAD_POOL_EXECUTOR的實現以下,它就是一個ThreadPoolExecutor。
public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); 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 BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128);
ThreadPoolExecutor所能承載任務數是有限的,它能承載的任務數爲sPoolWorkQueue的size+MAXIMUM_POOL_SIZE,若是踢提交的任務數超過了這個數,默認ThreadPoolExecutor會拋出異常,因此在使用的時候必定要注意。
對於任務超載,ThreadPoolExecutor提供了一個方法用時實現超載時候的任務處理策略
executor.setRejectedExecutionHandler();
參照於文章:https://blog.csdn.net/zty19901005/article/details/50727106