線程分爲主線程和子線程,主線程主要處理和界面相關的事情,而子線程則用於執行耗時操作。
android找那個線程有很多種實現方式:AsyncTask、IntentService、HandlerThread。
AsyncTask封裝了線程池和Handler,主要爲了方便開發者在子線程中更新UI
HandlerThread是具有消息循環的線程,內部可以使用Handler
IntentService是一個服務,內部採用HandlerThread執行任務,任務執行完畢後會自動退出
一、主線程和子線程
Android沿用了java的線程模型,分爲主線程和子線程,其中主線程也叫UI線程。主線程的作用是運行四大組件以及處理他們和用戶的交互,而子線程則執行耗時任務,比如網絡請求。I/O操作等。從android3.0開始系統要求網絡訪問必須在子線程中進行,否則會拋出NetworkOnMainThreadException異常,這樣做是爲了避免主線程由於被耗時操作阻塞而出現的ANR。
二、Android中的線程形態
1、AsyncTask:
AsyncTask是一種輕量級的異步任務類,可以在線程池中執行後臺任務,然後把執行的進度和結果傳遞給主線程並在主線程中更新UI。AsyncTask封裝了Thread和Handler,通過AsyncTask可以方便的執行後臺任務以及在主線程中訪問UI,但是AsyncTask不適合進行特別耗時的後臺任務,特別耗時的任務建議使用線程池。
AsyncTask是一個抽象的泛型類,提供了Params、Progress和Result三個泛型參數,Params表示參數類型,Progress表示後臺任務的執行進度的類型,Result表示後臺任務的返回結果的類型
2、AsyncTask提供4個核心方法:
(1)onPreExecute():在主線程執行,在異步任務執行之前會被調用,可以做一些準備工作。
(2)doInBackground(Params...params):在線程池中執行,用於執行異步任務,params表示異步任務的輸入參數。可以通過publishProgress方法更新任務的進度,publishProgress方法會調用onProgressUpdate方法,此方法需要返回計算結果給onPostExecute方法
(3)onProgressUpdate(Progress...values):在主線程執行,當後臺任務的執行進度發生改變時調用此方法
(4)onPostExecute(Result result):在主線程中執行,在異步任務執行之後會被調用,result參數是後臺任務的返回值,即doInBackground的返回值。
代碼實例:
private class DownloadFilesTask extends AsyncTask<URL,Integer,Long>{
protected Long doInBackground(URL...urls){
int count = urls.length;
long totalSize = 0;
for(int i=0;i<count;i++){
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int)((i / (float) count) *100));
if(isCancelled()){
break;
}
return totalSize;
}
}
protected void onProgressUpdate(Integer...progress){
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result){
showDialog("Downloaded"+result+"bytes");
}
}
3、AsyncTask在使用過程中有一些條件限制,主要有以下幾點:
(1)AsyncTask的類必須在主線程中加載。
(2)AsyncTask的對象必須在主線程中創建
(3)execute方法必須在UI線程調用
(4)不要在程序中直接調用onPreExecute、onPostExecute、doInBackground和onProgressUpdate方法
(5)一個AsyncTask對象只能執行一次,即只能調用一次execute方法,否則會報運行時異常
4、AsyncTask的工作原理
從execute開始分析,execute會調用executeOnExecutor方法,代碼如下:
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");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWroker.mParams = params;
exec.execute(mFuture);
return this;
}
sDefaultExecutor實際上是一個串行的線程池,一個進程中所有的AsyncTask全部在這個串行的線程池中排隊執行。在executeOnExcutor方法中,AsyncTask的onPreExecute方法最先執行,然後線程池開始執行。下面看線程池的執行過程。
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){
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);
}
}
}
AsyncTask中有兩個線程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一個Handler(InternalHandler),其中線程池SerialExecutor用於任務的排隊,而線程池THREAD_POOL_EXECUTOR用於真正的執行任務,InternalHandler用於將執行環境從線程池切換到主線程。
5、HandlerThread
HandlerThread繼承Thread,它是一種可以使用Handler的Thread,他的實現也很簡單,就是在run方法中通過Looper.prepare()來創建消息隊列,並通過Looper.loop()來開啓消息循環,這樣就運行在HandlerThread中創建Handler,HandlerThread的run方法代碼如下:
public void run(){
mTid = Process.myTid();
Looper.prepare();
synchronized(this){
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
6、IntentService
IntentService是一種特殊的Service,繼承了Service並且他是一個抽象類,因此必須創建他的子類才能使用。IntentService用於執行後臺耗時任務,當任務執行完成會自動停止,同時它的優先級比線程高很多。IntentService封裝了HandlerThread和Handler,可以從它的onCreate中看出,代碼如下:
public void onCreate(){
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService["+mName+"]");
thread.start();
mServiceLooper = thread.getLopper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
當IntentService第一次啓動時,oncreate會被調用並創建一個HandlerThread,然後使用它的Looper構造一個Handler對象mServiceHandler,這樣通過mServiceHandler發送的消息最終都會在HandlerThread中執行。從這個角度看,IntentService也可以執行後臺任務。每次啓動IntentService,他的onStartCommand方法就會調用一次,IntentService在onStartCommand中處理每個後臺任務的Intent。下面看一下onStartCommand如何處理外界Intent。代碼如下:
public void onStart(Intent intent,int startId){
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obe = intent;
mServiceHandler.sendMessage(msg);
}
下面來看一下IntentService的工作方式,示例代碼:
public class LocalIntentService extends IntentService{
private static final String TAG = "LocalIntentService";
public LocalIntentService(){
super(TAG);
}
@Override
protected void onHandleIntent(Intent intent){
String action = intent.getStringExtra("task_action");
SystemClock.sleep(3000);
if("com.ryg.action.TASK1".equals(action)){
Log.d(TAG,"handle task:"+action);
}
}
@Override
public void onDestroy(){
super.onDestroy();
}
}
三、Android中的線程池
1、線程池的優點:
(1)重用線程池中的線程,避免因爲線程的創建和銷燬所帶來的性能開銷
(2)能有效控制線程池的最大併發數,避免大量的線程之間因互相搶佔系統資源而導致的阻塞現象。
(3)能夠對線程進行簡單的管理,並提供定時執行以及制定間隔循環執行等功能。
Android中的線程池來源於java中的Executor,Executor是一個接口,真正的線程池的實現爲ThreadPoolExecutor。ThreadPoolExecutor提供了一系列參數來配置線程池,通過不同的參數可以創建不同的線程池。
2、ThreadPoolExecutor
(1)ThreadPoolExecutor是線程池的真正實現,他的構造方法提供了一系列參數來配置線程池,下面是ThreadPoolExecutor的一個常用的構造方法
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory)
corePoolSize:線程池的核心線程數,默認情況下,核心線程會在線程池中一直存活,即是處於閒置狀態
maximumPoolSize:線程池所能容納的最大線程數,當活動線程數達到這個數值後,後續的新任務會被阻塞
keepAliveTime:非核心線程閒置時的超時時間,超過這個時長,非核心線程就會被回收。當ThreadPoolExecutor的allowThreadTimeOut屬性設置爲true時,keepAliveTime同樣會作用於核心線程。
unit:用於指定keepAilveTime參數的時間單位,這是一個枚舉,常用的有TimeUnit、MILLISECONDS、TimeUnit.SECONDS以及TimeUnit.MINUTES等。
workQueue:線程池中的任務隊列,通過線程池的execute方法提交的Runnable對象會存儲在這個參數中。
threadFactory:線程工廠,爲線程池提供創建新線程的功能。ThreadFactory是一個接口,只有一個方法:Thread newThread(Runnable r)
(2)ThreadPoolExecutor執行任務時大致遵循如下規則:
- 如果線程池中的線程數量未達到核心線程的數量,那麼會直接啓動一個核心線程來執行任務。
- 如果線程池中的線程數量已經達到或者超過核心線程的數量,那麼任務會被插入到任務隊列中排隊等待執行。
- 如果無法將任務插入到任務隊列中,這往往由於任務隊列已滿,這時候如果線程數量未達到線程池規定的最大值,會立刻啓動一個非核心線程來執行任務。
- 如果線程數量已經達到最大值,那麼就拒絕執行此任務,ThreadPoolExecutor會調用RejectedExecutionHandler的rejectedExecution方法來通知調用者
(3)ThreadPoolExecutor的參數配置在AsyncTask中有明顯的體現,代碼如下
private static final int CUP_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);
AsyncTask對THREAD_POOL_EXECUTOR這個線程池進行了配置:
核心線程數等於CPU核心數+1
線程池的最大線程數爲CPU核心數的2倍+1
核心線程無超時機制,非核心線程在閒置時的超時時間爲1秒
任務隊列的容量爲128
四:線程池的分類
Android中最常見的四種具有不同功能特性的線程池,他們都直接或間接通過配置ThreadPoolExecutor來實現自己的功能特性,四種線程池分別是:FixedThreadPool、CachedThreadPool、ScheduledThreadPool以及SingleThreadExecutor。
1、FixedThreadPool:
Executors的newFixedThreadPool方法來創建。他是一種線程數量固定的線程池,當線程處於空閒狀態時,他並不會被回收,除非線程池被關閉了。示例代碼
public static ExecutorService newFixedThreadPool(int nThreads){
return new ThreadPoolExecutor(nThreads,nThreads,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}
2、CachedThreadPool:
Executors的newCachedThreadPool方法來創建。他是一種線程數量不定的線程池,他只有非核心線程,並且最大線程數爲Integer.MAX_VALUE。由於Integer.MAX_VALUE是一個很大的數,實際上相當於最大線程數.
public static ExecutorService newCachedThreadPool(int nThreads){
return new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.MILLISECONDS,new SynchronousQueue<Runnable>());
}
3、ScheduledThreadPool:
通過Executors的newScheduledThreadPool來創建。他的核心線程數量是固定的,而非核心線程數是沒有限制的,並且當非核心線程閒置時會被立即回收。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize){
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize){
super(corePoolSize,Integer.MAX_VALUE,0,NANOSECONDS,new DelayedWorkQueue());
}
4、SingleThreadExecutor:
通過Executors的newSingleThreadExecutor方法來創建。這類線程池內部只有一個核心線程,他確保所有任務都在同一個線程中按順序執行。SingleThreadExecutor的意義在於統一所有的外界任務到一個線程中,這使得在這些任務之間不需要處理線程同步的問題。代碼如下:
public static ExecutorService newSingleThreadExecutor(){
return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
除了上面系統提供的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);
scheduledThreadPool.schedule(command,2000,TimeUnit.MILLISECONDS);
scheduledThreadPool.scheduleAtFixedRate(command,10,1000,TimeUnit.MILLISECONDS);
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
singleThreadExecutor.execute(command);