Android開發藝術之執行緒
本篇的主要內容是Android的執行緒和執行緒池:
-
概述
-
執行緒形態
-
AsyncTask
-
HandlerThread
-
IntentService
-
執行緒池
一.概述
1.含義:執行緒是CPU排程的最小單元。
2.特點:執行緒是一種 受限 的系統資源。即執行緒不可無限制的產生且執行緒的建立和銷燬都有一定的開銷。
Q:如何避免頻繁建立和銷燬執行緒所帶來的系統開銷?
A:採用 執行緒池 ,池中會快取一定數量的執行緒,進而達到效果。
3.分類:
-
按用途可分為兩類:
-
主執行緒:一般一個程序只有一個主執行緒,主要處理 介面互動 相關的邏輯。
-
子執行緒:除主執行緒之外都是子執行緒,主要用於執行 耗時操作 。
-
按形態可分為三類:
-
AsyncTask:底層封裝了執行緒池和 Handler ,便於執行後臺任務以及在子執行緒中進行UI操作。
-
HandlerThread:一種具有 訊息迴圈 的執行緒,其內部可使用Handler。
-
IntentService:是一種 非同步、會自動停止 的服務,內部採用HandlerThread。

Picture
二.執行緒形態
對於主執行緒和子執行緒相信已經非常熟悉了,現在主要學習以下三種形態的執行緒:
1.AsyncTask
a.AsyncTask:一種輕量級的 非同步 任務類。
在Android中實現非同步任務機制有兩種方式:( https://www.jianshu.com/p/1c79fb5296b 6" target="_blank">Handler]和AsyncTask。
-
Handler機制存在的 問題 :程式碼相對臃腫;多工同時執行時不易精確控制執行緒。
-
引入AsyncTask的 好處 :建立非同步任務更簡單,直接繼承它可方便實現後臺非同步任務的執行和進度的回撥更新UI,而無需編寫任務執行緒和Handler例項就能完成相同的任務。
b.AsyncTask是抽象的泛型類,其組成成員有:
-
三個泛型引數:
-
Params
:表示執行AsyncTask需要傳入的引數,可用於在後臺任務中使用; -
Progress
:表示後臺任務執行的進度; -
Result
: 表示後臺任務的返回結果的型別; -
若沒有傳遞具體的引數,這三個泛型引數都可使用void。如:
//含義:在執行AsyncTask時不需要傳入引數給後臺任務、使用整型資料來作為進度顯示單位,最後使用布林型資料來反饋執行結果 public abstract class AsyncTask<Void, Integer, Boolean>
-
五個核心方法:
-
onPreExecute()
: -
執行在:主執行緒
-
呼叫時刻:在非同步任務執行之前被呼叫
-
作用:可用於進行一些介面上的 初始化 操作
-
doInBackground(Params…params)
: -
執行在:子執行緒
-
作用:可用於處理所有的 耗時任務 。若需要更新UI需呼叫
publishProgress(Progress...)
方法 -
注意:任務一旦完成就通過return語句將任務的執行結果返回,若Result被指定為void,就可不返回執行結果
-
onProgressUpdate(Progress…values)
: -
執行在:主執行緒
-
呼叫時刻:在後臺任務中呼叫
publishProgress(Progress...)
之後該方法會被呼叫 -
作用:可利用方法中攜帶的引數如Progress來對 UI 進行相應地更新
-
onPostExecute(Result result)
: -
執行在:主執行緒
-
呼叫時刻:在非同步任務執行完畢並通過return語句返回時被呼叫
-
作用:可利用方法中返回的資料來進行一些 UI 操作
-
onCancelled()
: -
執行在:主執行緒
-
呼叫時刻:當非同步任務被取消時被呼叫
-
作用:可用於做 介面取消 的更新
-
注意:
-
不要直接呼叫onPreExecute()、doInBackground()、onProgressUpdate()、onPostExecute)和onCancelled()方法
-
AsyncTask物件必須在 主執行緒 建立
-
開始和結束非同步任務的方法:
-
execute(Params...params)
-
必須在 主執行緒 中呼叫
-
作用:表示開始一個非同步任務
-
注意:一個非同步物件只能呼叫一次execute()方法
-
cancel(booleanmayInterruptIfRunning)
-
必須在 主執行緒 中呼叫
-
作用:表示停止一個非同步任務
比如自定義一個AsyncTask,來模擬一個下載任務:
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);
c.工作原理
-
內部有一個靜態的Handler物件即 InternalHandler :
-
作用:將執行環境從執行緒池切換到主執行緒;通過它來發送任務執行的進度以及執行結束等訊息。
-
注意:必須在主執行緒中建立
-
內部有兩個執行緒池:
-
SerialExecutor:用於任務的排隊,預設是 序列 的執行緒池
-
THREAD_POOL_EXECUTOR:用於真正執行任務。
-
排隊執行過程:
-
把引數Params封裝為FutureTask物件,相當於Runnable;
-
呼叫
SerialExecutor.execute()
將FutureTask插入到任務佇列tasks; -
若沒有正在活動的AsyncTask任務,則就會執行下一個AsyncTask任務。執行完畢後會繼續執行其他任務直到所有任務都完成。即預設使用 序列 方式執行任務。
注意:AsyncTask不適用於進行特別耗時的後臺任務,而是建議用執行緒池。
2.HandlerThread
a.HandlerThread是一個執行緒類,它繼承自Thread
與普通Thread的區別:具有 訊息迴圈 的效果。原理:
- 內部
HandlerThread.run()
方法中有Looper,通過Looper.prepare()
來建立訊息佇列,並通過Looper.loop()
來開啟訊息迴圈。
b實現方法
-
例項化一個HandlerThread物件,引數是該執行緒的名稱;
-
通過
HandlerThread.start()
開啟執行緒; -
例項化一個Handler並傳入HandlerThread中的looper物件,使得與HandlerThread繫結;
-
利用Handler即可執行非同步任務;
-
當不需要HandlerThread時,通過
HandlerThread.quit()
/quitSafely()
方法來終止執行緒的執行。
private HandlerThread myHandlerThread ; private Handler handler ; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //例項化HandlerThread myHandlerThread = new HandlerThread("myHandler") ; //開啟HandlerThread myHandlerThread.start(); //將Handler物件與HandlerThread執行緒繫結 handler =new Handler(myHandlerThread.getLooper()){ @Override publicvoid handleMessage(Message msg) { super.handleMessage(msg); // 這裡接收Handler發來的訊息,執行在handler_thread執行緒中 //TODO... } }; //在主執行緒給Handler傳送訊息 handler.sendEmptyMessage(1) ; new Thread(new Runnable() { @Override publicvoid run() { //在子執行緒給Handler傳送資料 handler.sendEmptyMessage(2) ; } }).start(); } @Override protected void onDestroy() { super.onDestroy(); //終止HandlerThread執行 myHandlerThread.quit() ; }
c.用途:
-
進行 序列 非同步通訊
-
構造IntentService
在之前學習淺析HandlerThread時知道在子執行緒使用Handler的方法,其實HandlerThread的出現使得這一過程變得更加簡便,更多解析見淺析HandlerThread
3.IntentService
a.IntentService是一個繼承自Service的抽象類
b.優點:
-
相比於執行緒:由於是服務,優先順序比執行緒高,更不容易被系統殺死。因此較適合執行一些 高優先順序 的後臺任務。
-
相比於普通Service:可 自動建立 子執行緒來執行任務,且任務執行完畢後 自動退出 。
c.IntentService內部封裝了HandlerThread和Handler,工作原理:
-
在
IntentService.onCreate()
裡建立一個Handle物件即 HandlerThread ,利用其內部的 Looper 會例項化一個 ServiceHandler 物件; -
任務請求的Intent會被封裝到 Message 並通過ServiceHandler傳送給Looper的 MessageQueue ,最終在HandlerThread中執行;
-
在
ServiceHandler.handleMessage()
中會呼叫IntentService.onHandleIntent()
,可在該方法中處理後臺任務的邏輯。

