本篇文章主要介紹以下幾個知識點:
- 主線程和子線程
- Android 中的線程形態(tài)
- Android 中的線程池

11.1 主線程和子線程
從用途上來說,線程分主線程和子線程。
主線程,指進(jìn)程所擁有的線程,Android 中也叫 UI 線程,主要處理和界面相關(guān)的事情。
子線程,也叫工作線程(除主線程以外的線程都是子線程),往往用于執(zhí)行耗時操作。
注:若在主線程中執(zhí)行耗時操作會出現(xiàn) ANR。
11.2 Android 中的線程形態(tài)
Android 中的線程形態(tài)除傳統(tǒng)的 Thread 外,還包含 AsyncTask、HandlerThread、IntentService 等。
11.2.1 AsyncTask
AsyncTask 是一種輕量級的異步任務(wù)類,可在線程池中執(zhí)行后臺任務(wù),把執(zhí)行的進(jìn)度和最終結(jié)果傳給主線程并更新 UI。它封裝了 Thread 和 Handler,但不適合進(jìn)行特別耗時的后臺任務(wù)(建議使用線程池)。
AsyncTask 是一個抽象的泛型類,如下:
// 三個泛型參數(shù):
// 1. Params 參數(shù)的類型
// 2. Progress 后臺任務(wù)的執(zhí)行進(jìn)度的類型
// 3. Result 后臺任務(wù)的返回結(jié)果的類型
public abstract class AsyncTask<Params, Progress, Result>{ ... }
AsyncTask 提供了4個核心方法,典型示例如下:
/**
* Function:模擬文件下載過程
* 輸入?yún)?shù)類型為URL,后臺任務(wù)進(jìn)程參數(shù)為Integer
* 當(dāng)要執(zhí)行下載任務(wù)時:new DownloadFilesTask().execute(url1, url2, url3);
*/
public class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
/**
* 核心方法 1:
* 在主線程中執(zhí)行,在異步任務(wù)執(zhí)行之前調(diào)用,做一些準(zhǔn)備工作
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
}
/**
* 核心方法 2:
* 在主線池中執(zhí)行,用于執(zhí)行異步任務(wù),params 是異步任務(wù)的輸入?yún)?shù)
* 此方法中可用 publishProgress 來更新任務(wù)進(jìn)度,它會調(diào)用 onProgressUpdate
* 另外,此方法需要返回計算結(jié)果給 onPostExecute
*/
@Override
protected Long doInBackground(URL... params) {
// 執(zhí)行具體的下載任務(wù)并通過 publishProgress更新下載進(jìn)度
int count = params.length;
long totalSize = 0;
for (int i = 0; i < count; i++){
totalSize += Downloader.downloadFile(params[i]);
publishProgress((int)(i/(float)count) * 100);
if (isCancelled())//判斷是否被取消
break;
}
// 返回下載的總字節(jié)數(shù)
return totalSize;
}
/**
* 核心方法 3:
* 在主線程中執(zhí)行,當(dāng)后臺任務(wù)的執(zhí)行進(jìn)度發(fā)生改變時調(diào)用
*/
@Override
protected void onProgressUpdate(Integer... values) {
setProgressPercent(values[0]);
}
/**
* 核心方法 4:
* 在主線程中執(zhí)行,在異步任務(wù)執(zhí)行之后調(diào)用,result 是后臺任務(wù)的返回值
*/
@Override
protected void onPostExecute(Long result) {
showDialog("Download " + result + "bytes");
}
/**
* 當(dāng)異步任務(wù)被取消時調(diào)用
*/
@Override
protected void onCancelled() {
super.onCancelled();
}
}
AsyncTask 在具體使用過程中也有一些條件限制,如下:
1. AsyncTask 的類必須在主線程中加載,即第一次訪問 AsyncTask 必須發(fā)生在主線程。
2. AsyncTask 的對象必須在主線程中創(chuàng)建。
3. execute 方法必須在 UI 線程調(diào)用。
4. 不要在程序中直接調(diào)用 AsyncTask 提供的4個核心方法。
5. 一個 AsyncTask 對象只能執(zhí)行一次,即只能調(diào)用一次 execute 方法。
11.2.2 AsyncTask 的工作原理
源碼分析。。。
11.2.3 HandlerThread
HandlerThread 繼承了 Thread,是一種可使用 Handler 的 Thread,其實現(xiàn)核心在 run 方法中:
public void run(){
mTid = Process.myTid();
// 創(chuàng)建消息隊列
Looper.prepare();
synchronized(this){
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
// 開啟消息循環(huán)
Looper.loop();
mTid = -1;
}
普通 Thread 主要用于在 run 方法中執(zhí)行一個耗時任務(wù),而 HandlerThread 在內(nèi)部創(chuàng)建了消息隊列,外界需要通過 Handler 的消息方式來通知 HandlerThread執(zhí)行一個具體的任務(wù)。
HandlerThread 的 run 方法是一個無線循環(huán),當(dāng)不用 HandlerThread 時可通過其 quit 或 quitSafely 來終止線程的執(zhí)行。
11.2.4 IntentService
IntentService 是一個繼承了 Service 的抽象類,是一種特殊的 Service。
IntentService 可用于執(zhí)行后臺耗時任務(wù),任務(wù)執(zhí)行后它會自動停止,其優(yōu)先級比單純的線程要高,時候執(zhí)行一些高優(yōu)先級的后臺任務(wù)。
IntentService 封裝了 HandlerThread 和 Handler,這在其 onCreate 方法中可知:
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();
// 1.創(chuàng)建一個 HandlerThread
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
// 2.構(gòu)造一個 Handler 對象 mServiceHandler
// 這樣通過 mServiceHandler 發(fā)送的消息最終都會在 HandlerThread 中執(zhí)行
mServiceHandler = new ServiceHandler(mServiceLooper);
}
每次啟動 IntentService,它的 onStartCommand 方法就會調(diào)用一次,處理每個后臺任務(wù)的 Intent,onStartCommand 調(diào)用了 onStart 方法如下:
public void onStart(Intent intent, int startId){
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
// IntentService 通過 mServiceHandler 發(fā)送了一個消息
// 這個消息會在 HandlerThread 中被處理
mServiceHandler.sendMessage(msg);
}
其中 ServiceHandler 的實現(xiàn)如下:
private final class ServiceHandler extends Handler{
public ServiceHandler(Looper looper){
super(looper);
}
@Override
public void handleMessage(Message msg){
onHandleIntent((Intent)mag.obj);
// 停止服務(wù)
stopSelf(msg.arg1);
}
}
IntentService 的 onHandleIntent 是一個抽象方法,其作用是從 intent 參數(shù)中區(qū)分具體的任務(wù)并執(zhí)行這些任務(wù)。
11.3 Android 中的線程池
線程池的優(yōu)點可概括為以下3點:
1. 重用線程池中的線程,避免因為線程的創(chuàng)建和銷毀所帶來的性能開銷。
2. 能有效控制線程池的最大并發(fā)數(shù),避免大量的線程之間因互相搶占系統(tǒng)資源而導(dǎo)致的阻塞現(xiàn)象。
3. 能夠?qū)€程進(jìn)行簡單的管理,并提供定時執(zhí)行以及指定間隔循環(huán)執(zhí)行等功能。
Android 中的線程池都是直接或間接通過配置 ThreadPoolExecutor 來實現(xiàn)的。
11.3.1 ThreadPoolExecutor
ThreadPoolExecutor 是線程池的真正實現(xiàn),其構(gòu)造方法提供了一系列參數(shù)來配置線程池如下:
// corePoolSize 線程池的核心線程,默認(rèn)情況下會在線程池中一直存活
// maximumPoolSize 線程中所能容納的最大線程數(shù)
// keepAliveTime 非核心線程閑置時的超時時長,超過這個時長非核心線程就會被回收
// unit 用于指定 keepAliveTime 參數(shù)的時間單位
// workQueue 線程池中的任務(wù)隊列,通過線程池的 execute 方法提交的 Runnable 對象會存儲在此參數(shù)中
// threadFactory 線程工廠,為線程池提供創(chuàng)建新線程的功能
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable>workQueue,
ThreadFactory threadFactory)
ThreadPoolExecutor 執(zhí)行任務(wù)時大致遵循如下規(guī)則:
1. 若線程池中的線程數(shù)量未達(dá)到核心線程的數(shù)量,那么會直接啟動一個核心線程來執(zhí)行任務(wù)。
2. 若線程池中的線程數(shù)量已達(dá)到或超過核心線程的數(shù)量,那么任務(wù)會被插入到任務(wù)隊列中等待執(zhí)行。
3. 若在步驟 2 中無法將任務(wù)插入到任務(wù)隊列中,此時若線程數(shù)量未達(dá)到線程池規(guī)定的最大值,則會立刻啟動一個非核心線程來執(zhí)行任務(wù)。
4. 若步驟 3 中線程數(shù)量已達(dá)到線程池規(guī)定的最大值,那么就拒絕執(zhí)行任務(wù),會調(diào)用 RejectedExecutionHandler 的 rejectExecution 來通知調(diào)用者。
ThreadPoolExecutor 的配置參數(shù)可在 AsyncTask 中體現(xiàn),配置后的線程池規(guī)格如下:
核心線程數(shù)等于 CPU 核心數(shù) + 1
線程池的最大線程數(shù)為 CPU 核心數(shù)的 2 倍 + 1
核心線程無超時機(jī)制,非核心線程子閑置時的超時時間為 1 秒
任務(wù)隊列的容量為 128
11.3.2 線程池的分類
1. FixedThreadPool
通過 executors 的 newFixedThreadPool 方法來創(chuàng)建,是一種線程數(shù)量固定的線程池,當(dāng)線程處于空閑時不會被回收(除非線程池被關(guān)閉了),其實現(xiàn)如下:
public static ExecutorService newFixedThreadPool(int nThreads){
// FixedThreadPool 只有核心線程并且沒用超時機(jī)制,任務(wù)隊列也沒有大小限制
// 意味著它能夠更加快速地響應(yīng)外界的請求
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockQueue<Runnable>());
}
2. CachedThreadPool
通過 executors 的 newCachedThreadPool 方法來創(chuàng)建,是一種線程數(shù)量不定的線程池,只有非核心線程,且最大線程數(shù)為 Integer.MAX_VALUE,其實現(xiàn)如下:
public static ExecutorService newCachedThreadPool(){
// 線程池中的空閑線程都有超時機(jī)制時長為60秒,超過60秒閑置線程就會被回收
// CachedThreadPool 的任務(wù)隊列相當(dāng)于一個空集合,任何任務(wù)都會被立即執(zhí)行
// 這類線程池比較適合執(zhí)行大量的耗時較少的任務(wù)
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>());
}
3. ScheduledThreadPool
通過 executors 的 newScheduledThreadPool 方法來創(chuàng)建,其核心線程數(shù)固定,非核心線程數(shù)無限制,其實現(xiàn)如下:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize){
// 這類線程池主要用于執(zhí)行定時任務(wù)和具有固定周期的重復(fù)任務(wù)
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize){
super(corePoolSize, Integer.MAX_VALUE, 0, NANSECONDS, new DelayedWorkQueue());
}
4. SingleThreadExecutor
通過 executors 的 newSingleThreadExecutor 方法來創(chuàng)建,內(nèi)部只有一個核心線程,確保所有的任務(wù)都在同一個線程中按順序執(zhí)行。其實現(xiàn)如下:
public static ExecutorService newSingleThreadExecutor(){
// SingleThreadExecutor 的意義在于統(tǒng)一所有的外界任務(wù)到一個線程中,使得這些任務(wù)之間不需要處理線程同步的問題
return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
}
以上便是 Android 中常見的 4 種線程池,實際開發(fā)也可根據(jù)需要靈活配置線程池。4 種線程池的使用方法如下:
Runnable command = new Runnable(){
@Override
public void run(){
SystemClock.sleep(2000);
}
};
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(4);
fixedThreadPool.execute(command);
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
cachedThreadPool .execute(command);
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(4);
// 2000ms 后執(zhí)行 command
scheduledThreadPool .schedule(command, 2000, TimeUnit.MILLISECONDS);
// 延遲 10ms 后,每隔 1000ms 執(zhí)行 一次command
scheduledThreadPool .scheduleAtFixedRate(command, 10, 1000, TimeUnit.MILLISECONDS);
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
singleThreadExecutor .execute(command);
本篇文章就介紹到這。