在 Android 中,除了 Thread 以外,還有 AsyncTask绢记、HandlerThread 和 IntentService 充當(dāng)線程的作用扁达,它們各自有不同的特點和適用場景。
AsyncTask 封裝了線程池和 Handler蠢熄,它能方便使用者在子線程中更新 UI跪解。HandlerThread 是使用 Handler 的線程。IntentService 是一個服務(wù)签孔,它比一般的后臺線程優(yōu)先級更高叉讥,不容易被系統(tǒng)殺死,所以可以更方便的執(zhí)行后臺線程饥追。
AsyncTask
AsyncTask 是一個輕量級的異步任務(wù)類图仓,它不適合進(jìn)行特別耗時的任務(wù)。
用法
public abstract class AsyncTask<Params, Progress, Result>
AsyncTask 是一個抽象的泛型類但绕,它提供了 Params救崔、Progress、Result 三個泛型參數(shù)捏顺。Params 表示參數(shù)的類型六孵,Progress 表示后臺任務(wù)的執(zhí)行進(jìn)度,Result 表示后臺任務(wù)返回結(jié)果的類型幅骄。如果 AsyncTask 不需要傳遞參數(shù)劫窒,以上三個都可以用 Void 來替代。
AsyncTask 有四個核心方法
- onPreExecute()
- doInBackground(Params... params)
- onProgressUpdate(Progress... values)
- onPostExecute()
onPreExecute 方法在主線程執(zhí)行拆座,一般用于準(zhǔn)備工作主巍。
doInBackground 方法會在線程池中執(zhí)行異步任務(wù),并將計算結(jié)果返回給 onPostExecute 方法挪凑。在該方法中孕索,可以調(diào)用 publishProgress 方法來更新 UI。
當(dāng)調(diào)用 publishProgress 方法后岖赋,onProgressUpdate 方法會在主線程中執(zhí)行檬果,它用于進(jìn)行 UI 操作。
當(dāng) doInBackground 方法完成后唐断,onPostExecute 方法將會執(zhí)行,它也存在于主線程杭抠。
需要注意的是脸甘,AsyncTask 還存在一個 onCancelled 方法。當(dāng)異步任務(wù)被取消時偏灿,它會被調(diào)用丹诀。該方法一旦被調(diào)用就不會再調(diào)用 onPostExecute 方法。它也同樣是在主線程中執(zhí)行。
注意事項
- AsyncTask 必須在主線程中加載
- AsyncTask 對象必須在主線程中創(chuàng)建
- 不要在程序中直接使用 onPreExecute铆遭、doInBackground硝桩、onProgressUpdate、onPostExecute 四個方法
- 一個 AsyncTask 對象只能執(zhí)行一次 execute 方法
- 從 Android 3.0 開始枚荣,AsyncTask 采用一個線程串行執(zhí)行任務(wù)碗脊,可以使用 executeOnExecutor 方法調(diào)整為并行
源碼分析
先看 execute 方法
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
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;
}
execute 方法調(diào)用了 executeOnExecutor 方法,在 executeOnExecutor 方法中橄妆,onPreExecute 方法首先得到執(zhí)行衙伶,然后是 sDefaultExecute
變量的 execute 方法。由于sDefaultExecute
是 SerialExecutor 類害碾,于是我們轉(zhuǎn)到該類
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);
}
}
}
從 SerialExecutor 中可以看到矢劲,execute 方法首先會把 Runnable 對象,也就是傳入的mFuture
慌随,插入到隊列 ArrayDeque 中芬沉。當(dāng)隊列頭部存在任務(wù)時,會調(diào)用 FutureTask 類(mFuture
)的 run 方法阁猜,并通過 scheduleNext 方法獲取下一個任務(wù)花嘶。如果隊列頭部沒有任務(wù),就會直接調(diào)用 scheduleNext 方法來獲取任務(wù)蹦漠。接著看 FutureTask 的 run 方法
public void run() {
...
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
...
}
} finally {
...
}
}
可以看到椭员,在這個方法中會調(diào)用 callable 的 call 方法,那這個方法是什么笛园?
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
};
mFuture = new FutureTask<Result>(mWorker) {
...
};
}
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
根據(jù)這一段代碼隘击,再結(jié)合前面的關(guān)系,execute 方法最終是調(diào)用了mWork
的 call 方法研铆。在這個方法中埋同,mTaskInvoked
被設(shè)為true
,表示當(dāng)前任務(wù)已經(jīng)被調(diào)用棵红,然后會執(zhí)行 doInBackground 方法凶赁,并將返回值傳給 postResult 方法
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
在 postResult 方法中,sHandler 會發(fā)送出MESSAGE_POST_RESULT
逆甜,來看 sHandler
private static final InternalHandler sHandler = new InternalHandler();
private static class InternalHandler extends Handler {
@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
是一個靜態(tài)的 Handler 對象,而靜態(tài)成員會在加載類時初始化交煞,為了能切換回主線程咏窿,這個sHandler
必須在主線程中創(chuàng)建,這就相當(dāng)于要求 AsyncTask 在主線程中加載素征。收到MESSAGE_POST_RESULT
后會調(diào)用 finish 方法集嵌,來看
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
如果任務(wù)被取消了萝挤,將會調(diào)用 onCancelled 方法。如果正常執(zhí)行根欧,會調(diào)用 onPostExecute 方法怜珍,參數(shù)是 doInBackground 方法的結(jié)果。
HandlerThread
它是可以使用 Handler 的 Thread凤粗,看它的 run 方法
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
在 run 方法中酥泛,通過Looper.prepare()
創(chuàng)建消息隊列侈沪,Looper.loop()
開啟消息循環(huán)揭璃。
HandlerThread 和 Handler 一樣,使用時需要通過 sendMessage 方法發(fā)送消息來處理任務(wù)亭罪。使用 HandlerThread 時瘦馍,需要通過 start 開啟線程,并實現(xiàn) handleMessage 方法來完成處理邏輯
HandlerThread handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();
Handler workderHandler = new Handler(handlerThread.getLooper()) {
@Override
public boolean handleMessage(Message msg) {
// 處理消息
return true;
}
}
當(dāng)使用完畢后应役,記得通過 quit 或 quitSafely 方法退出線程循環(huán)情组,避免內(nèi)存泄漏。
IntentService
IntentService 是一個抽象類箩祥,它繼承了 Service院崇。使用時需要創(chuàng)建它的子類。IntentService 一般用于執(zhí)行后臺程序袍祖,原因是它屬于 Service底瓣,導(dǎo)致它比一般線程優(yōu)先級要高,更不容易被殺死蕉陋。它適用于高優(yōu)先級的后臺任務(wù)捐凭。
使用方法
必須實現(xiàn) onHandleIntent 方法以執(zhí)行后臺任務(wù)〉树蓿可根據(jù)需求重寫 onCreate茁肠、onStartCommand、onDestroy 方法缩举。
源碼分析
IntentService 內(nèi)部封裝了 Handler 和 HandlerThread垦梆。先看它的 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();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
第一次啟動 IntentService 會調(diào)用它的 onCreate 方法,這個方法中會創(chuàng)建 HandlerThread仅孩。
再看 onStartCommand 方法
public int onStartCommand(Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
onStartCommand 方法調(diào)用了 onStart 方法托猩,那么轉(zhuǎn)到 onStart
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
在 onStart 方法中,intent
被賦給msg.obj
杠氢,并通過 ServiceHandler 實例發(fā)送了出去站刑,ServiceHandler 繼承自 Handler,意味著這條消息最終會來到 handleMessage 方法中鼻百,于是轉(zhuǎn)到該方法
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);
}
}
在這里看到绞旅,會在 onHandleIntent 方法中進(jìn)行處理,結(jié)合前面的代碼可以知道温艇,該方法中的 Intent 對象和通過 startIntent 方法中轉(zhuǎn)入的 Intent 對象是相同的因悲,我們可以通過這個 Intent 對象解析出外界啟動 IntentService 時傳入的參數(shù),通過這些參數(shù)就可區(qū)分具體的后臺任務(wù)勺爱。onHandleIntent 是一個抽象方法晃琳,需要我們在子類中實現(xiàn)。
stopSelf(mig.arg1)
方法用來嘗試停止任務(wù)琐鲁。注意區(qū)分stoopSelf(int startId)
方法和stopSelf()
方法卫旱,前者會等所有消息處理完之后才停止服務(wù),而后者會立刻停止服務(wù)围段。
線程池
線程池有以下幾個優(yōu)點:
- 重用線程池中的線程顾翼,減少線程創(chuàng)建和銷毀的性能開銷
- 控制最大線程數(shù),避免因線程過多而搶占系統(tǒng)資源導(dǎo)致的阻塞
- 能夠?qū)M(jìn)程進(jìn)行管理
Android 中的線程池來自 Java 的 Executor奈泪,它是一個接口适贸,真正的實現(xiàn)是 ThreadPoolExecutor。
ThreadPoolExecutor
基本介紹
它的構(gòu)造方法提供了一系列參數(shù)來配置線程池
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
- corePoolSize
線程池的核心線程數(shù)涝桅,默認(rèn)情況下拜姿,核心線程會一直在線程池里存活。
- maximumPoolSize
最大線程數(shù)冯遂,當(dāng)活動線程達(dá)到最大值時蕊肥,后續(xù)的任務(wù)會被阻塞。
- keepAliveTime
非核心線程存活時間蛤肌,當(dāng)非核心線程閑置時間超過它時壁却,就會被回收。當(dāng) ThreadPoolExecutor 的 allowCoreThreadTimeOut 設(shè)置為true
時寻定,keepAliveTime 同樣會作用于核心線程儒洛。
- unit
keepAliveTime 的時間單位,常用的有狼速,TimeOut.MILLISECONDS
(毫秒)琅锻、TimeOut.SECONDS
(秒)。
- workQueue
任務(wù)隊列向胡,execute 方法提交的 Runnable 對象會儲存在這里恼蓬。
- threadFactory
線程工廠,為線程池提供創(chuàng)建新線程的功能僵芹。它是一個接口处硬。
大致執(zhí)行規(guī)則
- 當(dāng)線程池中線程數(shù)小于核心線程數(shù),啟動核心線程執(zhí)行該任務(wù)
- 當(dāng)線程池中線程數(shù)大于等于核心線程數(shù)拇派,將任務(wù)加入任務(wù)隊列
- 當(dāng)任務(wù)隊列已滿荷辕,并且未達(dá)到線程池最大值凿跳,啟動非核心線程來執(zhí)行該任務(wù)
- 當(dāng)任務(wù)數(shù)大于最大線程數(shù)時,拒接執(zhí)行任務(wù)疮方,會調(diào)用 RejectedExecutionHandler 的 rejectedExecution 方法通知調(diào)用者
線程池的分類
- FixedThreadPool
一種數(shù)量恒定的線程池控嗜,只有核心線程,且核心線程沒有超時機制骡显。這意味著疆栏,除非線程池被關(guān)閉,否則這些線程將一直存在惫谤。并且它的任務(wù)隊列大小沒有限制壁顶。該線程可以通過 newFixedThreadPool 方法來創(chuàng)建,方法如下
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
- CachedThreadPool
一種數(shù)量不定的線程池溜歪,只有非核心線程若专,基本上無數(shù)量限制。這些線程的超時時間設(shè)置為 60 秒痹愚。它有一個特殊的任務(wù)隊列 SynchronousQueue富岳,可以被理解為一個無法儲存元素的隊列。所以拯腮,當(dāng)有新的任務(wù)加入且無閑置線程時窖式,會立刻啟動一個線程執(zhí)行該任務(wù)。該線程池適用于執(zhí)行大量耗時少的任務(wù)动壤。它使用 newCachedThreadPool 創(chuàng)建萝喘,方法如下
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
- ScheduledThreadPool
它的核心線程數(shù)是固定的,而非核心線程數(shù)沒有限制琼懊,但只要非核心線程閑置阁簸,就馬上會被回收。它適用于執(zhí)行定時和有固定周期的任務(wù)哼丈。使用 newScheduledThreadPool 創(chuàng)建启妹,方法如下
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
- SingleThreadPool
該線程池只有一個線程,即核心線程醉旦,它確保所有的任務(wù)都在一個線程中按順序執(zhí)行饶米。當(dāng)所有的任務(wù)都添加到該線程時,可以不必考慮同步問題车胡。它使用 newSingleThreadExecutor 方法創(chuàng)建檬输,如下
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}