Picture
d.使用方法:
-
新建類並繼承IntentService,重寫
onHandleIntent()
方法,該方法: -
執行在:子執行緒,因此可以去處理一些耗時操作。
-
作用:從Intent引數中區分具體的任務並執行這些任務
-
在配置檔案中進行註冊。
-
在活動中利用Intent實現IntentService的啟動:
Intent intent = new Intent(this, MyService.class); intent.putExtra("xxx",xxx); startService(intent);//啟動服務
注意:無需手動停止服務, onHandleIntent()
執行結束之後,IntentService會自動停止。
三.執行緒池
1.優點
-
重用執行緒池中的執行緒,避免執行緒的建立和銷燬帶來的效能消耗;
-
有效控制執行緒池的 最大併發數 ,避免大量的執行緒之間因互相搶佔系統資源而導致阻塞現象;
-
進行 執行緒管理 ,提供定時/迴圈間隔執行等功能。
-
執行緒池的概念來源:Java中的 Executor ,它是一個介面。
-
執行緒池的真正實現: ThreadPoolExecutor ,提供一系列引數來配置執行緒池。
//構造引數 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
-
corePoolSize:核心執行緒數
-
預設情況下,核心執行緒會線上程中一直存活。
-
當設定ThreadPoolExecutor的 allowCoreThreadTimeOut 屬性為
-
true:表示核心執行緒閒置超過超時時長,會被回收;
-
false:表示核心執行緒不會被回收,會線上程池中一直存活。
-
maximumPoolSize:最大執行緒數
-
當活動執行緒數達到這個數值後,後續的任務將會被阻塞。
-
keepAliveTime:非核心執行緒超時時間
-
超過這個時長,閒置的非核心執行緒就會被回收。
-
當設定ThreadPoolExecutor的allowCoreThreadTimeTout屬性為true時,keepAliveTime對核心執行緒同樣有效。
-
unit:用於指定keepAliveTime引數的時間單位
-
單位有:TimeUnit.MILLISECONDS、TimeUnit.SECONDS、TimeUnit.MINUTES等;
-
workQueue:任務佇列
-
通過執行緒池的
execute()
方法提交的Runnable物件會儲存在這個引數中。 -
threadFactory:執行緒工廠,可建立新執行緒
-
是個介面,只有一個方法
Thread newThread(Runnable r)
。 -
handler:線上程池無法執行新任務時進行排程。
3.ThreadPoolExecutor的 預設工作策略 :
-
若程池中的執行緒數量 未達到 核心執行緒數,則會直接啟動一個核心執行緒執行任務。
-
若執行緒池中的執行緒數量 已達到 或者超過核心執行緒數量,則任務會被插入到任務列表等待執行。
-
若任務 無法插入 到任務列表中,往往由於任務列表已滿,此時如果
-
執行緒數量 未達到 執行緒池最大執行緒數,則會啟動一個非核心執行緒執行任務;
-
執行緒數量 已達到 執行緒池規定的最大值,則拒絕執行此任務,ThreadPoolExecutor會呼叫RejectedExecutionHandler的rejectedExecution方法來通知呼叫者。
4.ThreadPoolExecutor執行緒池的分類:
-
FixThreadPool:
-
含義:執行緒數量固定的執行緒池,所有執行緒都是 核心執行緒 ,當執行緒空閒時 不會 被回收。
-
特點:能 快速 響應外界請求。
-
CachedThreadPool:
-
含義:執行緒數量不定的執行緒池(最大執行緒數為 Integer.MAX_VALUE ),只有 非核心執行緒 ,空閒執行緒有超時機制,超時回收。
-
特點:適合於執行大量的 耗時較少 的任務
-
ScheduledThreadPool:
-
含義:核心執行緒數量 固定 ,非核心執行緒數量 不定 。
-
特點: 定時 任務和 固定 週期的任務。
-
SingleThreadExecutor:
-
含義:只有 一個核心執行緒 ,可確保所有的任務都在同一個執行緒中 按順序 執行。
-
特點:無需處理 執行緒同步 問題。
我自己是一名從事了5年Android的老程式設計師,辭職目前在做講師,今年年初我花了一個月整理了一份最適合2019年學習的Android學習乾貨,各個方面都有整理,送給每一位Android開發的小夥伴,這裡是開發者聚集地,歡迎初學和進階中的小夥伴。
加QQ群:457848807(招募中)

image