參考文章:要點提煉|開發藝術之線程。
概述
線程是CPU調度的最小單元。
線程是一種受限的資源,不能多次的創建與停止。
分類:
主線程:一般一個線程只有一個主線程。主線程中一般用於UI操作,用於與用戶交互。
子線程:子線程中一般執行耗時操作,比如網絡操作和IO操作。
線程形態:
AysncTask:一種異步執行類,便於執行後臺任務以及在主線程更新UI。其中封裝了兩個線程池,一個Handler,其中一個線程池用於任務的排序,另一個線程池用於處理任務。Handler用於切換到主線程。
HandlerThread:可以持續的循環從消息隊列中取消息的線程。(其實內部調用了Looper.prepare和Looper.loop方法)。
IntentService:是一個異步並會自動停止的服務,好處就是優先級比較高,不容易被回收。內部封裝了一個HandlerThread和一個Handler,Handler與HandlerThread中的Looper綁定。
線程形態
AsyncTask
概述與使用
在Android中實現異步任務機制有兩種方式:Handler和AsyncTask。
Handler機制存在的問題:代碼相對臃腫;多任務同時執行時不易精確控制線程。
引入AsyncTask的好處:創建異步任務更簡單,直接繼承它可方便實現後臺異步任務的執行和進度的回調更新UI,而無需編寫任務線程和Handler實例就能完成相同的任務。
AsyncTask是一個泛型類,其中包括三個泛型參數 public abstract class AsyncTask<Void, Integer, Boolean>
- params:傳入的數據。(一般是傳入URL,類型對應是String)
- progress:任務進度(類型一般是Integer)
- result:結果。
包含五個重要方法
- onPreExecute:運行在主線程中,在後臺任務執行前會調用,進行一些初始化操作。
- doInBackground(…params):運行在子線程中,後臺任務具體在這裏會執行。如果在其中想要知道任務執行的進度需要調用publishProgress(progress)方法,該方法會回調onProgressUpdate方法。
- onProgressUpdate(progress):運行在主線程,可以根據progress的值來更新UI。
- onPostExcute(result):運行在主線程,當任務運行結束後會調用,可以根據result值來更新UI。
- onCanceled:運行在主線程,當任務被取消時會調用。
開始和結束異步任務的方法:
- 開始異步任務:
AysncTask.execute(Params...params)
,必須在主線程中使用,同時注意一個任務只能調用一次execute方法否則會報錯。 - 結束異步任務:
AysncTask.cancel
,必須在主線程中使用,表示停止一個服務。
使用注意:
(1)不要直接調用AysncTask中的方法比如直接調用onProgressUpdate方法來獲取當前進度,而是應該使用其中的progress來更新UI。
(2)AysncTask的實例必須在主線程中創建。(具體原因在於其中有一個Handler,是用於轉換到主線程的,它的一系列handle方法要在主線程使用,因此Handler必須在主線程中創建,進而AysncTask也必須在主線程創建)。
接下來是一個自定義AysncTask的實例
class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
@Override//初始化一個ProgressDialog
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();
}
}
}
開啓和停止的代碼如下:
// 開始任務
DownloadTask mDownloadTask = new DownloadTask();
mDownloadTask .execute();
// 停止任務
mDownloadTask .cancel(true);
原理
AysncTask包括兩個線程池:SerialExecutor與THREAD_POOL_EXECUTOR;他們的作用分別是任務的排隊與任務的實際執行。
AysncTask包含一個Handler,該Handler的作用是將執行環境由線程池切換至主線程,並通過它來發送任務的執行情況的消息。
任務排隊的原理是:
(1)params被封裝爲FutureTask;
(2)調用SerialExecutor.execute()
將FutureTask插入到任務隊列tasks
(3)判斷當前是否沒有任務在執行,沒有的話會調用scheduleNext
方法去執行下一個任務,同時當任務執行完畢後,也會調用scheduleNext
方法去執行下一個任務。可以看出AysncTask是以串行的方式執行任務。
任務執行的原理:
(1)FutureTask的run函數會調用mWorker的call函數;
(2)在call函數中調用doInbackground方法,接着將doInbackground的返回值傳入postResult方法;
(3)postResult方法中會調用sendMessage方法。
(4)在handleMessage方法中根據發送的msg可以判斷當前的情況,進而去調用onPostExcute或onCancel或onProgressUpdate方法
HandlerThread
該類是一個線程類,與普通線程的區別在於,內部對Looper進行了創建,因此可以持續的接收和查找消息。內部調用了Looper.prepare;Looper.loop
方法。
實現方法:
1.創建一個HandlerThread實例,並傳入該線程的名字。
2.通過 HandlerThread.start()開啓線程;
3.創建Handler,並在構造函數中傳入HandlerThread的Looper對象,完成與Handler的綁定。
4.當不需要HandlerThread時,通過HandlerThread.quit()/quitSafely()方法來終止線程的執行
IntentService
IntentService是一個服務,可以在其中執行耗時的任務,IntentService內部包含了一個HandlerThread和一個Handler。
優點:由於是服務,優先級比較高,不會輕易被殺死,所以適合去做一些優先級高的耗時任務;
和普通線程相比,可自動創建子線程來執行任務,且任務執行完畢後自動退出。
工作原理
1.在服務onCreate中會首先創建一個HandlerThread的對象並開啓線程,之後通過傳入Looper對象的方式創建Handler,將Handler與HandlerThread綁定。
2.當調用onStartCommand方法後,會調用Handler的sendMessage(將intent對象放入message的obj字段中),將消息發送到MessageQueue中。Looper持續調用MessageQueue的next函數將msg取出,並找到msg對應的Handler。
3.handleMessage中包含兩個函數:onHandleIntent(intent),以及stopself(startId)。
onHandleIntent方是具體處理Intent的異步請求,stopself是用於停止服務的。
而無參數stopSelf方法時立刻停止服務,有參數stopSelf則不同,該方法會在嘗試停止服務之前會判斷啓動服務的次數與startId,是否相等。相等纔會停止服務,這樣做的好處在於可以當所有消息對處理之後才停止服務。
具體流程如下:
IntentService是一個抽象類,需要我們自己實現其中的onHandleIntent抽象方法,下面是一個實例。
在使用上,我們需要對Intent實例,然後進行intent.putExtra("task_action","具體任務")
操作,最後在調用startService(intent)
。
public class LocalIntentService extends IntentService {
private static final String TAG = "LocalIntentService";
public LocalIntentService() {
super(TAG);
}
@Override
protected void onHandleIntent( Intent intent) {
String action = intent.getStringExtra("task_action");
Log.i(TAG,"receiver action:" + action);
SystemClock.sleep(3000);
if("com.lgl.test.ACTION".equals(action)){
Log.i(TAG,"handler action:" + action);
}
}
@Override
public void onDestroy() {
Log.i(TAG,"onDestroy");
super.onDestroy();
}
}
線程池
線程池的優點與構成
線程池的優點:
(1)可以重用線程池中的線程,避免線程的多次創建和銷燬以節省資源。
(2)可以控制線程池中的最大併發數,避免大量的線程之間因互相搶佔系統資源而導致阻塞現象。
(3)進行線程管理,提供定時/循環間隔執行等功能
線程池實現了Executor接口,Android中的一個具體實現是ThreadPoolExcutor,其中包含了一些參數。
//構造參數
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
1.CorePoolSize:核心線程數,空閒下的核心線程常規情況下是不會被銷燬的,但是可以設置一個boolean變量allowCoreThreadTimeOut,如果該變量爲true的情況下,核心線程空閒時間超過AliveTime就會被銷燬。
2.maxiumPoolSize:最大線程數,當活動線程超過這個數時,新任務會進入阻塞。
3.keepAliveTime:非核心線程下的允許空閒時間,非核心線程如果超過該時間會銷燬該線程。當allowCoreThreadTimeOut爲true時,也會對核心線程有效。
4.workQueue,任務隊列,通過線程池的execute()方法提交的Runnable對象會存儲在這個參數中。
5.threadFactory,線程工廠。是一個抽象類,用於創建線程。其中只包含了一個方法Thread newThread(Runnable r)
。
6.handler:當線程池無法執行新任務時進行調度,一般會拋出異常。
ThreadPoolExecutor的工作原理:
當一個任務到來後,
如果當前所使用的線程數小於核心線程數,就直接開啓一個核心線程來執行這個任務;
否則噹噹前所使用的線程數大於等於核心線程數,則讓任務進入任務隊列等待;
如果任務隊列已經滿了的話,會啓動一個非核心線程;
如果當前所使用的線程數等於最大線程數,那麼就拒絕執行該任務,並通過Handler來拋出異常。
線程池的分類
1.FixThreadPool:線程數量固定的線程池,其中全部是核心線程,核心線程始終存活。
能快速響應外界請求。
2.:CachedThreadPool:該線程池全部爲非核心線程,最大線程量爲Integer.MAX_VALUE。時間超過60s線程會被回收。當整個線程池處於閒置狀態時,所有線程都會因爲超時而被回收,就變成了一個空的線程池,幾乎不會佔系統資源;
適合於執行大量的耗時短的任務。
3.ScheduledThreadPool:核心線程數量固定,非核心線程數量不固定,非核心線程只要處於空閒就會銷燬。
適合於定時任務和具有固定週期的任務。
4.SingeleThreadExcutor:只有一個核心線程,確保所有的任務都在一個線程中按順序進行
將外界任務統一到一個線程中,無需考慮線程同步問題。