android進階(十一)-----Android線程和線程池

線程分爲主線程和子線程,主線程主要處理和界面相關的事情,而子線程則用於執行耗時操作。

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);

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章