前言:java
在移動端開發中,咱們必須正確處理好主線程和子線程之間的關係,耗時操做必須在子線程中完成,避免阻塞主線程,致使ANR。異步處理技術是提升引用性能,解決主線程和子線程之間通訊問題的關鍵。android
一般在以下兩種狀況下會彈出ANR對話框:數據庫
形成以上兩種狀況的首要緣由就是在主線程(UI線程)裏面作了太多的阻塞耗時操做, 例如文件讀寫, 數據庫讀寫, 網絡查詢等等,避免ANR,其實就是不能在主線程中進行耗時操做,這就須要用到異步處理技術了。安全
異步處理技術有不少種,常見的有Thread、AsyncTask、Handler&Looper、Executor等。一個完整的異步處理技術的繼承樹以下所示:cookie
下面咱們就根據繼承樹的結構並結合具體使用和源碼一一展開介紹。網絡
1、Thread併發
線程做爲Java語言的一個概念,是實際執行任務的基本單元,Thread是異步處理技術的基礎。它的使用至關簡單,簡單的講,建立線程的方法有兩種:app
1.繼承Thread類並重寫run方法框架
語句以下異步
public class Mythread extends Thread { @Override public void run() { //實現具體的邏輯,如文件讀寫,網絡請求等 } public void startThread(){ Mythread mythread = new Mythread(); mythread.start();//使用start啓動線程 } }
2.實現Runnable接口並實現run方法
public class MyRunnable implements Runnable { @Override public void run() { //實現具體的邏輯,如文件讀寫,網絡請求等 } public void startThread(){ MyRunnable runnable = new MyRunnable(); Thread thread = new Thread(runnable); thread.start();//一樣利用start啓動線程 } }
這裏須要注意的一點是,若是咱們沒有利用start方法去啓動線程,而是直接調用run方法,那這將不會是一個異步操做,而只是一個簡單的方法調用,其操做仍是在主線程上進行的。
3.Android中的線程分類
此外,咱們再擴展一下Android應用中線程的概念。Android引用中的各種線程其本質都是基於Linux系統的pthreads,在應用層能夠分爲三種類型的線程。
1.主線程:主線程也被稱爲UI線程,它隨着應用的啓動而啓動,主線程用來運行Android組件,同時刷新屏幕上的UI元素。Android系統若是檢測到非主線程在更新UI組件,則會拋出CallFromWrongThreadException異常。爲何只有主線程才能操做UI,這是由於Android的UI工具包並非線程安全的。主線程中建立的Handler會順序執行接收到的消息,包括從其餘線程中發送的消息。所以,若是消息隊列中前面的消息沒有很快執行完,那麼就會阻塞隊列中的 其餘消息的及時處理。
2.Binder線程:Binder線程用於不一樣進程之間的線程通訊(其原理在前面文章中有提到),每一個進程都維護了一個Binder線程池,用於處理其餘進程中線程發送的消息。這些進程包括系統服務、Intents、ContentProvider和Service等。在大部分狀況下,應用不須要關心Binder線程,由於系統會優先將請求轉換爲使用主線程。一個典型的應用場景就是應用提供給其餘進程經過AIDL接口綁定的Service。
3.後臺線程,在應用中顯示建立的線程都是後臺線程,也就是當剛建立處理的時候,折現線程的執行體是空的,須要手動添加任務。
爲何有主線程和後臺線程的區別,在Linux層面上,主線程和後臺線程是同樣的,在Android框架中經過WindowManager賦予了主線程只能處理UI更新、後臺線程不能直接操做UI的限制。
2、HandlerThread
HandlerThread是一個集成了Looper和MessageQueue的線程,當啓動HandlerThread的同時,會生成Looper和MessageQueue,而後等待消息進行處理。
先看一下這個類的結構
既然是集成自Thread類,咱們先來看一下它的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內部集成了Looper,經過Looper.prepare()新建了一個Looper。
具體最後會調用到如下方法:
private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
其中 sThreadLocal是ThreadLocal<Looper>類型的變量。
使用HandlerThread的好處在於開發者不須要本身去建立並維護Looper,它的用法和普通線程是一致的
HandlerThread handlerThread = new HandlerThread("myHandlerThread"); handlerThread.start(); Handler myHandler = new Handler(handlerThread.getLooper()){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); //處理接收到的消息 } };
HandlerThread內部只有一個消息隊列,隊列中的消息是順序執行的,於是是線程安全的,固然其吞吐量會有必定影響,隊列中的任務可能會被前面沒有執行完的任務阻塞。
這個時候有人可能會有這樣的疑問,那有沒有可能出現發送消息的時候Looper尚未準備好呀。這一點咱們經過源碼,看到其內部的機制就能確保建立Looper和發送消息之間不存在競態條件。
/** * This method returns the Looper associated with this thread. If this thread not been started * or for any reason is isAlive() returns false, this method will return null. If this thread * has been started, this method will block until the looper has been initialized. * @return The looper. */ public Looper getLooper() { if (!isAlive()) { return null; } // If the thread has been started, wait until the looper has been created. synchronized (this) { while (isAlive() && mLooper == null) { try { wait(); } catch (InterruptedException e) { } } } return mLooper; }
當線程已經啓動,Looper尚未準備好的時候,線程會進行wait狀態。
在HandlerLooper中還有一個方法聲明,onLooperPrepared(),這個方法能夠被重寫,用於在HandlerThread開始接受消息以前進行某些初始化操做。
import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.os.Process; /** * Created by HustZhb on 2017/8/3. * 描述: */ public class MyHandlerThread extends HandlerThread{ private Handler mHandler; public MyHandlerThread() { super("MyHandlerThread", Process.THREAD_PRIORITY_BACKGROUND); } @Override protected void onLooperPrepared() { super.onLooperPrepared(); mHandler = new Handler(getLooper()){ @Override public void handleMessage(Message msg) { switch (msg.what){ case 1: //處理message break; case 2: //處理message break; default: break; } } }; } public void publishedMehthod1(){ mHandler.sendEmptyMessage(1); } public void publishMehthod2(){ mHandler.sendEmptyMessage(2); } }
上述例子在onLooperPrepared函數中建立了與HandlerThread關聯的Handler實例,一樣對外隱藏了咱們的Handler實例。
3、AsyncQueryHandler
而在HandlerThread的基礎上,AsyncQueryHandler是用於在ContentProvider上執行異步的CRUD操做的工具類,其中CRUD操做被放到一個單獨的子線程中進行,當操做獲取到結構後經過message的形式傳遞給調用AsyncQueryHandler的線程,一般就是主線程。
AsyncQueryHandler是一個抽象類,它繼承自Handler,經過封裝ContentResolver、HandlerThread和Handler等實現對ContentProvider的異步操做。
經過這個類結構,咱們陷入就能夠發現其中彷佛封裝了以下四個方法來操做ContentProvider,一樣又封裝了對應的四個回調函數。具體以下所示:
四個CRUD方法
//刪除操做 public final void startDelete(int token, Object cookie, Uri uri, String selection, String[] selectionArgs) { // Use the token as what so cancelOperations works properly Message msg = mWorkerThreadHandler.obtainMessage(token); msg.arg1 = EVENT_ARG_DELETE; WorkerArgs args = new WorkerArgs(); args.handler = this; args.uri = uri; args.cookie = cookie; args.selection = selection; args.selectionArgs = selectionArgs; msg.obj = args; mWorkerThreadHandler.sendMessage(msg); } //插入操做 public final void startInsert(int token, Object cookie, Uri uri, ContentValues initialValues) { // Use the token as what so cancelOperations works properly Message msg = mWorkerThreadHandler.obtainMessage(token); msg.arg1 = EVENT_ARG_INSERT; WorkerArgs args = new WorkerArgs(); args.handler = this; args.uri = uri; args.cookie = cookie; args.values = initialValues; msg.obj = args; mWorkerThreadHandler.sendMessage(msg); } //查找操做 public void startQuery(int token, Object cookie, Uri uri, String[] projection, String selection, String[] selectionArgs, String orderBy) { // Use the token as what so cancelOperations works properly Message msg = mWorkerThreadHandler.obtainMessage(token); msg.arg1 = EVENT_ARG_QUERY; WorkerArgs args = new WorkerArgs(); args.handler = this; args.uri = uri; args.projection = projection; args.selection = selection; args.selectionArgs = selectionArgs; args.orderBy = orderBy; args.cookie = cookie; msg.obj = args; mWorkerThreadHandler.sendMessage(msg); } //更新操做 public final void startUpdate(int token, Object cookie, Uri uri, ContentValues values, String selection, String[] selectionArgs) { // Use the token as what so cancelOperations works properly Message msg = mWorkerThreadHandler.obtainMessage(token); msg.arg1 = EVENT_ARG_UPDATE; WorkerArgs args = new WorkerArgs(); args.handler = this; args.uri = uri; args.cookie = cookie; args.values = values; args.selection = selection; args.selectionArgs = selectionArgs; msg.obj = args; mWorkerThreadHandler.sendMessage(msg); }
其實現邏輯都是相似的,經過新建一個WorkerArgs來實現配置參數,並將這個配置後的實例傳遞到msg.obj,而後經過handler發送消息。最後應該是在WorkerHandler中的handleMessage中來調用ContentResolver的對應方法。
具體代碼以下:
@Override public void handleMessage(Message msg) { final ContentResolver resolver = mResolver.get(); if (resolver == null) return; WorkerArgs args = (WorkerArgs) msg.obj; int token = msg.what; int event = msg.arg1; switch (event) { case EVENT_ARG_QUERY: Cursor cursor; try { cursor = resolver.query(args.uri, args.projection, args.selection, args.selectionArgs, args.orderBy); // Calling getCount() causes the cursor window to be filled, // which will make the first access on the main thread a lot faster. if (cursor != null) { cursor.getCount(); } } catch (Exception e) { Log.w(TAG, "Exception thrown during handling EVENT_ARG_QUERY", e); cursor = null; } args.result = cursor; break; case EVENT_ARG_INSERT: args.result = resolver.insert(args.uri, args.values); break; case EVENT_ARG_UPDATE: args.result = resolver.update(args.uri, args.values, args.selection, args.selectionArgs); break; case EVENT_ARG_DELETE: args.result = resolver.delete(args.uri, args.selection, args.selectionArgs); break; } // passing the original token value back to the caller // on top of the event values in arg1. Message reply = args.handler.obtainMessage(token); reply.obj = args; reply.arg1 = msg.arg1; if (localLOGV) { Log.d(TAG, "WorkerHandler.handleMsg: msg.arg1=" + msg.arg1 + ", reply.what=" + reply.what); } reply.sendToTarget(); } }
這裏咱們能夠發如今調用完CRUD操做後,它將結果填充到了reply這個Message對象上,發回到調用方。
四個可重寫的回調方法
protected void onQueryComplete(int token, Object cookie, Cursor cursor) { // Empty } protected void onInsertComplete(int token, Object cookie, Uri uri) { // Empty } protected void onUpdateComplete(int token, Object cookie, int result) { // Empty } protected void onDeleteComplete(int token, Object cookie, int result) { // Empty }
咱們能夠根據實際的需求來實現下面的回調方法,從而獲得上面的CRUD操做的返回結果。
4、IntentService
IntentService想必你們也不陌生,相比較於Service而言,Service的各個生命週期都是運行在主線程的,所以它自己並不具有異步的能力,因此通常咱們在使用Service的時候須要在其中的onStartCommand方法中本身手動新建一個子線程,而且,在子線程的run方法中最後調用stopSelf方法結束自身來確保結束服務。
而這樣的寫法增長了出錯的可能性,忘開線程或者忘記調用stopSelf方法。雖有爲了更好的實現一個異步的、可自動中止的服務,Android專門引入了一個Service的子類 IntentService來實現這種操做。
IntentService具備和Service同樣的生命週期,同時也提供了後臺線程中處理異步任務的機制。與HandlerThread相似,IntentService也是在一個後臺線程中順序執行全部的任務。咱們能夠經過給Context.startService傳遞一個Intent類型的參數能夠啓動IntentService的異步執行,若是此時IntentService正在運行,那麼這個新的Intent將會進入到隊列中進行排隊,知道後臺線程處理完隊列前面的任務。若是此時IntentService沒有在運行,那麼將會啓動一個新的IntentService,當後臺線程隊列中全部任務處理後,IntentService將自動結束它的生命週期。
IntentService自己是一個抽象類,使用它以前須要繼承並實現其中的onHandleIntent(Intent)方法,在這個方法中實現具體的後臺處理業務邏輯,同時在子類的構造方法中須要調用弗雷的有參構造函數。傳入子類的名字。
import android.app.IntentService; import android.content.Intent; import android.support.annotation.Nullable; public class SimpleIntentService extends IntentService { /** * Creates an IntentService. Invoked by your subclass's constructor. * * @param name Used to name the worker thread, important only for debugging. */ public SimpleIntentService(String name) { super(SimpleIntentService.class.getName()); setIntentRedelivery(true); } @Override protected void onHandleIntent(@Nullable Intent intent) { //這個方法是在後臺線程中調用的 } }
當咱們須要啓動這個IntentService的時候,咱們只須要進行如下操做。
Intent intentService = new Intent(this,SimpleIntentService.class); startService(intentService);
固然,其中有一步是不可以遺忘的,IntentServie做爲Service的子類,它一樣須要在AndroidManifest文件中註冊這個類。
(這裏咱們在其構造器中寫入了一行setIntentRedelivery(true),這邊先不解釋,下面結合源碼會提到)
下面結合源碼來看一個它內部的實現。首先先放一張它的類結構圖。
下面是一個完整的源碼
public abstract class IntentService extends Service { private volatile Looper mServiceLooper; private volatile ServiceHandler mServiceHandler; private String mName; private boolean mRedelivery; 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); } } public IntentService(String name) { super(); mName = name; } public void setIntentRedelivery(boolean enabled) { mRedelivery = enabled; } @Override public void onCreate() { super.onCreate(); HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); thread.start(); mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); } @Override public void onStart(@Nullable Intent intent, int startId) { Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; msg.obj = intent; mServiceHandler.sendMessage(msg); } @Override public int onStartCommand(@Nullable Intent intent, int flags, int startId) { onStart(intent, startId); return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY; } @Override public void onDestroy() { mServiceLooper.quit(); } @Override @Nullable public IBinder onBind(Intent intent) { return null; } @WorkerThread protected abstract void onHandleIntent(@Nullable Intent intent); }
代碼的邏輯很簡答,在onCreate方法中,咱們能夠清晰地看到IntentService是經過HandlerThread來實現後臺任務的處理,
@Override public void onCreate() { super.onCreate(); HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); thread.start(); mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); }
此外,還有一點須要知道的,剛纔在繼承IntentService的例子中,咱們在其構造器中寫入了一行setIntentRedelivery(true),這一行代碼的做用是什麼呢?
從字面上看是設置Intent重現投遞爲真。其效果是IntentService的onStartCommand方法將返回 START_REDELIVER_INTENT,這時,若是onHandleIntent方法返回以前進程死亡,那麼在進程從新啓動的時候,intent會從新投遞。
@Override public int onStartCommand(@Nullable Intent intent, int flags, int startId) { onStart(intent, startId); return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY; }
5、Executor FrameWork
1.概述
下面咱們來介紹在Java併發開發中常常用到的一個框架Java Executor框架。咱們知道建立和銷燬對象是存在開銷的,若是應用中頻繁出現線程的建立和銷燬會影響到應用的性能,使用Java Executor 框架能夠經過線程池等機制解決這個問題。
1.建立工做線程池,同時經過隊列來控制這些線程執行的任務的個數
2.檢測致使線程意外終止的錯誤
3.等待線程執行完成並獲取執行結果
4.批量執行線程,並經過固定的順序獲取執行結果
5.在合適的時機啓動後臺線程,從而保證線程執行結果能夠很快反饋給用戶。
Executor框架主要由如下三部分組成:
1.任務 包括被執行任務須要實現的接口:Runnable接口或者Callable接口
2.任務的執行 包括任務執行機制的核心接口Executor,以及繼承自Executor的ExecutorService接口(Executor框架中有兩個類實現了ExecutorService接口,ThreadPoolExecutor和ScheduledThreadExecutor)
3.異步計算的結構 包括接口Future和實現Future接口的Future的FutureTask類。
2.Executor框架中的重要組成部分
1)Executor框架的基礎是一個Executor的接口,Executor的主要做用是分離任務的建立和執行,最終實現上述功能。
public interface Executor {
void execute(Runnable command); }
2)ThreadPoolExecutor是線程池的核心實現類,用來執行被提交的任務。
3)ScheduledThreadPoolExecutor是一個實現類,能夠在給定的延遲後執行命令,或者按期執行命令。ScheduledThreadPoolExecutor比Timer更靈活,功能也更增強大。
4)Future接口和實現Future接口的FutureTask類,表明異步計算的結果。
5)Runnable和Callable接口的實現類,能夠被ThreadPoolExecutor和ScheduledThreadPoolExecutor執行。
主線程首先要建立一個實現Runnable接口或者Callable接口的任務對象,工具類Executors能夠把一個Runnbale對象封裝爲Callable對象。(Executors.callable(Runnbale task )或 Executor.callable(Runnbale task, Object result)),而後將Runnbale對象直接交給ExecutorService執行(ExecutorService.execute(Runnbale command))或是將Runnbale對象或Callable對象提交給ExecutorServie執行(ExecutorService.submit(Runnable task)或ExecutorService.submit(Callable<T> task))
若是執行的是submit方法,ExecutorService將返回一個實現Future接口的對象,(FutureTask對象),因爲FutureTask實現了Runnbale,咱們也能夠本身建立FutureTask,而後直接交給ExecutorService運行。最後主線程能夠執行FutureTask.get()方法來等待任務執行完成,主線程也可使用FutureTask.cancel來取消任務的執行。
3.核心類ThreadPoolExecutor詳解
前面咱們提到了ThreadPoolExecutor是框架中核心類,下面就展開詳細的介紹。
1)構造函數
先從構造函數入手,下述源碼是ThreadPoolExecutor一個經常使用的構造函數
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler); }
上述參數解釋以下:
除了上述參數之外,看上面經常使用的構造函數內部經過this調用其餘構造器,其中還有一個參數defaultHandler,ThreadPoolExecutor還有一個參數RejectedExecutionHandler handler,當線程池沒法執行新任務的時候(多是由於任務隊列已滿或沒法成功過執行任務),。這是ThreadPoolExecutor會調用handler的rejectedExecution方法來通知調用者,默認狀況會直接拋出一個RejectedExecutionException。
2)ThreadPoolExecutor的執行規則
ThreadPoolExecutor執行任務時大體聽從下述規則:
1.若是線程池中的線程數量未達到核心線程的數量,那麼直接啓動一個核心線程
2.若是線程池中的線程數量已經達到或者超過核心線程的數量,那麼任務會被插入到任務隊列中排隊等待執行。
3.若是在步驟2中沒法將任務插入到任務隊列中,這每每是由於任務隊列已滿,這是若是先吃數量未達到線程池規定的最大值,會當即啓動一個非核心線程來執行任務。
4.若是步驟3中的線程數量已經達到線程池規定的最大值,那麼就拒絕執行此任務,ThreadPoolExecutor會調用RejectedExecutionHandler的rejectedExecution方法來通知調用者。
3)線程池的分類
ThreadPoolExecutor一般都是經過工廠類Executors來建立,主要有如下三種ThreadPoolTExecutor;
1.FixedThreadPool
2.SingleThreadPool
3.CacheThreadPool
4.ScheduledThreadPool
接下來重點介紹各個線程池。
1.FixedThreadPool 可重用固定線程數的線程池
下面是它的源碼實現
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
其corePool和maximumPool值都被設置爲建立FixedThreadPoolExecutor時指定的參數nThreads。keepAliveTime爲0,即多餘的空閒線程會被當即中止,任務隊列採用LinkedBlockingQueue
1)若是當前運行的線程數少於corePoolSize,則建立新線程來執行任務
2)噹噹前線程數等於corePoolSize時,將任務加入到LinkedBlockingQueue中
3)線程執行完1中的任務後,會在循環中反覆從LinkedBlockingQueue中獲取任務來執行。
2.SingleThreadPoolExecutor
SingleThreadPoolExecutor是使用單個工做線程的Executor,下面是它的源碼實現
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
可見其核心線程數和最大線程數都爲1,任務隊列一樣採用的是LinkedBlockingQueue。從這個角度講,當咱們把剛纔的FixedThreadPoolExecutor傳入的nThreads爲1 時,它就和SingleThreadPoolExecutor一致。
1)若是當前運行的線程數少於corePoolSize,即線程池中無運行線程,則建立新線程來執行任務
2)噹噹前線程數等於corePoolSize(1)時,將任務加入到LinkedBlockingQueue中
3)線程執行完1中的任務後,會在循環中反覆從LinkedBlockingQueue中獲取任務來執行。
3.CachedThreadPoolExecutor
CachedThreadPoolExecutor是一個根據須要建立新線程的線程池,照例貼源碼:
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
能夠發現CachedThreadPoolExecutor它沒有核心線程,而將maximumPoolSize的值設爲Interger.MAX_VALUE,即爲無界的。將keepAliveTime設置爲60L,則意味着當CachedThreadPoolExecutor的空閒線程等待新任務的最長時間是60s,不然就會被終止。
CachedThreadPoolExecutor使用的是沒有容量的SychronousQueue做爲線程池的工做隊列,但因爲其最大線程數無界,這就意味着若是主線程提交任務的速度高於線程處理任務的速度,就會不斷建立新線程,在極端狀況下CachedThreadPool會由於建立過分線程耗盡CPU和內存資源。
CachedThreadPoolExecutor的execute過程和前面兩個有點不一樣,具體以下
1)首先執行SynchronousQueue.offer,提交一個任務給工做隊列,若是當前的maximumPool中有空閒線程正在執行SynchronousQueue.poll,那麼即匹配成功,主線程將任務交給空閒線程執行,execute方法執行完成,不然執行步驟2
2)當初始maximumPool爲空或者沒有空閒線程時,將沒有線程執行SynchronousQueue.poll,這樣配對將失敗,此時線程池會新建一個線程執行任務怒,execute方法執行完成。
3)當步驟2中建立的線程將任務執行完後,會執行SynchronousQueue.poll,這個poll操做會讓空閒線程最多在SychronousQueue中等待60s,若是60s內主線程沒有提供新任務,空閒線程將終止。
4.ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor繼承自ThreadPoolExecutor,主要用於在給定延遲以後運行任務或者按期執行任務。這點上與Timer相似,但ScheduledThreadPoolExecutor功能更強大,更靈活,不一樣於Timer對應於單個後臺線程,ScheduledThreadPoolExecutor能夠在構造函數中指定多個對應的後臺線程數。
private static final long DEFAULT_KEEPALIVE_MILLIS = 10L;
public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS, new DelayedWorkQueue()); }
該線程池只須要指定核心線程數,其最大線程數無界,其超時時間爲10ms,而且它使用的工做隊列是DelayedWorkQueue,DelayedWorkQueue是一個無界隊列,因此其最大線程數沒有意義。
1) 當調用ScheduledThreadPoolExecutor的ScheduledThreadPoolExecutor的scheduleAtFixedRate()或scheduleWithFixedDelay()方法時,會從ScheduledThreadPoolExecutor的DelayQueue中添加一個實現了RunnbaleScheduledFuture接口的ScheduledFutureTask。
2)線程池中的線程從DelayQueue中獲取ScheduledFutureTask,而後執行任務。
咱們先來看一下ScheduledFutureTask的結構
private class ScheduledFutureTask<V>
extends FutureTask<V> implements RunnableScheduledFuture<V> {
ScheduledFutureTask(Runnable r, V result, long triggerTime,
long period, long sequenceNumber) {
super(r, result);
this.time = triggerTime;
this.period = period;
this.sequenceNumber = sequenceNumber;
}
}
它繼承自FutureTask,它主要包括三個成員變量。以下
time:表示任務將被執行的具體時間
sequenceNumber:表示任務被添加到ScheduledThreadPoolExecutor中的序號
period:表示任務執行的間隔週期
而後咱們是將這個task提交到了DelayQueue中,DelayQueue封裝了一個PriorityQueue,它會對隊列中的ScheduledFutureTask進行排序。排序時,time小的在前面。若是time相同,則比較sequenceNumber,sequenceNumber小的排前面。
ScheduledThreadPoolExecutor的任務執行步驟
1.線程1從DelayQueue獲取到期的任務(ScheduledFutureTask的time大於等於當前時間)
2.線程1 執行這個任務
3.線程1修改任務的time,變動爲下次將被執行的時間
4.將修改後的任務放回到DelayQueue中。
上述獲取任務和添加任務都是線程安全的(使用可重入鎖 ReentrantLock)
6、AsyncTask
AsyncTask是在Executor框架基礎上的再封裝,它實現將耗時任務移動到工做線程中執行,同時提供了方便的接口實現工做線程和主線程之間的通訊。使用AsyncTask通常都會用到以下方法:
import android.os.AsyncTask; public class FullTask extends AsyncTask<Params,Progress,Result>{ @Override protected void onPreExecute() { super.onPreExecute(); } @Override protected Result doInBackground(Params... params) { return null; } @Override protected void onProgressUpdate(Progress... values) { super.onProgressUpdate(values); } @Override protected void onPostExecute(Result result) { super.onPostExecute(result); } @Override protected void onCancelled() { super.onCancelled(); } }
下面給出一個具體的例子直觀地展現它的基本用法。
import android.os.AsyncTask; import java.net.URL; public class DownloadFilesTask extends AsyncTask<URL,Integer,Long>{ @Override 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; } @Override protected void onProgressUpdate(Integer... progress) { setProgressPercent(progress[0]); } @Override protected void onPostExecute(Long result) { showDialog("Downloader" + result+" bytes"); } }
上述程序模擬了一個文件下載過程,當要執行上述下載任務時,能夠經過以下方式來完成:
new DownloadFileTask().execute(url1,url2,url3);
在上述程序中,doInBackground用來執行具體的下載任務並經過publishProgress方法來更新下載進度,同時還要判斷下載任務是否被外界取消了。當下載任務完成時,它返回結果,即下載的總字節數。這裏須要注意的是doInBackground是在線程池中執行。
而onProgressUpdate用於更新界面中下載的進度。運行在主線程中,當publishProgress方法被調用的時候,它就會被調用。當下載任務完成後,onPostExecute方法會被調用,它也是運行在主線程中。這個時候咱們能夠給出一些相應的提示,提醒用戶下載完成。
綜上所述,onPreExecute()用於開始後臺任務前的UI準備。onProgressUpdate用於任務進行時的UI操做,onPostExecute用於任務完成後的UI操做。而doInBackground則用於後臺任務的執行。
注意點:
1.AsyncTask的類必須在主線程中加載。
2.AsyncTask的對象必須在主線程中建立。
3.execute方法必須在UI線程中調用
4.不要在程序直接調用onPreExecute()、onProgressUpdate()、onPostExecute()和doInbackground()
5.AsyncTask對象只能執行一次,即只能調用一次execute方法
6.在android3.0開始,execute()方法採用一個線程串行執行任務,而executeOnExecutor()方法能夠並行執行任務。
下面結合源碼,咱們簡單瞭解一下AsyncTask的原理。
咱們首先看它的構造函數:
public AsyncTask() { mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked Result result = doInBackground(mParams); Binder.flushPendingCommands(); return postResult(result); } }; mFuture = new FutureTask<Result>(mWorker) { @Override protected void done() { try { postResultIfNotInvoked(get()); } catch (InterruptedException e) { android.util.Log.w(LOG_TAG, e); } catch (ExecutionException e) { throw new RuntimeException("An error occurred while executing doInBackground()", e.getCause()); } catch (CancellationException e) { postResultIfNotInvoked(null); } } }; }
這個構造函數將咱們傳入的Params和Result先封裝成了一個WorkerRunnable,再將其傳入FutureTask中。這邊它返回的是postResult方法,查看這個方法
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result; }
在這個方法裏彷佛調用了Handler,併發送了message。點開getHandler方法,最後追蹤到InternalHandler類,正是它實現了執行環境從線程池到主線程的切換。,它的邏輯很常見,這邊就不解釋了。
private static class InternalHandler extends Handler {
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; } } }
獲取這個Handler對象,這邊有一個簡單的同步。
private static Handler getHandler() {
synchronized (AsyncTask.class) { if (sHandler == null) { sHandler = new InternalHandler(); } return sHandler; } }
前面咱們經過構造函數了解了AsyncTask如何將Params和Result進行封裝,如何利用Handler將執行環境從線程池切換到主線程中。接下來咱們來具體看一下AsyncTask的調用過程。咱們從execute()方法入手
能夠看到這邊註解了execute方法必須在主線程上調用,而且這個方法接着調用了executeOnExecutor方法,傳入了sDefalutExecutor,這邊咱們首先得知道這個參數是什麼,找到這個參數。
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
這個sDefalutExecutor是什麼呢?查找SERIAL_EXECUTOR的初始化地方,咱們看到了
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
下面咱們就能夠查看這個SerialExecutor線程池內部類。
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); } } }
從SerialExecutor的實現能夠分析AsyncTask的串行執行的過程。首先系統會把AsyncTask的Params參數封裝爲FutureTask對象,這個FutrueTask對象就被充當爲Runnable叫給SerialExecutor的execute方法去處理。這個方法首先會把FutureTask對象插入到任務隊列mTasks中,並對其進行必定程度的封裝,在後面增長了scheduleNext()方法。注意這個時候它並無進行任務的執行(可見SerialExecutor線程池並非用來執行任務的,而是用來任務的排隊的)若是這個時候沒有處理活動狀態的AsyncTask任務,會調用scheduleNext()方法,來執行下一個任務。
這邊咱們又看到一個線程池,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; }
接着咱們回到executeOnExecutor,
@MainThread 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方法只能執行一次。在executeOnExecutor方法中onPreExecute()方法最先被執行,然後再調用execute進入到咱們先去講到的SerialExecutor線程池進行任務的排隊,最後進入任務執行的線程池進行任務的執行。
至此,大部分結構就算理清了。
7、總結
Android平臺提供了上述諸多異步處理技術,咱們在進行選擇的時候須要根據就提的須要而定。
主要的參考點: