學習內容
- 線程基本概念
- 線程的不同形式
- AsyncTask
- HandlerThread
- IntentService
- 線程池基礎
原文開篇部分
- 主線程主要處理和界面相關的事情,而子線程則往往用于執行耗時操作。
- 線程的多種形態:
- AsyncTask:底層封裝了線程池和 Handler,目的在于方便開發者在子線程中更新 UI。
- HandlerThread:底層直接使用了線程,它是一個具有消息循環的線程,其內部可以使用 Handler
- IntentService:底層直接使用了線程,內部采用 HandlerThread 來執行任務,完成后即退出。它是類似后臺線程的服務,不易被系統殺死從而保證后臺任務的執行。而單純后臺線程的話,如果內部沒有活動的四大組件,優先級低,容易被殺死。
- 線程池:
- 線程的創建和銷毀有相應的開銷,因此采用線程池,線程池中會緩存一定數量的線程,通過線程池可以避免因為頻繁創建和銷毀線程所帶來的系統開銷。
主線程和子線程
基本介紹
- 主線程指進程擁有的線程;子線程也叫工作線程,除了主線程以外的線程都是子線程。
- 主線程的作用是運行四大組件以及處理它們和用戶的交互,而子線程的作用則是執行耗時任務。
- Android 3.0 以后,網絡訪問必須在子線程中進行,否則網絡訪問會失敗,并拋出 NetworkOnMainThreadException 異常,此舉是為了避免主線程由于被耗時任務所阻塞而出現 ANR。
Android 中的線程形態
AsyncTask
1. 基本
- 輕量級
- 適合執行后臺任務以及在主線程中訪問 UI,但不適合執行特別好使的后臺任務。(特別耗時的,建議線程池)
2.參數
-
AsyncTask 是一個抽象的泛型類,有三個參數,具體聲明如下
public abstract class AsyncTask<Params, Progress, Result>
Params:表示參數的類型
Progress:表示后臺任務的執行進度的類型
Result:表示后臺任務的返回結果的類型
3.核心方法
- onPreExecute():在主線程中執行。在異步任務之前調用此方法,做一些準備工作
- doInBackground(Params...params):
- 在線程池中調用,用于執行異步任務,參數表明異步任務的輸入參數。
- 內部可以通過 publishProgress 方法更新任務進度,而 publishProgress 方法中會調用 onProgressUpdate方法
- 另外該方法返回結果給 onPostExecute 方法。
- onProgressUpdate(Progress...progress):在主線程中執行,當后臺任務的進度發生改變時此方法被調用。用于更新界面中的進度。
- onPostExecute(Result result):在主線程中執行,異步任務執行之后,此方法被調用,result 是后臺任務的返回值。用于在任務完成后給出提示。
- 補充
- 執行順序:onPreExecute --> doInBackground --> onPostExecute。
- onCanceled():在 主線程 中執行,異步任務取消時,該方法被調用,此時 onPoseExecute 方法不會被調用。
4.限制
- AsyncTask 的類必須在 主線程 中加載,即第一次訪問 AsyncTask 必須發生在主線程,Android 4.1 及以上版本已自動完成。
- AsyncTask 的對象必須在 主線程 中創建
- execute 方法必須在 UI 線程 調用
- 不要在程序中直接調用 onPreExecute、doInBackground、onProgressUpdate、onPostExecute 方法
- 一個 AsyncTask 對象只能 執行一次,即只能調用一次 execute 方法,否則報異常
- Android 1.6 以前,串行執行任務;Android 1.6 時采用線程池處理并行任務;Andorid 3.0 開始,采用單個線程來串行執行任務,但是可以通過 AsyncTask 的 executeOnExecutor 方法來并行執行任務。
AsyncTask 的工作原理
1.分析
-
從 execute(Params... params) 入手分析,它會調用 **executeOnExecutor **方法:
@MainThread public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); } @MainThread 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; }
在 executeOnExecutor 方法中,首先 onPreExecute 方法被調用,之后線程池開始執行 exec.execute(mFuture),這里實際上調用的是 SerialExecutor.execute(final Runnable r) 方法:
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); } } }
此處可以分析 AsyncTask 的排隊執行的過程。首先系統把 AsyncTask 的 Params 參數封裝成 FutureTask 對象,FutureTask 是一個并發類,它充當 Runnable 的作用。接著這個 FutureTask 會交給 SerialExecutor 的 execute 方法處理。
execute 方法首先會把該 FutureTask 對象插入到任務隊列 mTasks,如果此時沒有正在活動的 AsyncTask 任務,就會執行下一個 AsyncTask 任務;同時當一個 AsyncTask 任務執行完后,AsyncTask 會繼續執行其他任務直到所有任務都執行完為止。(串行執行)
AsyncTask 有兩個線程池(THREAD_POOL_EXECUTOR 和 SerialExecutor )和一個 Handler(InternalHandler)。
- SerialExecutor 用于任務的排隊
- THREAD_POOL_EXECUTOR 用于真正的執行任務
- InternalHandler 用于將執行環境從線程池切換到主線程
在上面的分析中,我們看到 SerialExecutor 中調用了 FutrueTask 的 run 方法,而其 run 方法中會調用 mWorker 的 call 方法,call 方法定義在 AsyncTask 的構造函數中
mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { mTaskInvoked.set(true); Result result = null; try { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked result = doInBackground(mParams); Binder.flushPendingCommands(); } catch (Throwable tr) { mCancelled.set(true); throw tr; } finally { postResult(result); } return result; } };
能看到,call 方法中首先將 mTaskInvoked 設為 true,表明當前任務已被調用,然后執行 AsyncTask 的 doInBackground 方法,接著講返回值傳遞給 postResult 方法,它的實現如下所示:
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result; }
上面的代碼中,postResult 方法會通過 getHandler() 方法得到一個 Handler 對象,然后通過該 Handler 發送一個消息;實際上 getHandler 返回了一個 mHandler。
private Handler getHandler() { return mHandler; }
而AsyncTask 的構造方法中,對 mHandler 做出如下設置:
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper() ? getMainHandler() : new Handler(callbackLooper); private static Handler getMainHandler() { synchronized (AsyncTask.class) { if (sHandler == null) { sHandler = new InternalHandler(Looper.getMainLooper()); } return sHandler; } }
因此實際最后得到的 Handler 對象即為 sHandler,變相要求了 AsyncTask 的類必須在主線程中加載。這個 sHandler 的定義如下:
private static class InternalHandler extends Handler { public InternalHandler(Looper looper) { super(looper); } @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_RESULT 消息后會調用 AsyncTask 的 finish 方法。
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }
如果被取消執行,那么調用 onCancelled 方法,否則調用 onPostExecute 方法,此時 doInBackground 的返回值 result 就傳遞給了 onPostExecute 方法。
到此為止,AsyncTask 的整個工作流程就分析完畢了。
2.流程順序
AsyncTask.execute(Params... params) -> AsyncTask.executeOnExecutor(Executor exec,Params... params) (此方法中調用 onPreExecute)-> SerialExecutor.execute(final Runnable r) -> FutureTask r.run() -> WorkerRunnable<Params,Result> mWorker.call()(此方法中調用 doInBackground) -> AsyncTask.postResult(Result result) -> sHandler 發送消息 -> InternalHandler.handleMessage -> AsyncTask.finish(此方法中調用 onCancelled 或者 onPostExecute )
HandlerThread
介紹
- HandlerThread 繼承了 Thread,是一種可以使用 Handler 的 Thread。
- 實現簡單:在 run 方法中通過 Looper.prepare() 來創建消息隊列,并通過 Looper.loop() 開啟消息循環,這樣就可以創建使用 Handler 了。
- HandlerThread vs. 普通的 Thread:
- 普通 Thread 主要用于在 run 方法中執行一個耗時任務;
- HandlerThread 在內部創建了消息隊列,外界需要通過 Handler 的消息方式來通知 HandlerThread 執行一個具體的任務
- 使用場景之一:IntentService
- 建議:和 Looper 類似,當明確不再需要 HandlerThread 時,通過它的 quit 或者 quitSafely 方法來終止線程的執行。
IntentService
1.介紹
- IntentService 是一種特殊的 Service,繼承了 Service 并且是一個 抽象類。
- 用于執行后臺耗時的任務,任務執行后它會自動停止。同時因為其本質是 Service,導致優先級比普通的線程高,不容易被殺死,所以適合執行一些 高優先級的后臺任務。
2.原理
-
IntentService 封裝了 HandlerThread 和 Handler,從源碼中可以清楚的看到:
public abstract class IntentService extends Service { //... private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { onHandleIntent((Intent)msg.obj); stopSelf(msg.arg1); } } @Override public void onCreate() { // TODO: It would be nice to have an option to hold a partial wakelock // during processing, and to have a static startService(Context, Intent) // method that would launch the service & hand off a wakelock. super.onCreate(); HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); thread.start(); mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); } //... }
在第一次啟動 IntentService 時,onCreate 方法創建一個 HandlerThread,然后利用 Looper 來構造一個 Handler 對象 mServiceHandler,這樣通過 mServiceHandler 發送的消息最后都會在 HandlerThread 中執行。
當每次啟動 IntentService 時,都會調用 onStartConmmand 方法,而其又會調用 onStart 方法,源碼如下:
@Override public int onStartCommand(@Nullable Intent intent, int flags, int startId) { onStart(intent, startId); return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY; } @Override public void onStart(@Nullable Intent intent, int startId) { Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; msg.obj = intent; mServiceHandler.sendMessage(msg); }
可以看出,它只是通過 mServiceHandler 發送了一條消息,這條消息包含了 Intent 對象,這個消息會在 HandlerThread 中處理。當 mServiceHandler 收到消息后,會將 Intent 對象傳遞給 onHandleIntent 方法,而 onHandleIntent 方法是一個抽象方法,通過子類重寫該方法從而實現通過 Intent 對象解析外界啟動 IntentService 時傳入的參數,并針對不同參數區分具體的后臺任務。
@WorkerThread protected abstract void onHandleIntent(@Nullable Intent intent);
當 onHandlerIntent 結束后,就會調用 stopSelf(int startId) 方法停止服務,該方法會等待所有的消息都處理完畢后才會終止服務。(stopSelf()方法會立即終止)
作為補充,IntentService 是順序執行后臺任務的,原因在于 IntentService 內部是通過消息的方式向 HandlerThread 請求執行任務,而 Handler 的 Looper 是順序處理的,因此 IntentService 也是順序執行的。
Android 中的線程池
線程池的優點:
- 重用線程池中的線程,避免因為線程的創建和銷毀所帶來的性能開銷
- 有效控制線程池的最大并發數,避免大量的線程之間因互相搶占系統自ui按導致的阻塞現象
- 能夠對線程進行簡單的管理,并提供定時指定以及指定間隔循環執行等功能。
1.ThreadPoolExecutor
1.基本介紹
該類是線程池的真正實現,Android 的線程池都是直接或者間接通過配置 ThreadPoolExecutor 來實現的。
-
一個比較常用的構造方法:
public ThreadPoolExector(int corePoolSize, int maximunPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)
參數說明:
- corePoolSize:線程池的核心線程數,默認情況下,核心線程會一直存活。如果設置 allowCoreThreadTimeOut 為 true,且等待時間超過 keepAliveTime 所制定的時長后,核心線程會被終止。
- maximunPoolSize:線程池能容納的最大線程數,當活動的線程達到這個數值之后,后續任務會被阻塞。
- keepAliveTime:非核心線程閑置時的超時時長,超過這個時長,非核心線程就會被回收。當設置 allowCoreThreadTimeOut = true 時,同樣會作用于核心線程。
- unit:指定 keepAliveTime 參數的時間單位,這是一個枚舉。
- workQueue:線程池中的任務隊列,通過線程池的 execute 方法提交的 Runnable 對象會存儲在這個參數中。
- threadFactory:線程工廠,為線程池提供創建新線程的功能功能。它是個接口,只有一個方法:
Thread newThread(Runnable r)
不常用的參數
- RejectedExecutionHandler handler:當線程池無法執行新任務時,可能是由于任務隊列已滿或者時無法成功執行任務,此時 ThreadPoolExecutor 會調用 handler 的 rejectdExecution 方法來通知調用者。
2.執行任務的規則
- 線程池中的線程數量未達到核心線程的數量:直接啟動一個核心線程來執行任務。
- 如果線程池中的線程數量已經大導或者超過核心線程的數量,那么任務會被插入到任務隊列等待執行
- 如果 2 中無法將任務插入到任務隊列中,這往往是由于任務隊列已滿,此時如果線程數量未達到線程池規定的最大值,那么會立刻啟動一個非核心線程來執行任務。
- 如果 3 中線程數量已經達到線程池規定的最大值,那么據拒絕執行此任務,此時上述不常用的參數 handler 發揮作用。
3.AsyncTask 線程池的配置
(針對 THREAD_POOL_EXECUTOR 線程池)
- 核心線程數等于 CPU 核心數 + 1
- 線程池的最大線程數為 CPU 核心數的 2 倍 + 1
- 核心線程無超時機制,非核心線程在閑置時的超時時間為 1 秒
- 任務隊列的容量為 128
2.線程池的分類
常見的 4 個線程池
- FixedThreadPool
- 線程池固定的線程池,當線程處于空閑狀態時,它們并不會被回收,除非線程池被關閉。當所有的線程都處于活動狀態時,新任務都會處于等待狀態,直到有線程空閑出來。
- 只有核心線程并且不會被回收,能夠更加快速的響應外界的請求。
- CachedThreadPool
- 線程數量不定的線程池,只有非核心線程,最大線程數為 Integer.MAX_VALUE。
- 當線程池中的線程都處于活動狀態時,線程池會創建新的線程來處理新任務,否則利用空閑的線程來處理新任務。線程池中的空閑線程具有超時機制,為 60s。
- 任務隊列相當于一個空集合,導致任何任務都會立即被執行,適合執行大量耗時較少的任務。當整個線程池都處于限制狀態時,線程池中的縣城都會超時而被停止。
- ScheduledThreadPool
- 核心線程數量固定,非核心線程數沒有限制,并且非核心線程閑置的時候立即回收。
- 主要用于執行定時任務和具有固定周期的重復任務
- SingleThreadExecutor
- 只有一個核心線程,保證所有的任務都在一個線程中順序執行。
- 意義在于不需要處理線程同步的問題。