簡介
1. 線程分類
- 主線程(UI線程) : 處理和界面相關的事情.
- 子線程 : 處理耗時操作.
Android中規定, 只有在主線程中可以進行UI操作, 但是同時, 主線程中不能進行耗時操作,否則會產生ANR,因此耗時操作必須放到子線程中進行處理.
2. Android中多線程技術
- Thread
- AsyncTask : 底層用到了線程池.
封裝了線程池和Handler, 主要使用場景是在子線程中更新UI.
- IntentService : 底層使用Thread
內部采用HandlerThread來執行任務,當任務執行完成后IntentService就會自動退出. 從任務執行的角度看,IntentService很像是一個后臺線程,其實他是一個Service,正是因為他是服務所以不容易被系統殺死從而保證任務的正常進行.
- HandlerThread : 底層使用Thread
具有消息循環的線程, 內部可以使用Handler.
注意一 : 從Android 3.0 開始如果在子線程中進行網絡操作就會產生NetworkOnMainThreadException
AsyncTask
AsyncTask 是一個輕量級異步任務類, AsyncTask 封裝了Thread和Handler, 通過AsyncTask可以更加方便地執行后臺任務以及在主線程中訪問UI, 但是AsyncTask并不適合進行特別耗時的后臺任務,對于特別耗時的后臺任務來說建議使用線程池.
1. AsyncTask 簡單使用
- 創建自定義任務類,繼承自AsyncTask.
/**
* 自定義 AsyncTask 類.
* P1 : 輸入參數類型
* P2 : 進度類型
* P3 : 返回值類型
*/
private class DownloadTask extends AsyncTask<Integer, Integer, Integer> {
/**
* 1. 主線程
* 2. 執行任務之前執行, 常用來初始化任務,顯示UI等.
*/
@Override
protected void onPreExecute() {
Log.d(TAG, "onPreExecute: " + Thread.currentThread().getId());
}
/**
* 1. 子線程
* 2. onPreExecute執行后會立即執行這個方法.任務邏輯就是在這個方法中進行的.
* 3. 可以通過 publicProgress(Progress...) 將任務進度傳遞到onProgressUpdate.
* 4. 任務結果通過返回值返回, return.傳遞到onPostExecute.
* @param params P2
* @return P3
*/
@Override
protected Integer doInBackground(Integer... params) {
Log.d(TAG, "doInBackground: " + Thread.currentThread().getId());
for (int i = 0; i < params[0]; i++) {
// 檢查任務是否被取消
if (isCancelled()) {
return -1;
}
publishProgress(i);
}
return 0;
}
/**
* 1. 主線程
* 2. publicProgress被調用后會將進度傳遞到這個方法中.可以在這進行UI更新操作.
* @param values 任務進度.
*/
@Override
protected void onProgressUpdate(Integer... values) {
Log.d(TAG, "onProgressUpdate: " + Thread.currentThread().getId());
Log.d(TAG, "onProgressUpdate: " + values[0]);
}
/**
* 任務執行結束后,就會進入這個回調.
* @param integer 任務結果
*/
@Override
protected void onPostExecute(Integer integer) {
Log.d(TAG, "onPostExecute: " + Thread.currentThread().getId());
Log.d(TAG, "onPostExecute: " + integer);
}
/**
* 任務取消后調用
* @param integer doInBackground 返回值.
*/
@Override
protected void onCancelled(Integer integer) {
super.onCancelled(integer);
}
}
- 啟動任務
// 創建并啟動任務
new DownloadTask().execute(5);
- 輸出
//
D/MainActivity: onPreExecute: 1
//
D/MainActivity: doInBackground: 161
//
D/MainActivity: onProgressUpdate: 1 // 線程id
D/MainActivity: onProgressUpdate: 0 // 進度
D/MainActivity: onProgressUpdate: 1
D/MainActivity: onProgressUpdate: 1 // 進度
D/MainActivity: onProgressUpdate: 1
D/MainActivity: onProgressUpdate: 2 // 進度
D/MainActivity: onProgressUpdate: 1
D/MainActivity: onProgressUpdate: 3 // 進度
D/MainActivity: onProgressUpdate: 1
D/MainActivity: onProgressUpdate: 4 // 進度
//
D/MainActivity: onPostExecute: 1 // 線程id
D/MainActivity: onPostExecute: 0 // 結果
代碼中已經有了詳細的注釋,不進行過多解釋,通過Log也證實了.這里說下取消任務
AsyncTask 提供了 cancel(boolean)
取消任務.
- 參數含義:是否立即中斷線程執行,true表示立即終止線程,false表示允許任務完成.
- 調用了
cancel()
后onCancelled()
回調會被執行(UI線程),onPostExecute()
不會被執行了. - 調用后
isCancelled
會返回 true. - 建議
onInBackground()
中檢查isCancelled
以便盡快結束任務.
2. AsyncTask使用注意
- AsyncTask這個類必須在主線程中進行加載. 在Android 4.1 以后系統自動完成這一過程.Android 6.0 以后可以在子線程中加載
- AsyncTask必須在主線程中創建.
- execute() 必須在UI線程中進行調用.
- 不要在直接調用
onPreExecute(), onPostExecute(), onInBackground() ,onProgressUpdate()
等方法. - 一個AsyncTask 對象 只能調用一次execute方法,如果執行多個任務就創建多個任務.
- Android3.0 以后 ,AsyncTask用一個線程串行執行任務.
3. AsyncTask的工作原理
- 首先分析execute() 方法.由于它是直接調用了executeOnExecutor()因此主要分析一下后者
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
// 調用 : executeOnExecutor
// sDefaultExecutor 是一個串行的線程池Executor.
return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
// 檢查狀態, 此處可用證明為什么一個AsyncTask對象只可以調用一次execute方法.
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, 證實了onPreExecute他回調最先執行.
// onPreExecute在主線程中執行, 這也是AsyncTask的execute()必須在主線程調用的一個的原因.
onPreExecute();
// 保存參數
mWorker.mParams = params;
// 執行線程
// mFuture 是一個Runnable.
exec.execute(mFuture);
return this;
}
代碼中已經有了詳解的注釋這里就不過多的解釋了.
-
@MainThread
注解說明這兩個方法都必須在主線程中執行,因此execute()
方法必須在主線程中調用. -
if (mStatus != Status.PENDING){...}
中的判斷說明了,一個AsyncTask對象只可以調用一次execute()
方法. -
sDefaultExecutor
是一個串行的線程池主要用于任務排隊,并不負責任務的實際執行,源碼分析如下
// sDefaultExecutor 定義
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
// SERIAL_EXECUTOR : 用于任務排隊.
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
// SerialExecutor 串行線程池
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
// 執行Runnable
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
// 調度下一個.
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
// 啟動下一個Runnable
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
// 執行任務.
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
// mFuture是一個Runnable
private final FutureTask<Result> mFuture;
-
THREAD_POOL_EXECUTOR
也是一個線程池,負責任務的執行.
// 定義
public static final Executor THREAD_POOL_EXECUTOR;
// 靜態加載線程池.
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
-
sHandler
負責線程環境的切換.
// 7.0 代碼
// 負責線程切換
private static InternalHandler sHandler;
// 獲取Handler
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
// 該類負責實現線程的切換
private static class InternalHandler extends Handler {
// 7.0
public InternalHandler() {
super(Looper.getMainLooper());
}
@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;
}
}
}
// 任務結束函數
private void finish(Result result) {
if (isCancelled()) {
// 任務被取消
onCancelled(result);
} else {
// 任務正常完成
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
Android 5.0 代碼
// 靜態加載.
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 是一個靜態的Handler對象,Android 6.0 以前,為了能夠將執行環境切換到主線程,這就要求sHandler這個對象必須在主線程中進行創建.
- Android 6.0 以后 sHandler 使用了懶加載,因此,在子線程中也可以進行加載AsyncTask.同時在ActivityThread的main() 方法中也去掉了
AsyncTask.init();
也證實了這一點.
5 總結
- AsyncTask 本質上就是封裝了線程池和Handler
- Android 6.0 以后可以在子線程中加載AsyncTask.
HandlerThread
HandlerThread 繼承了Thread ,添加加了Looper, 實現方式如下:
- 通過Looper.prepare() 來創建消息隊列.
- 通過Looper.loop() 來開啟消息循環.
@Override
public void run() {
mTid = Process.myTid();
// 創建消息隊列.
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
// 子類實現.
onLooperPrepared();
// 開啟運行循環.
Looper.loop();
mTid = -1;
}
- 普通的Thread主要用于進行耗時操作.HandlerThread內部常見了消息隊列,外界需要通過Handler的消息方式通知HandlerThread來執行一個任務.由于HandlerThread的run() 是一個無限循環(Looper.loop()),因此當明確不需要時需要主動調用,
quit()
或者quitSafely()
來終止線程的執行.
關于HandlerThread的使用方式參考IntentService源碼
IntentService
1. 簡介
- IntentService是一種特殊的服務, 他繼承自Service并且他是一個抽象類.
- IntentService用于后臺耗時任務, 任務執行結束后自動停止.
- 由于它是一個服務因此有著比普通Thread更高的優先級,不容易被系統殺死.因此他適合執行一些優先級較高的后臺任務.
2. 工作原理
- IntentService 其實就是封裝了 Handler 和 HandlerThread從下面的源碼分析中可以看出來.
// 1. Handler
private volatile ServiceHandler mServiceHandler;
// 定義Handler
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
// onHandleIntent 這個方法是子類來實現的.
onHandleIntent((Intent)msg.obj);
// 停止當前服務.
stopSelf(msg.arg1);
}
}
// IntentService 的onCreate方法
@Override
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.
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
// 保存Looper
mServiceLooper = thread.getLooper();
// 創建Handler , 并且將創建的Handler 和 HandlerThread 的Looper綁定在一起.
mServiceHandler = new ServiceHandler(mServiceLooper);
}
- mServiceHandler就是一個Handler , 并且他的
handleMessage
處理邏輯也十分簡單,首先調用onHandleIntent
將結果返給子類處理. 然后調用stopSelf() 來停止服務. - 在onCreate() 方法中創建了一個HandlerThread 并且將Handler和他綁定.
IntentService工作原理如下:
- 在onCreate() 中創建Handler和HandlerThread.并綁定.
- 在
onStart()
中通過Handler 發送消息. - 在 Handler中處理調用onHandleIntent()進行耗時操作.
- 數據通過Intent進行傳輸.
- 任務執行結束后,后停止服務.
- 在
onDestory()
中退出HandlerThread.
3. 使用
// 定義
public class LocalIntentService extends IntentService{
/**
* 構造方法
*/
public LocalIntentService() {
super("123");
}
@Override
protected void onHandleIntent(Intent intent) {
String action = intent.getStringExtra("task_action");
Log.d(TAG, "onHandleIntent: " + action);
// 延時
SystemClock.sleep(500);
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy: 服務結束");
super.onDestroy();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand: ");
return super.onStartCommand(intent, flags, startId);
}
}
Intent intent = new Intent(this, LocalIntentService.class);
intent.putExtra("task_action", "任務 1");
startService(intent);
// 任務二
intent.putExtra("task_action", "任務 2");
startService(intent);
SystemClock.sleep(2000);
// 任務三
intent.putExtra("task_action", "任務 3");
startService(intent);
運行代碼發現上述三個任務串行執行,最后會停止服務.
Android中的線程池
Android線程池的概念源自于Java的Executor, Executor 是一個接口,ThreadPoolExecutor 是他的實現類.ThreadPoolExecutor提過了一系列的參數來配置線程池.
Android中的線程池主要分為四類.后面會詳細介紹.
1. 線程池的好處
- 重用線程池中的線程, 避免線程的創建和銷毀帶來的性能開銷.
- 能有效的控制線程池的最大并發數, 避免大量線程之間因為搶奪子系統資源而導致的阻塞現象.
- 能夠對象成進行簡單的管理, 并提供定時執行以及定時間隔循環執行的功能.
2. ThreadPoolExecutor
- 構造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory);
- corePoolSize : 線程池的核心線程數,默認情況下核心線程會在線程池中一直存活.如果ThreadPoolExecutor 的 allowCoreThreadTimeOut 屬性設置為true, 那么閑置的核心線程會有超時策略,超時時間由keepAliveTime指定.
- maximumPoolSize : 線程池最大的線程數.達到最大值后新任務會阻塞.
- keepAliveTime : 超時時間.
- unit : 超時單位,MILLISECONDS(毫秒),SECONDS(秒), MINUTES(分鐘)
- workQueue: 線程池中的任務隊列,通過線程池的execute方法提交的Runnable對象會存儲在這里.
- threadFactory : 線程工廠, 為線程池提供創建新線程的功能.
除了上面的主要參數以外, ThreadPoolExecuteor還有一個不常用的參數RejectedExecutionHandler handler. 當線程池無法執行新的任務時會調用handler 的rejectedExeception方法來通知調用者, 默認情況下 rejectedExecution 方法會拋出一個rejectedExecutionException異常,具體的可以查看API文檔.
-
ThreadPoolExecutor 執行時遵循以下規則:
- 如果線程池中的線程為達到核心線程的數量, 那么會直接啟動一個核心線程來執行任務.
- 如果線程池中的線程數量已經達到或者超過核心線程數量, 那么任務會插入任務隊列中進行排隊.
- 如果過步驟2中無法將任務插入到任務隊列,一般是隊列已滿,這個時候如果線程數量沒有達到線程池的最大數量限制,那么會立即啟動一個非核心線程來執行任務.
- 如果步驟3中線程數量已經到了線程池規定的最大值, 那么就拒絕執行此任務,ThreadPoolExecutor會調用RejectedExecutionHandler的rejectedExecution() 方法來通知調用者.
AsyncTask中的線程池配置
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
3. Android中的四種線程池.
Android中常用有四種線程池,他們都是直接或者間接配置ThreadPoolExecutor 來實現自己的功能的.
3.1 FixedThreadPool
- 通過Executors 的newFixedThreadPool 方法來創建.
- 線程數量固定.
- 線程處于空閑狀態時, 線程也不會被回收,除非線程池關閉了.正是這個原因所以它能夠更加快速第響應外界請求.
- 當所有的線程都處于活動狀態時, 新任務處于等待狀態, 直到有線程空閑.
- 沒有超時機制
- 沒有任務熟練限制.
newFixedThreadPool方法源碼
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
3.2 CachedThreadPool
- 通過Executors的newCachedThreadPool 來創建, 它是一種線程數量不定的線程池,他只有非核心線程并且最大線程數為integer.MAX_VALUE.
- 當線程池中的活動都處于活動狀態時,會直接創建新的線程來處理任務.否則就利用空閑線程來處理任務.
- 超時時間是 60s ,線程閑置60s就會被回收.
- 這種線程適合大量的耗時較小的任務.
newCachedThreadPool源碼
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
3.3 ScheduledThreadPool
- 通過Executors的newScheduledThreadPool方法來創建.
- 核心線程數是固定的,非核心線程數沒有限制.當非核心線程一旦閑置就會被立即回收.
- ScheduledThreadPool主要用來執行定時任務和具有固定周期的重復任務.
newScheduledThreadPool源碼
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
3.4 SingleThreadPool
- 通過Executors的newSingleThreadExecutor方法創建.
- 只有一個核心線程,他可以確保所有的任務都在同一個線程中順序執行.
- 他可以統一所有的外界任務到一個線程中,這樣可以避免線程同步問題.
newSingleThreadExecutor源碼
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
4. 四種常用線程池的用法
private void threadPoolTest() {
// 創建任務
Runnable command = new Runnable() {
@Override
public void run() {
Log.d(TAG, "run: " + Thread.currentThread().getId());
SystemClock.sleep(2000);
}
};
// FixedThreadPool
ExecutorService fixed = Executors.newFixedThreadPool(4);
fixed.execute(command);
// Cached
ExecutorService cached = Executors.newCachedThreadPool();
cached.execute(command);
// Scheduled
ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(4);
// 2000 ms 后執行
scheduled.schedule(command, 2000, TimeUnit.MILLISECONDS);
// 延時 10 后每隔 1000ms 執行一次
scheduled.scheduleAtFixedRate(command, 10, 1000, TimeUnit.MICROSECONDS);
// Single
ExecutorService single = Executors.newSingleThreadExecutor();
single.execute(command);
}