摘要:的用法的聲明如下是一個抽象類,同時也是一個泛型類。多線程問題通過以上分析,涉及到兩個線程池。是串行線程池,使用隊列管理任務對象。是真正執行任務的線程池,若要實現并行執行任務,則可自定義線程池,傳入方法作為線程池對象。
AsyncTask 分析
AsyncTask簡介位置:frameworksasecorejavaandroidosAsyncTask.java
在Android中,所有的UI更新操作必須要在主線程中進行,而耗時操作(如訪問網絡、加載圖片、IO操作等)需要在子線程中進行,線程間的交互需要借助Android的異步消息處理機制,也就是Handler。但是為了更方便地讓子線程與主線程交互,更新UI元素,Android系統提供了輕量級的異步任務類AsyncTask。
AsyncTask的用法AsyncTask的聲明如下:
public abstract class AsyncTask
AsyncTask是一個抽象類,同時也是一個泛型類。在使用時需要創建子類去繼承它,并指定相應的泛型類型參數。三個參數的含義
Params
執行AsyncTask時需要傳入的參數,用于執行后臺任務。
Progress
顯示任務進度單位。
Result
任務執行完畢后返回的結果
一個簡單的AsyncTask使用示例:
class DownloadTask extends AsyncTask{ @Override protected void onPreExecute() { progressDialog.show(); } @Override protected Boolean doInBackground(Void... params) { try { while (true) { int downloadPercent = doDownload(); publishProgress(downloadPercent); if (downloadPercent >= 100) { break; } } } catch (Exception e) { return false; } return true; } @Override protected void onProgressUpdate(Integer... values) { progressDialog.setMessage("當前下載進度:" + values[0] + "%"); } @Override protected void onPostExecute(Boolean result) { progressDialog.dismiss(); if (result) { Toast.makeText(context, "下載成功", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(context, "下載失敗", Toast.LENGTH_SHORT).show(); } } }
onPreExecute
此方法會在后臺任務執行前被調用,用于進行一些準備工作,在主線程執行。
doInBackground
此方法為要執行的后臺任務,在子線程中執行。不能進行UI操作。
onProgressUpdate
顯示任務執行進度,在doInBackground中調用publishProgress(Progress... values),values會傳入此方法中,進行進度顯示。在主線程執行。
onPostExecute
當后臺任務執行完畢后,通過return語句返回執行結果,執行結果傳入此方法作為參數
任務的啟動:
new DownloadTask().execute();AsyncTask源碼分析
基于Android 6.0源碼
首先看AsyncTask的構造函數:
public AsyncTask() { mWorker = new WorkerRunnable() { 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 (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); } } }; }
構造函數里初始化了兩個對象:mWorker和mFuture,mWorker是WorkerRunnable抽象類,實現了Callable接口。在mWorker的call()方法中,mTaskInvoked.set(true)表示當前任務已被調用過,然后設置當前線程的優先級。接著調用AsyncTask的doInBackground(mParams)方法執行后臺任務,并將執行結果存入result傳入postResult方法。因此,要執行的后臺任務包含在了AsyncTask的mWorker的call()方法中。
然后是mFuture,mFuture是一個FutureTask類對象,在構造方法中傳入mWorker作為參數。mFuture其實是一個封裝了mWorker的FutureTask對象,FutureTask類實現了FutureRunnable接口,通過這個接口可以方便的取消后臺任務以及獲取后臺任務的執行結果,具體可參考:Java并發編程:Callable、Future和FutureTask。
由上分析可知:后臺任務在mWorker的call()方法中,當call()被執行時后臺任務doInBackground也就會被執行了。那么何時才會被執行?因為mWorker又封裝在了mFuture中,因此當mFuture對象被提交到AsyncTask包含的線程池執行時,call方法就會被調用,自定義的后臺任務也就開始執行了。
要啟動某一個任務,就需要調用該任務的execute()方法,下面看execute()方法的源碼:
public final AsyncTaskexecute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }
在執行execute時傳入params參數,這個參數會一層一層傳遞到doInBackground方法中,方法中調用了executeOnExecutor并將其返回值返回,傳入了兩個參數,其中sDefaultExecutor是AsyncTask默認的線程池。
下面看executeOnExecutor方法:
public final AsyncTaskexecuteOnExecutor(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; }
在executeOnExecutor方法中,首先判斷任務的狀態,當任務正在運行或已經結束時會拋出異常。
然后調用了AsyncTask的 onPreExecute() 方法,因此前面自定義的AsyncTask類中,onPreExecute()方法會第一個被執行。
然后將傳入的params賦值給mWorker的mParams變量
然后執行exec的execute()方法,傳入mFuture作為參數,exec就是前面傳進來的sDefaultExecutor
接下來看看sDefaultExecutor
public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); …… private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
sDefaultExecutor是一個靜態的SerialExecutor常量,SerialExecutor類定義如下:
private static class SerialExecutor implements Executor { final ArrayDequemTasks = new ArrayDeque (); 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); } } }
SerialExecutor類是一個串行線程池,其中定義了一個mTasks任務緩存隊列。
在execute方法中,mTasks.offer向緩存隊列添加了一個Runnable任務,任務中run方法執行了r.run(),其實就是調用前面傳入的mFuture對象的run方法,而mFuture的run方法內部會調用mWorker的call方法,然后就會調用doInBackground方法,因此后臺任務就開始執行了。
提交到任務緩存隊列中的任務什么時候會被執行呢?
SerialExecutor中定義了一個Runnable對象mActive,表示當前正在執行的任務,當第一次執行execute時,mActive肯定是null,因此進入if語句執行scheduleNext(),取出第一個任務賦給mActive并判斷是否為null,然后調用THREAD_POOL_EXECUTOR.execute()方法傳入mActive參數,執行取出的mActive引用的任務對象。此時后臺任務真正開始。
當有新的任務被執行時,同樣會offer()方法將傳入的Runnable添加到隊列的尾部,在后續操作判斷mActive就是非null,則不會進入if語句中。
但是,第一個任務被執行時,在Runnable的run方法,中finally中調用了scheduleNext()方法,因此每當一個任務執行完畢后,下一個任務才會得到執行,SerialExecutor模仿的是單一線程池的效果,如果啟動了很多任務,同一時刻只會有一個線程正在執行,其余的均保存在任務隊列中,處于等待狀態。
由上可知,真正執行任務的是THREAD_POOL_EXECUTOR
public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
THREAD_POOL_EXECUTOR是一個線程池,它的配置如下:
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 BlockingQueuesPoolWorkQueue = new LinkedBlockingQueue (128);
核心線程數corePoolSize為CPU數加一;
最大線程數maximumPoolSize為CPU數的二倍加一;
存活時間為1秒;
任務緩存隊列為LinkedBlockingQueue,最多緩存128個任務。
前面說了,當子線程執行任務時,其實是執行mFuture對象的run方法,也就是執行mWorker對象的call()方法
下面再看一下mWorker對象的call()方法:
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); }
doInBackground的執行結果保存在result,作為參數傳入postResult()方法
postResult():
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult(this, result)); message.sendToTarget(); return result; }
這里通過AsyncTask類內部的Handler對象發出一條MESSAGE_POST_RESULT消息,消息內保存了AsyncTaskResult對象,該對象封裝了任務的執行結果。
下面看一下這個Handler的相關源碼:
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; } } }
在主線程創建一個sHandler對象
收到MESSAGE_POST_PROGRESS消息后,會調用onProgressUpdate方法,MESSAGE_POST_PROGRESS消息通過publishProgress方法發送,我們常會在doInBackground方法中調用這個方法,用于回傳任務執行進度。
protected final void publishProgress(Progress... values) { if (!isCancelled()) { getHandler().obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult
當sHandler收到MESSAGE_POST_RESULT消息后,會調用finish方法
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }
在finish方法中,首先判斷任務的狀態,然后執行onPostExecute方法,最后設置任務狀態為FINISHED,當前的AsyncTask任務全部執行完畢。
多線程問題通過以上分析,AsyncTask涉及到兩個線程池:SerialExecutor、ThreadPoolExecutor。
SerialExecutor是串行線程池,使用ArrayDeque隊列管理任務(Runnable對象)。它同時只能執行一個任務,多個任務要等待上一個任務執行完畢才能繼續執行下一個。它在AsyncTask中的作用是保證任務按順序地一個接一個串行執行。
ThreadPoolExecutor是真正執行任務的線程池,若要實現并行執行任務,則可自定義線程池,傳入executeOnExecutor(Executor exec, Params... params)方法作為線程池對象。如:
Executor exec = new ThreadPoolExecutor(15, 200, 10, TimeUnit.SECONDS, new LinkedBlockingQueue()); new DownloadTask().executeOnExecutor(exec,params);
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/67445.html
摘要:異步任務的構造方法主要用于初始化線程池先關的成員變量創建一個新的異步任務。所以,我們是必須確保在銷毀活動之前取消任務。 目錄介紹 01.先看下AsyncTask用法 02.AsyncTask源碼深入分析 2.1 構造方法源碼分析 2.2 看execute(Params... params)方法 2.3 mWorker和mFuture的創建過程 03.異步機制的實現 04.不同...
閱讀 2645·2023-04-26 02:17
閱讀 1619·2021-11-24 09:39
閱讀 1079·2021-11-18 13:13
閱讀 2649·2021-09-02 15:11
閱讀 2781·2019-08-30 15:48
閱讀 3412·2019-08-30 14:00
閱讀 2443·2019-08-29 13:43
閱讀 666·2019-08-29 13:07