Android異步處理技術

前言:java

  在移動端開發中,咱們必須正確處理好主線程和子線程之間的關係,耗時操做必須在子線程中完成,避免阻塞主線程,致使ANR。異步處理技術是提升引用性能,解決主線程和子線程之間通訊問題的關鍵。android

一般在以下兩種狀況下會彈出ANR對話框:數據庫

  • 5s內沒法響應用戶輸入事件(例如鍵盤輸入, 觸摸屏幕等).
  • BroadcastReceiver在10s內沒法結束.

  形成以上兩種狀況的首要緣由就是在主線程(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);
    }

  上述參數解釋以下:

  • corePoolsize:線程中的核心線程數,默認狀況下核心線程會在線程池一直存活,即便處於空閒狀態。若是將ThreadPoolExecutor的allowCoreThreadTimeOut屬性設置爲ture,那麼閒置的核心線程在等待新任務時會有超時策略,時間間隔由keepAliveTime指定。
  • maximumPoolSize:線程池所能容納的最大線程數,當活動線程數達到這個數值後,後續的新任務將會被阻塞。
  • keepAliveTiem:非核心線程顯示時的超時時長,超過這個時長,非核心線程會被回收,核心線程如上所示。
  • unit:用於指定keepAliveTime參數的時間單位
  • workQueue:線程池的任務隊列,經過線程池的execute方法提交的Runnable對象存儲在這個參數中。
  • threadFactory:線程工廠,爲線程池提供建立新線程的能力。ThreadFactory是一個藉口,只有一個方法 Thread newThread(Runnbale r)。

  除了上述參數之外,看上面經常使用的構造函數內部經過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平臺提供了上述諸多異步處理技術,咱們在進行選擇的時候須要根據就提的須要而定。

  主要的參考點:

  •   儘可能少地佔用系統資源,如cpu和內存
  •   爲應用提供更好的性能和響應度
  •   實現和使用起來複雜度不高,結構清晰。
  •   寫出來的代碼是否符合好的設計,是否容易理解和維護。
相關文章
相關標籤/搜索