本篇文章主要介紹以下幾個(gè)知識(shí)點(diǎn):
- 主線程和子線程
- Android 中的線程形態(tài)
- Android 中的線程池
11.1 主線程和子線程
從用途上來(lái)說(shuō),線程分主線程和子線程棚点。
主線程舔琅,指進(jìn)程所擁有的線程碗硬,Android 中也叫 UI 線程扛或,主要處理和界面相關(guān)的事情绵咱。
子線程,也叫工作線程(除主線程以外的線程都是子線程)熙兔,往往用于執(zhí)行耗時(shí)操作悲伶。
注:若在主線程中執(zhí)行耗時(shí)操作會(huì)出現(xiàn) ANR。
11.2 Android 中的線程形態(tài)
Android 中的線程形態(tài)除傳統(tǒng)的 Thread
外住涉,還包含 AsyncTask
麸锉、HandlerThread
、IntentService
等舆声。
11.2.1 AsyncTask
AsyncTask 是一種輕量級(jí)的異步任務(wù)類(lèi)花沉,可在線程池中執(zhí)行后臺(tái)任務(wù),把執(zhí)行的進(jìn)度和最終結(jié)果傳給主線程并更新 UI媳握。它封裝了 Thread
和 Handler
主穗,但不適合進(jìn)行特別耗時(shí)的后臺(tái)任務(wù)(建議使用線程池)。
AsyncTask 是一個(gè)抽象的泛型類(lèi)毙芜,如下:
// 三個(gè)泛型參數(shù):
// 1. Params 參數(shù)的類(lèi)型
// 2. Progress 后臺(tái)任務(wù)的執(zhí)行進(jìn)度的類(lèi)型
// 3. Result 后臺(tái)任務(wù)的返回結(jié)果的類(lèi)型
public abstract class AsyncTask<Params, Progress, Result>{ ... }
AsyncTask 提供了4個(gè)核心方法,典型示例如下:
/**
* Function:模擬文件下載過(guò)程
* 輸入?yún)?shù)類(lèi)型為URL争拐,后臺(tái)任務(wù)進(jìn)程參數(shù)為Integer
* 當(dāng)要執(zhí)行下載任務(wù)時(shí):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 來(lái)更新任務(wù)進(jìn)度,它會(huì)調(diào)用 onProgressUpdate
* 另外绑雄,此方法需要返回計(jì)算結(jié)果給 onPostExecute
*/
@Override
protected Long doInBackground(URL... params) {
// 執(zhí)行具體的下載任務(wù)并通過(guò) 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)后臺(tái)任務(wù)的執(zhí)行進(jìn)度發(fā)生改變時(shí)調(diào)用
*/
@Override
protected void onProgressUpdate(Integer... values) {
setProgressPercent(values[0]);
}
/**
* 核心方法 4:
* 在主線程中執(zhí)行,在異步任務(wù)執(zhí)行之后調(diào)用万牺,result 是后臺(tái)任務(wù)的返回值
*/
@Override
protected void onPostExecute(Long result) {
showDialog("Download " + result + "bytes");
}
/**
* 當(dāng)異步任務(wù)被取消時(shí)調(diào)用
*/
@Override
protected void onCancelled() {
super.onCancelled();
}
}
AsyncTask 在具體使用過(guò)程中也有一些條件限制罗珍,如下:
1. AsyncTask
的類(lèi)必須在主線程中加載洽腺,即第一次訪問(wèn) AsyncTask
必須發(fā)生在主線程。
2. AsyncTask
的對(duì)象必須在主線程中創(chuàng)建覆旱。
3. execute
方法必須在 UI 線程調(diào)用蘸朋。
4. 不要在程序中直接調(diào)用 AsyncTask
提供的4個(gè)核心方法。
5. 一個(gè) AsyncTask
對(duì)象只能執(zhí)行一次扣唱,即只能調(diào)用一次 execute
方法藕坯。
11.2.2 AsyncTask 的工作原理
源碼分析。噪沙。炼彪。
11.2.3 HandlerThread
HandlerThread 繼承了 Thread
,是一種可使用 Handler
的 Thread
正歼,其實(shí)現(xiàn)核心在 run
方法中:
public void run(){
mTid = Process.myTid();
// 創(chuàng)建消息隊(duì)列
Looper.prepare();
synchronized(this){
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
// 開(kāi)啟消息循環(huán)
Looper.loop();
mTid = -1;
}
普通 Thread
主要用于在 run
方法中執(zhí)行一個(gè)耗時(shí)任務(wù)辐马,而 HandlerThread
在內(nèi)部創(chuàng)建了消息隊(duì)列,外界需要通過(guò) Handler
的消息方式來(lái)通知 HandlerThread
執(zhí)行一個(gè)具體的任務(wù)朋腋。
HandlerThread 的 run
方法是一個(gè)無(wú)線循環(huán)齐疙,當(dāng)不用 HandlerThread
時(shí)可通過(guò)其 quit
或 quitSafely
來(lái)終止線程的執(zhí)行。
11.2.4 IntentService
IntentService 是一個(gè)繼承了 Service
的抽象類(lèi)旭咽,是一種特殊的 Service
贞奋。
IntentService 可用于執(zhí)行后臺(tái)耗時(shí)任務(wù),任務(wù)執(zhí)行后它會(huì)自動(dòng)停止穷绵,其優(yōu)先級(jí)比單純的線程要高轿塔,時(shí)候執(zhí)行一些高優(yōu)先級(jí)的后臺(tái)任務(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)建一個(gè) HandlerThread
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
// 2.構(gòu)造一個(gè) Handler 對(duì)象 mServiceHandler
// 這樣通過(guò) mServiceHandler 發(fā)送的消息最終都會(huì)在 HandlerThread 中執(zhí)行
mServiceHandler = new ServiceHandler(mServiceLooper);
}
每次啟動(dòng) IntentService
勾缭,它的 onStartCommand
方法就會(huì)調(diào)用一次,處理每個(gè)后臺(tái)任務(wù)的 Intent
目养,onStartCommand
調(diào)用了 onStart
方法如下:
public void onStart(Intent intent, int startId){
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
// IntentService 通過(guò) mServiceHandler 發(fā)送了一個(gè)消息
// 這個(gè)消息會(huì)在 HandlerThread 中被處理
mServiceHandler.sendMessage(msg);
}
其中 ServiceHandler
的實(shí)現(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
是一個(gè)抽象方法俩由,其作用是從 intent
參數(shù)中區(qū)分具體的任務(wù)并執(zhí)行這些任務(wù)。
11.3 Android 中的線程池
線程池的優(yōu)點(diǎn)可概括為以下3點(diǎn):
1. 重用線程池中的線程癌蚁,避免因?yàn)榫€程的創(chuàng)建和銷(xiāo)毀所帶來(lái)的性能開(kāi)銷(xiāo)幻梯。
2. 能有效控制線程池的最大并發(fā)數(shù),避免大量的線程之間因互相搶占系統(tǒng)資源而導(dǎo)致的阻塞現(xiàn)象努释。
3. 能夠?qū)€程進(jìn)行簡(jiǎn)單的管理碘梢,并提供定時(shí)執(zhí)行以及指定間隔循環(huán)執(zhí)行等功能。
Android 中的線程池都是直接或間接通過(guò)配置 ThreadPoolExecutor
來(lái)實(shí)現(xiàn)的伐蒂。
11.3.1 ThreadPoolExecutor
ThreadPoolExecutor 是線程池的真正實(shí)現(xiàn)煞躬,其構(gòu)造方法提供了一系列參數(shù)來(lái)配置線程池如下:
// corePoolSize 線程池的核心線程,默認(rèn)情況下會(huì)在線程池中一直存活
// maximumPoolSize 線程中所能容納的最大線程數(shù)
// keepAliveTime 非核心線程閑置時(shí)的超時(shí)時(shí)長(zhǎng),超過(guò)這個(gè)時(shí)長(zhǎng)非核心線程就會(huì)被回收
// unit 用于指定 keepAliveTime 參數(shù)的時(shí)間單位
// workQueue 線程池中的任務(wù)隊(duì)列恩沛,通過(guò)線程池的 execute 方法提交的 Runnable 對(duì)象會(huì)存儲(chǔ)在此參數(shù)中
// threadFactory 線程工廠在扰,為線程池提供創(chuàng)建新線程的功能
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable>workQueue,
ThreadFactory threadFactory)
ThreadPoolExecutor 執(zhí)行任務(wù)時(shí)大致遵循如下規(guī)則:
1. 若線程池中的線程數(shù)量未達(dá)到核心線程的數(shù)量,那么會(huì)直接啟動(dòng)一個(gè)核心線程來(lái)執(zhí)行任務(wù)复唤。
2. 若線程池中的線程數(shù)量已達(dá)到或超過(guò)核心線程的數(shù)量健田,那么任務(wù)會(huì)被插入到任務(wù)隊(duì)列中等待執(zhí)行。
3. 若在步驟 2 中無(wú)法將任務(wù)插入到任務(wù)隊(duì)列中佛纫,此時(shí)若線程數(shù)量未達(dá)到線程池規(guī)定的最大值妓局,則會(huì)立刻啟動(dòng)一個(gè)非核心線程來(lái)執(zhí)行任務(wù)。
4. 若步驟 3 中線程數(shù)量已達(dá)到線程池規(guī)定的最大值呈宇,那么就拒絕執(zhí)行任務(wù)好爬,會(huì)調(diào)用 RejectedExecutionHandler
的 rejectExecution
來(lái)通知調(diào)用者。
ThreadPoolExecutor 的配置參數(shù)可在 AsyncTask
中體現(xiàn)甥啄,配置后的線程池規(guī)格如下:
核心線程數(shù)等于 CPU 核心數(shù) + 1
線程池的最大線程數(shù)為 CPU 核心數(shù)的 2 倍 + 1
核心線程無(wú)超時(shí)機(jī)制存炮,非核心線程子閑置時(shí)的超時(shí)時(shí)間為 1 秒
任務(wù)隊(duì)列的容量為 128
11.3.2 線程池的分類(lèi)
1. FixedThreadPool
通過(guò) executors
的 newFixedThreadPool
方法來(lái)創(chuàng)建,是一種線程數(shù)量固定的線程池蜈漓,當(dāng)線程處于空閑時(shí)不會(huì)被回收(除非線程池被關(guān)閉了)穆桂,其實(shí)現(xiàn)如下:
public static ExecutorService newFixedThreadPool(int nThreads){
// FixedThreadPool 只有核心線程并且沒(méi)用超時(shí)機(jī)制,任務(wù)隊(duì)列也沒(méi)有大小限制
// 意味著它能夠更加快速地響應(yīng)外界的請(qǐng)求
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockQueue<Runnable>());
}
2. CachedThreadPool
通過(guò) executors
的 newCachedThreadPool
方法來(lái)創(chuàng)建融虽,是一種線程數(shù)量不定的線程池享完,只有非核心線程,且最大線程數(shù)為 Integer.MAX_VALUE
有额,其實(shí)現(xiàn)如下:
public static ExecutorService newCachedThreadPool(){
// 線程池中的空閑線程都有超時(shí)機(jī)制時(shí)長(zhǎng)為60秒般又,超過(guò)60秒閑置線程就會(huì)被回收
// CachedThreadPool 的任務(wù)隊(duì)列相當(dāng)于一個(gè)空集合,任何任務(wù)都會(huì)被立即執(zhí)行
// 這類(lèi)線程池比較適合執(zhí)行大量的耗時(shí)較少的任務(wù)
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>());
}
3. ScheduledThreadPool
通過(guò) executors
的 newScheduledThreadPool
方法來(lái)創(chuàng)建巍佑,其核心線程數(shù)固定茴迁,非核心線程數(shù)無(wú)限制,其實(shí)現(xiàn)如下:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize){
// 這類(lèi)線程池主要用于執(zhí)行定時(shí)任務(wù)和具有固定周期的重復(fù)任務(wù)
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize){
super(corePoolSize, Integer.MAX_VALUE, 0, NANSECONDS, new DelayedWorkQueue());
}
4. SingleThreadExecutor
通過(guò) executors
的 newSingleThreadExecutor
方法來(lái)創(chuàng)建萤衰,內(nèi)部只有一個(gè)核心線程堕义,確保所有的任務(wù)都在同一個(gè)線程中按順序執(zhí)行。其實(shí)現(xiàn)如下:
public static ExecutorService newSingleThreadExecutor(){
// SingleThreadExecutor 的意義在于統(tǒng)一所有的外界任務(wù)到一個(gè)線程中脆栋,使得這些任務(wù)之間不需要處理線程同步的問(wèn)題
return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
}
以上便是 Android 中常見(jiàn)的 4 種線程池胳螟,實(shí)際開(kāi)發(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);
本篇文章就介紹到這。