Android線程概述
線程分為主線程和子線程,主線程主要處理和界面相關的事情,子線程則往往用于處理耗時操作。
線程是操作系統最小的調度單元,同時線程又是一種受限的系統資源,即線程不可能無限制的產生,并且線程的創建和銷毀都會有響應的開銷。線程池會緩存一定數量的線程,通過線程池可以避免因為頻繁創建和銷毀線程所帶來的系統開銷。android中的線程池來源于Java,主要是通過Executor來派生特定類型的線程池。
主線程和子線程
主線程主要處理界面交互相關的邏輯,因為用戶隨時會和界面發生交互,因此主線程在任何時候都必須要有較高的響應速度,否則就會產生一種界面卡頓的感覺。
子線程,也叫工作線程,除了主線程以外的線程都叫子線程。
Android中的主線程也叫UI線程,作用是運行四大組件,以及處理它們和用戶的交互。子線程執行耗時任務,比如網絡請求,I/O操作等。
四種線程形態
Thread
最基本的線程使用方法,從Java繼承而來。使用方法可以直接從Thread生成變量,或者使用Runnable初始化Thread。不再贅述。
AsyncTask
封裝了線程池,和Handler,主要為了方便開發者在子線程中更新UI。
可以在線程池中執行后臺任務,然后把執行的進度和最終的結果傳遞給主線程中更新UI。從實現上來說,AsyncTask封裝了Thread和Handler,可以很方便的執行后臺任務并更新UI。但是AsyncTask不適合進行特別耗時的任務,耗時任務建議使用線程池做。
四個核心方法
- onPreExecute,在主線程中執行,異步任務執行之前,此方法被調用,一般可以用于做一些準備工作
- doInBackground,在線程池中執行,用于執行異步任務,可以調用publishProgres更新任務的進度,publishProgress會調用onProgressUpdate方法。
- onProgressUpdate,主線程中執行,進度發生變化。
- onPostExecute,主線程中執行,異步任務執行完成之后,此方法會被調用。
一些限制
- AsyncTask的必須在主線程中加載。
- AsyncTask的對象必須在主線程中創建。
- execute方法必須在UI線程中調用。
- 不能直接調用onPreExecute,onPostExecute,doInBackground,onProgressUpdate。
- 一個AsyncTask對象只能執行一次,即只能調用一次execute方法。
- android 1.6之前AsyncTask串行執行,android 1.6到android 3.0之前為并行執行,android 3.0包括(3.0)串行執行。
工作原理
execute直接調用了executeOnExecutor,兩個方法源代碼如下:
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
//直接調用了executeOnExecutor方法。
}
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
//此處保證一個AsyncTask只執行一次
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException(".....");
case FINISHED:
throw new IllegalStateException("......");
}
}
mStatus = Status.RUNNING;
//調用onPreExecute,此時仍然在主線程上
onPreExecute();
mWorker.mParams = params;
//在線程池上執行
exec.execute(mFuture);
return this;
}
sDefaultExecutor是一個串行的線程池,sDefaultExecutor代碼如下:
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
private static class SerialExecutor implements Executor {
//保存需要執行的線程的隊列
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
//當前正在執行的線程
Runnable mActive;
public synchronized void execute(final Runnable r) {
//offer,想隊列的尾部插入一個數據。
//不能直接將參數的Runnable加入到隊列中,因為在參數的run方法執行完之后執行scheduleNext方法,所以新建了一個Runnable對象,在新建的Runnable對象的run方法中直接執行了參數的run方法。
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
//獲取頭部的對象,并賦值給mActivie,如果不是null,則在線程池THREAD_POOL_EXECUTOR上執行。
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
從上面的代碼中可以看到AsyncTask是現行執行的。
AsyncTask有兩個線程池,分別為sDefaultExecutor和THREAD_POOL_EXECUTOR,其中THREAD_POOL_EXECUTOR用來真正的執行AsyncTask,sDefaultExecutor用來做排隊,保證AsyncTask順序執行。
如何保證能夠更新UI
AsyncTask有一個InternalHandler,其代碼如下:
private static class InternalHandler extends Handler {
//構造函數,使用的Looper時主線程Looper,保證其處理消息是在主線程上。
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;
}
}
}
使用方法
- 生成AsncTask的對象,需要重寫doInBackground,和onPostExecute方法。其中doInBackground方法是子線程需要執行的操作,onPostExecute表示子線程處理之后主線程需要執行的操作。如果需要可以重寫onProgressUpdate方法,在主線程顯示進度的變化。
- 執行AsyncTask的execute方法。
HandlerThread
是一種具有了消息循環的線程,在它的內部可以使用Handler。實現非常簡單,就是在run方法中通過Looper.prepare來創建消息隊列,通過loop方法開啟消息循環,這樣在實際的使用中就可以在HandlerThread中創建Handler了。其run方法如下:
@Override
public void run() {
mTid = Process.myTid();
//創建消息隊列
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
//開啟消息循環
Looper.loop();
mTid = -1;
}
HandlerThread有getLooper方法獲取該線程對應的Looper,將次Looper傳遞給Handler就可以子線程中執行一些操作,比如主線程需要子線程執行網絡請求,則可以通過Handler post一個Runnable即可,此Runnable會請求網絡數據,數據請求完成之后將數據保存起來,或者將數據更新到UI(需要進行線程切換,比如使用主線程的handler來更新界面)。使用方法示例代碼:
public class Activity1 extends Activity implement OnClickListener{
//HandlerThread使用的Handler
private Handler mHandler;
private HandlerThread mHandlerThread;
private Handler uiHandler = new Handler(){
@override
public void handleMessage(Message msg) {
//處理消息
//更新界面
}
};
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(this);
mHandlerThread = new HandlerThread("Test");
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
}
private Runnable mRunnable = new Runnable() {
@Override
public void run() {
Log.d("MainActivity", "test HandlerThread...");
try {
//從網絡請求數據
getDataFromNetWork();
//使用uiHandler執行一個post請求
uiHandler.obtainMessage(1,"finished").sendToTarget();
} catch (Exception e) {
e.printStackTrace();
}
}
};
@Override
public void onClick(View v) {
switch(v.getId()) {
case R.id.btn :
mHandler.post(mRunnable);
break;
default :
break;
}
}
@Override
protected void onDestroy() {
mRunning = false;
//移除回調
mHandler.removeCallbacks(mRunnable);
super.onDestroy();
}
}
使用步驟:
- 聲明Handler,HandlerThread對象。
- 生命Runnable對象
- 初始化HandlerThread對象, mHandlerThread = new HandlerThread("Test");
- HandlerThread啟動,mHandlerThread.start();
- 初始化Handler對象,mHandler = new Handler(mHandlerThread.getLooper());
- 執行Runnable,mHandler.post(mRunnable);
- 在Runnable對象的run方法執行完畢后可以調用主線程的Handler,進行界面更新。
IntentService
是一個服務(Service),系統對其進行了封裝使其可以更方便地執行后臺任務,IntentService內部采用了HandlerThread來執行任務,當任務執行完畢之后IntentService會自動退出。作為一個服務,IntentService不容易被系統殺死從而可以盡量保證任務的執行。
IntentService是Service的子類,但它是一個抽象類,因此需要創建它的子類才能使用。其封裝了HandlerThread和Handler。從其代碼中可以看出其使用了HandlerThread。
@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 thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
Intent的處理
看其onStartCommand方法,onStartCommand掉哦那個onStart方法,而onStart的代碼為:
@Override
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
//直接調用了mServiceHandler發送消息,保證了消息在子線程中被調用。
mServiceHandler.sendMessage(msg);
}
ServiceHanlder對消息的處理如下:
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的onHandleIntent方法為一個抽象方法,需要子類重寫。
protected abstract void onHandleIntent(Intent intent);
由于Looper是順序執行任務的,因此IntentService也是順序執行任務的。
線程池
線程池的優點
- 重用線程池中的線程,避免因為線程的創建和銷毀帶來的性能開銷。
- 能有效的控制線程池的最大并發數,避免大量的線程之間因相互搶占系統資源而導致的阻塞現象。
- 能夠對線程驚醒簡單的管理,并提供定時執行以及指定間隔循環執行等功能。
Android中得線程池來源于Java中的Executor,Executor是一個接口,真正的線程池的實現為ThreadPoolExecutor. ThreadPoolExecutor提供了一些列參數來配置線程池,根據不同的參數可以創建不同的線程池,從線程池的功能特性來說,Android的線程池主要有四類,通過Executors所提供的方法來得到。
ThreadPoolExecutor
ThreadPoolExecutor是線程池的真正實現,其構造方法提供了一系列參數來配置線程池。構造方法如下所示:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
- corePoolSize,核心線程數,默認情況下核心線程會一直存活,即使它們處于閑置狀態。如果線程池的allowCoreThreadTimeOut屬性為true,則閑置的核心線程在等待新任務到來時會有超時策略,時間間隔有keepAliveTime決定。
- maximumPoolSize,線程池所能容納的最大線程數,當活動線程數達到這個數值后,后續的新任務將會被阻塞。
- keepAliveTime,非核心線程閑置時的超時時長,超過這個時長,非核心線程就會被回收。allowCoreThreadTimeOut屬性為true,keepAliveTime同樣作用于核心線程。
- unit,keepAliveTime的單位,使用TimeUnit的值。
- workQueue,任務隊列,通過線程池的execute方法提交的Runnable對象會存儲在這個參數中。
- threadFactory,線程工廠,為線程池創建一個新的線程。ThreadFactory是一個接口,只有一個Thread newThread(Runnable r)方法。
除了上面的參數外還有一個參數,RejectedExecutionHandler,當線程池無法執行任務時會調用handler的rejectedExecution方法通知調用者。
規則
- 如果線程池的線程數量未達到核心線程的數量,就會直接啟動一個核心線程。
- 如果線程池中的線程數量已經達到或者超過核心線程的數量,那么任務會被插入到任務隊列中排隊等待執行。
- 如果無法將任務插入到任務隊列中,這往往是由于任務隊列已經滿了,這個時候如果線程數量未達到線程池規定的最大值,那么會立刻啟動一個非核心線程執行任務。
- 如果上面的線程數量已經達到線程池規定的最大值,那么就拒絕執行此任務會調用RejectedExecutionHandler 的rejectedExecution方法。
AsyncTask的線程池
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
/**
* An {@link Executor} that executes tasks one at a time in serial
* order. This serialization is global to a particular process.
*/
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
從上面的代碼可以知道AsyncTask對THREAD_POOL_EXECUTOR這個線程池進行了配置,最后的規則為:
- 核心線程數為CPU核心數+1
- 線程池的最大線程數為CPU核心數的2倍 + 1
- 核心線程無超市機制,非核心線程在閑置時的超時時間為1秒
- 任務隊列的容量為128
線程池分類
- FixedThreadPool,通過Executors的newFiexedThreadPool方法創建,是線程數量固定的線程池。只有核心線程且核心線程不會被回收,能更快的響應外界的請求。
- CachedThreadPool,通過Executors的newCachedThreadPool方法創建,只有非核心線程,且最大線程數為Integer.MAX_VALUE。
- ScheduleedThreadPool,通過Executors的newScheduleedThreadPool方法創建,核心線程個數固定,非核心線程個數無限制,用于執行定時任務和具有固定周期的重復任務。
- SingleThreadPool,通過Executors的newSingleThreadPool方法創建,只有一個核心線程,確保所有的任務都在同一個線程中順序執行。