Android 線程與進(jìn)程

當(dāng)某個應(yīng)用組件啟動且該應(yīng)用沒有運(yùn)行其他任何組件時,Android 系統(tǒng)會使用單個執(zhí)行線程為應(yīng)用啟動新的 Linux 進(jìn)程。默認(rèn)情況下,同一應(yīng)用的所有組件在相同的進(jìn)程和線程(稱為“主”線程)中運(yùn)行。 如果某個應(yīng)用組件啟動且該應(yīng)用已存在進(jìn)程(因?yàn)榇嬖谠搼?yīng)用的其他組件),則該組件會在此進(jìn)程內(nèi)啟動并使用相同的執(zhí)行線程。 但是,您可以安排應(yīng)用中的其他組件在單獨(dú)的進(jìn)程中運(yùn)行,并為任何進(jìn)程創(chuàng)建額外的線程。

進(jìn)程

默認(rèn)情況下,同一應(yīng)用的所有組件均在相同的進(jìn)程中運(yùn)行,且大多數(shù)應(yīng)用都不會改變這一點(diǎn)。 但是,如果您發(fā)現(xiàn)需要控制某個組件所屬的進(jìn)程,則可在清單文件中執(zhí)行此操作。

各類組件元素的清單文件條目—<activity>、<service>、<receiver> 和 <provider>—均支持 android:process 屬性,此屬性可以指定該組件應(yīng)在哪個進(jìn)程運(yùn)行。您可以設(shè)置此屬性,使每個組件均在各自的進(jìn)程中運(yùn)行,或者使一些組件共享一個進(jìn)程,而其他組件則不共享。 此外,您還可以設(shè)置 android:process,使不同應(yīng)用的組件在相同的進(jìn)程中運(yùn)行,但前提是這些應(yīng)用共享相同的 Linux 用戶 ID 并使用相同的證書進(jìn)行簽署。

此外,<application> 元素還支持 android:process 屬性,以設(shè)置適用于所有組件的默認(rèn)值。

如果內(nèi)存不足,而其他為用戶提供更緊急服務(wù)的進(jìn)程又需要內(nèi)存時,Android 可能會決定在某一時刻關(guān)閉某一進(jìn)程。在被終止進(jìn)程中運(yùn)行的應(yīng)用組件也會隨之銷毀。 當(dāng)這些組件需要再次運(yùn)行時,系統(tǒng)將為它們重啟進(jìn)程。

決定終止哪個進(jìn)程時,Android 系統(tǒng)將權(quán)衡它們對用戶的相對重要程度。例如,相對于托管可見 Activity 的進(jìn)程而言,它更有可能關(guān)閉托管屏幕上不再可見的 Activity 的進(jìn)程。 因此,是否終止某個進(jìn)程的決定取決于該進(jìn)程中所運(yùn)行組件的狀態(tài)。 下面,我們介紹決定終止進(jìn)程所用的規(guī)則。

進(jìn)程生命周期

Android 系統(tǒng)將盡量長時間地保持應(yīng)用進(jìn)程,但為了新建進(jìn)程或運(yùn)行更重要的進(jìn)程,最終需要移除舊進(jìn)程來回收內(nèi)存。 為了確定保留或終止哪些進(jìn)程,系統(tǒng)會根據(jù)進(jìn)程中正在運(yùn)行的組件以及這些組件的狀態(tài),將每個進(jìn)程放入“重要性層次結(jié)構(gòu)”中。 必要時,系統(tǒng)會首先消除重要性最低的進(jìn)程,然后是重要性略遜的進(jìn)程,依此類推,以回收系統(tǒng)資源。

重要性層次結(jié)構(gòu)一共有 5 級。以下列表按照重要程度列出了各類進(jìn)程(第一個進(jìn)程最重要,將是最后一個被終止的進(jìn)程):

  1. 前臺進(jìn)程
    用戶當(dāng)前操作所必需的進(jìn)程。如果一個進(jìn)程滿足以下任一條件,即視為前臺進(jìn)程:
    1> 托管用戶正在交互的 Activity(已調(diào)用 Activity 的 onResume() 方法)
    2> 托管某個 Service,后者綁定到用戶正在交互的 Activity
    3> 托管正在“前臺”運(yùn)行的 Service(服務(wù)已調(diào)用 startForeground())
    4> 托管正執(zhí)行一個生命周期回調(diào)的 Service(onCreate()、onStart() 或 onDestroy())
    5> 托管正執(zhí)行其 onReceive() 方法的 BroadcastReceiver
    通常,在任意給定時間前臺進(jìn)程都為數(shù)不多。只有在內(nèi)存不足以支持它們同時繼續(xù)運(yùn)行這一萬不得已的情況下,系統(tǒng)才會終止它們。 此時,設(shè)備往往已達(dá)到內(nèi)存分頁狀態(tài),因此需要終止一些前臺進(jìn)程來確保用戶界面正常響應(yīng)。

  2. 可見進(jìn)程
    沒有任何前臺組件、但仍會影響用戶在屏幕上所見內(nèi)容的進(jìn)程。 如果一個進(jìn)程滿足以下任一條件,即視為可見進(jìn)程:
    1> 托管不在前臺、但仍對用戶可見的 Activity(已調(diào)用其 onPause() 方法)。例如,如果前臺 Activity 啟動了一個對話框,允許在其后顯示上一 Activity,則有可能會發(fā)生這種情況。
    2> 托管綁定到可見(或前臺)Activity 的 Service。
    可見進(jìn)程被視為是極其重要的進(jìn)程,除非為了維持所有前臺進(jìn)程同時運(yùn)行而必須終止,否則系統(tǒng)不會終止這些進(jìn)程。

  3. 服務(wù)進(jìn)程
    正在運(yùn)行已使用 startService() 方法啟動的服務(wù)且不屬于上述兩個更高類別進(jìn)程的進(jìn)程。盡管服務(wù)進(jìn)程與用戶所見內(nèi)容沒有直接關(guān)聯(lián),但是它們通常在執(zhí)行一些用戶關(guān)心的操作(例如,在后臺播放音樂或從網(wǎng)絡(luò)下載數(shù)據(jù))。因此,除非內(nèi)存不足以維持所有前臺進(jìn)程和可見進(jìn)程同時運(yùn)行,否則系統(tǒng)會讓服務(wù)進(jìn)程保持運(yùn)行狀態(tài)。

  4. 后臺進(jìn)程
    包含目前對用戶不可見的 Activity 的進(jìn)程(已調(diào)用 Activity 的 onStop() 方法)。這些進(jìn)程對用戶體驗(yàn)沒有直接影響,系統(tǒng)可能隨時終止它們,以回收內(nèi)存供前臺進(jìn)程、可見進(jìn)程或服務(wù)進(jìn)程使用。 通常會有很多后臺進(jìn)程在運(yùn)行,因此它們會保存在 LRU (最近最少使用)列表中,以確保包含用戶最近查看的 Activity 的進(jìn)程最后一個被終止。如果某個 Activity 正確實(shí)現(xiàn)了生命周期方法,并保存了其當(dāng)前狀態(tài),則終止其進(jìn)程不會對用戶體驗(yàn)產(chǎn)生明顯影響,因?yàn)楫?dāng)用戶導(dǎo)航回該 Activity 時,Activity 會恢復(fù)其所有可見狀態(tài)。

  5. 空進(jìn)程
    不含任何活動應(yīng)用組件的進(jìn)程。保留這種進(jìn)程的的唯一目的是用作緩存,以縮短下次在其中運(yùn)行組件所需的啟動時間。 為使總體系統(tǒng)資源在進(jìn)程緩存和底層內(nèi)核緩存之間保持平衡,系統(tǒng)往往會終止這些進(jìn)程。

根據(jù)進(jìn)程中當(dāng)前活動組件的重要程度,Android 會將進(jìn)程評定為它可能達(dá)到的最高級別。例如,如果某進(jìn)程托管著服務(wù)和可見 Activity,則會將此進(jìn)程評定為可見進(jìn)程,而不是服務(wù)進(jìn)程。

此外,一個進(jìn)程的級別可能會因其他進(jìn)程對它的依賴而有所提高,即服務(wù)于另一進(jìn)程的進(jìn)程其級別永遠(yuǎn)不會低于其所服務(wù)的進(jìn)程。 例如,如果進(jìn)程 A 中的內(nèi)容提供程序?yàn)檫M(jìn)程 B 中的客戶端提供服務(wù),或者如果進(jìn)程 A 中的服務(wù)綁定到進(jìn)程 B 中的組件,則進(jìn)程 A 始終被視為至少與進(jìn)程 B 同樣重要。

由于運(yùn)行服務(wù)的進(jìn)程其級別高于托管后臺 Activity 的進(jìn)程,因此啟動長時間運(yùn)行操作的 Activity 最好為該操作啟動服務(wù),而不是簡單地創(chuàng)建工作線程,當(dāng)操作有可能比 Activity 更加持久時尤要如此。例如,正在將圖片上傳到網(wǎng)站的 Activity 應(yīng)該啟動服務(wù)來執(zhí)行上傳,這樣一來,即使用戶退出 Activity,仍可在后臺繼續(xù)執(zhí)行上傳操作。使用服務(wù)可以保證,無論 Activity 發(fā)生什么情況,該操作至少具備“服務(wù)進(jìn)程”優(yōu)先級。 同理,廣播接收器也應(yīng)使用服務(wù),而不是簡單地將耗時冗長的操作放入線程中。

1 UI 線程

應(yīng)用啟動時,系統(tǒng)會為應(yīng)用創(chuàng)建一個名為“主線程”的執(zhí)行線程,通常用來處理UI操作的,所以也叫著UI 線程,UI 線程默認(rèn)會創(chuàng)建其對應(yīng)的Loopper,Loopper提供了Android事件驅(qū)動的能力。

系統(tǒng)不會為每個組件實(shí)例創(chuàng)建單獨(dú)的線程。運(yùn)行于同一進(jìn)程的所有組件均在 UI 線程中實(shí)例化,并且對每個組件的系統(tǒng)調(diào)用均由該線程進(jìn)行分派。 因此,響應(yīng)系統(tǒng)回調(diào)的方法(例如,報告用戶操作的 onKeyDown() 或生命周期回調(diào)方法)始終在進(jìn)程的 UI 線程中運(yùn)行。

例如,當(dāng)用戶觸摸屏幕上的按鈕時,應(yīng)用的 UI 線程會將觸摸事件分派給小部件,而小部件反過來又設(shè)置其按下狀態(tài),并將失效請求發(fā)布到事件隊(duì)列中。 UI 線程從隊(duì)列中取消該請求并通知小部件應(yīng)該重繪自身。

如果 UI 線程處理繁重的耗時操作,則執(zhí)行耗時很長的操作(例如,網(wǎng)絡(luò)訪問或數(shù)據(jù)庫查詢)將會阻塞整個 UI。 一旦線程被阻塞,將無法分派任何事件,包括繪圖事件。 從用戶的角度來看,應(yīng)用顯示為掛起。 更糟糕的是,如果 UI 線程被阻塞超過幾秒鐘時間(目前大約是 5 秒鐘),用戶就會看到一個讓人厭煩的“應(yīng)用無響應(yīng)”(ANR) 對話框。

此外,Android UI 工具包并非線程安全工具包。因此,您不得通過工作線程操縱 UI,而只能通過 UI 線程操縱用戶界面。 因此,Android 的單線程模式必須遵守兩條規(guī)則:
1> 不要阻塞 UI 線程
2> 不要在 UI 線程之外訪問 Android UI 工具包

2 工作線程

根據(jù)上述單線程模式,要保證 UI 的響應(yīng)能力,關(guān)鍵是不能阻塞 UI 線程。 如果執(zhí)行的操作不能很快完成,則應(yīng)確保它們在子線程中運(yùn)行。

例如,以下代碼演示了一個點(diǎn)擊偵聽器從單獨(dú)的線程下載圖像并將其顯示在 ImageView 中:

public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            Bitmap b = loadImageFromNetwork("http://example.com/image.png");
            mImageView.setImageBitmap(b);
        }
    }).start();
}

乍看起來,這段代碼似乎運(yùn)行良好,因?yàn)樗鼊?chuàng)建了一個新線程來處理網(wǎng)絡(luò)操作。 但是,它違反了單線程模式的第二條規(guī)則:不要在 UI 線程之外訪問 Android UI 工具包 — 此示例從工作線程(而不是 UI 線程)修改了 ImageView。 這可能導(dǎo)致出現(xiàn)不明確、不可預(yù)見的行為。

為解決此問題,Android 提供了幾種途徑來從其他線程訪問 UI 線程。 以下列出了幾種有用的方法:
1> Activity.runOnUiThread(Runnable)
2> View.post(Runnable)
3> View.postDelayed(Runnable, long)
例如,您可以通過使用 View.post(Runnable) 方法修復(fù)上述代碼:

public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            final Bitmap bitmap =
                    loadImageFromNetwork("http://example.com/image.png");
            mImageView.post(new Runnable() {
                public void run() {
                    mImageView.setImageBitmap(bitmap);
                }
            });
        }
    }).start();
}

現(xiàn)在,上述實(shí)現(xiàn)屬于線程安全型:在單獨(dú)的線程中完成網(wǎng)絡(luò)操作,而在 UI 線程中操縱 ImageView。但是隨著操作日趨復(fù)雜,這類代碼也會變得復(fù)雜且難以維護(hù),可以考慮使用Rxjava來解決線程切換的嵌套問題。

線程的同步

在java語言中,引入了 對象互斥鎖 的概念來實(shí)現(xiàn)不同線程對共享數(shù)據(jù)操作的實(shí)現(xiàn)。對象互斥鎖 阻止多個線程同時訪問同一個條件變量(就是synchronized后面跟的變量,通常是this,所以可以直接將其放到方法返回值前),java可以為每一個實(shí)例配一個 對象互斥鎖,且只有對象才可以被放到synchronized后面的括號中。

在java語言中,有兩種方法實(shí)現(xiàn) 對象互斥鎖:
1> 用關(guān)鍵字volatile來聲明一個共享數(shù)據(jù)。
2> 用關(guān)鍵字synchronized來聲明一個操作共享數(shù)據(jù)的方法、一段代碼。

第一種方法很少被使用,第二種方法更多被使用,用關(guān)鍵字synchronized來聲明一段代碼比聲明一個方法更加高效。

1. synchronized的用法

在android開發(fā)中synchronized主要有兩種用法。第一是在方法聲明時使用;
第二是在對某一代碼塊時使用。具體的實(shí)現(xiàn)代碼如下:

1.1 方法聲明時使用
放在范圍操作符(public等)之后,返回類型聲明(void等)之前.這時,線程獲得的是方法中this實(shí)例的對象鎖,即一次只能有一個線程進(jìn)入該方法,其他線程要想在此時調(diào)用該方法,由于獲得不到對像鎖只能排隊(duì)等候,當(dāng)前線程執(zhí)行完該方法后就會釋放對象鎖,別的線程才能進(jìn)入。

例如:
public synchronized void synMethod() {
//方法體
}

1.2 對某一代碼塊使用
synchronized后跟括號,括號里是變量(只能是對象),這樣,一次只有一個線程進(jìn)入該代碼塊.此時,線程獲得的是成員鎖.例如:
public void synMethod(Integer a1){
synchronized(a1) {
//一次只能有一個線程進(jìn)入
}
}

線程池

線程池的使用在java中占有極其重要的地位,在jdk1.4及其之前的jdk版本中,關(guān)于線程池的使用是極其簡陋的。在jdk1.5之后這一情況有了很大的改觀。Jdk1.5之后加入了java.util.concurrent包,這個包中主要介紹java中線程以及線程池的使用。為我們在開發(fā)中處理線程的問題提供了非常大的幫助。

1. 線程池的作用

線程池作用就是限制執(zhí)行線程的數(shù)量。根據(jù)系統(tǒng)的環(huán)境情況,可以自動或手動設(shè)置線程數(shù)量,達(dá)到運(yùn)行的最佳效果;少了浪費(fèi)了系統(tǒng)資源,多了造成系統(tǒng)擁擠效率不高。用線程池控制線程數(shù)量,空閑線程排隊(duì)等候。一個任務(wù)執(zhí)行完畢,再從隊(duì)列的中取最前面的任務(wù)開始執(zhí)行。若隊(duì)列中沒有等待進(jìn)程,線程池的這一任務(wù)處于等待。當(dāng)一個新任務(wù)需要運(yùn)行時,如果線程池中有等待的工作線程,就可以開始運(yùn)行了;否則進(jìn)入等待隊(duì)列。

2. 為什么要用線程池

1> 減少了創(chuàng)建和銷毀線程的次數(shù),每個工作線程都可以被重復(fù)利用,可執(zhí)行多個任務(wù)。
2> 可以根據(jù)系統(tǒng)的承受能力,調(diào)整線程池中工作線線程的數(shù)目,防止因?yàn)橄倪^多的內(nèi)存,而把服務(wù)器累趴下(每個線程需要大約1MB內(nèi)存,線程開的越多,消耗的內(nèi)存也就越大,最后死機(jī))。
Java里面線程池的頂級接口是Executor,但是嚴(yán)格意義上講Executor并不是一個線程池,而只是一個執(zhí)行線程的工具。真正的線程池接口是ExecutorService。

3. 比較重要的幾個類

類名 說明
ExecutorService 真正的線程池接口
ScheduledExecutorService 和Timer/TimerTask類似,解決那些需要任務(wù)重復(fù)執(zhí)行的問題
ThreadPoolExecutor ExecutorService的默認(rèn)實(shí)現(xiàn)
ScheduledThreadPoolExecutor 繼承ThreadPoolExecutor類和對ScheduledExecutorService接口的實(shí)現(xiàn),周期性任務(wù)調(diào)度的類實(shí)現(xiàn)

要配置一個線程池是比較復(fù)雜的,尤其是對于線程池的原理不是很清楚的情況下,很有可能配置的線程池不是較優(yōu)的,因此在Executors類里面提供了一些工廠方法用來生成一些常用的線程池:

/**
 * 創(chuàng)建一個使用單個線程操作一個無界任務(wù)隊(duì)列的線程池。(但是,如果這個唯一的工作線程
 *由于執(zhí)行期間的異常而終止,此時如果需要執(zhí)行后續(xù)的任務(wù),那么會有一個新的線程來替代它。)
 *任務(wù)保證被順序地執(zhí)行,并且在任何給定的時間不多于一個任務(wù)被正在執(zhí)行 。 
 *與其他等效的{@code newFixedThreadPool(1)}不同,返回的線程池不能被重新配置以使用額外的線程。
 *
 * @return 新創(chuàng)建的單線程線程池
 */
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
/**
 * 創(chuàng)建一個使用單個線程操作一個無界任務(wù)隊(duì)列的線程池,并在需要時使用提供的
 *ThreadFactory創(chuàng)建一個新線程。 與其他等效的{@code newFixedThreadPool(1,threadFactory)}不同,
 *返回的線程池不能被重新配置以使用額外的線程。
 *
 * @param threadFactory 在創(chuàng)建新線程時使用
 *
 * @return 新創(chuàng)建的單線程線程池
 * @throws NullPointerException if threadFactory is null
 */
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>(),
                                threadFactory));
}
/**
 * 創(chuàng)建一個使用固定數(shù)量線程去操作一個共享無界任務(wù)隊(duì)列的線程池。 在任何時候,至多nThreads個線程
 *正在處理任務(wù)。 如果在所有線程都處于活動狀態(tài)時提交新任務(wù),那么新任務(wù)將在任務(wù)隊(duì)列中等待,直到線程可用。 
 *如果任何線程由于執(zhí)行期間的異常而終止,此時如果需要執(zhí)行后續(xù)的任務(wù),那么會有一個新的線程來替代它。 
 *池中的線程將永遠(yuǎn)存在,直到顯式調(diào)用{@link ExecutorService#shutdown shutdown}。
 *
 * @param nThreads 池中的線程數(shù)
 * @return 新創(chuàng)建的線程池
 * @throws IllegalArgumentException if {@code nThreads <= 0}
 */
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

/**
 *  創(chuàng)建一個使用固定數(shù)量線程去操作一個共享無界任務(wù)隊(duì)列的線程池。
 *使用提供的ThreadFactory在需要時創(chuàng)建新線程。 在任何時候,至多nThreads個線程正在處理任務(wù)。
 *如果在所有線程都處于活動狀態(tài)時提交新任務(wù),那么新任務(wù)將在任務(wù)隊(duì)列中等待,直到線程可用。 
 *如果任何線程由于執(zhí)行期間的異常而終止,此時如果需要執(zhí)行后續(xù)的任務(wù),那么會有一個新的線程來替代它。 
 *池中的線程將永遠(yuǎn)存在,直到顯式調(diào)用{@link ExecutorService#shutdown shutdown}。
 *
 * @param nThreads 池中的線程數(shù)
 * @param threadFactory 在創(chuàng)建新線程時使用
 * @return 新創(chuàng)建的線程池
 * @throws NullPointerException if threadFactory is null
 * @throws IllegalArgumentException if {@code nThreads <= 0}
 */
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>(),
                                  threadFactory);
}
/**
 * 創(chuàng)建一個根據(jù)需要創(chuàng)建新線程的線程池,但會在以前構(gòu)造的線程可用時重用它。 這樣的線程池通常
 *會提高需要執(zhí)行許多短暫異步任務(wù)的程序的性能。 調(diào)用{@code execute}將重用以前構(gòu)造的線程(如果可用)。 
 *如果沒有現(xiàn)有線程可用,將創(chuàng)建一個新線程并將其添加到池中。 超過60秒未被使用的線程將被終止,
 *并從緩存中刪除。 因此,這樣的線程池長時間的空閑也不會消耗任何資源。 
 *注意,可以使用{@link ThreadPoolExecutor}構(gòu)造函數(shù)創(chuàng)建具有相似屬性但不同細(xì)節(jié)
 *(例如,超時參數(shù))的線程池。
 *
 * @return 新創(chuàng)建的線程池
 */
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

/**
 * 創(chuàng)建一個根據(jù)需要創(chuàng)建新線程的線程池,但會在以前構(gòu)造的線程可用時重用它,
 *并在需要時使用提供的ThreadFactory創(chuàng)建新線程。
 * @param threadFactory 在創(chuàng)建新線程時使用
 * @return 新創(chuàng)建的線程池
 * @throws NullPointerException if threadFactory is null
 */
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>(),
                                  threadFactory);
}
/**
 * 創(chuàng)建可在給定延遲后被調(diào)度運(yùn)行或者定期執(zhí)行的線程池。
 * @param corePoolSize 在池中保留的線程數(shù),即使它們是空閑的
 * @return 新創(chuàng)建的調(diào)度線程池
 * @throws IllegalArgumentException if {@code corePoolSize < 0}
 */
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

/**
 * 創(chuàng)建可在給定延遲后被調(diào)度運(yùn)行或者定期執(zhí)行的線程池。
 * @param corePoolSize 在池中保留的線程數(shù),即使它們是空閑的
 * @param threadFactory 在創(chuàng)建新線程時使用
 * @return 新創(chuàng)建的調(diào)度線程池
 * @throws IllegalArgumentException if {@code corePoolSize < 0}
 * @throws NullPointerException if threadFactory is null
 */
public static ScheduledExecutorService newScheduledThreadPool(
        int corePoolSize, ThreadFactory threadFactory) {
    return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
/**
 * 創(chuàng)建單線程的線程池,可以在給定延遲后被調(diào)度運(yùn)行,或定期執(zhí)行。 
 *(但是,如果該單個線程由于在執(zhí)行期間的異常而終止,此時如果需要執(zhí)行后續(xù)的任務(wù),
 *那么會有一個新的線程來替代它。)任務(wù)保證被順序地執(zhí)行,并且在任何給定的時間不多于一個任務(wù)被正在執(zhí)行。 
 *與其他等效的{@code newScheduledThreadPool(1)}不同,返回的線程池不能被重新配置以使用額外的線程。
 * @return 新創(chuàng)建的調(diào)度線程池
 */
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
    return new DelegatedScheduledExecutorService
        (new ScheduledThreadPoolExecutor(1));
}

/**
 * 創(chuàng)建單線程線程池,可以在給定延遲后被調(diào)度運(yùn)行,或定期執(zhí)行。 
 *(但是,如果該單個線程由于在執(zhí)行期間的異常而終止,此時如果需要執(zhí)行后續(xù)的任務(wù),
 *那么會有一個新的線程來替代它。)任務(wù)保證被順序地執(zhí)行,并且在任何給定的時間不多于一個任務(wù)被正在執(zhí)行。 
 *與其他等效的{@code newScheduledThreadPool(1,threadFactory)}不同,
 *返回的線程池不能被重新配置為使用額外的線程。
 * @param threadFactory 在創(chuàng)建新線程時使用
 * @return 新創(chuàng)建的調(diào)度線程池
 * @throws NullPointerException if threadFactory is null
 */
public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {
    return new DelegatedScheduledExecutorService
        (new ScheduledThreadPoolExecutor(1, threadFactory));
}

上面的注釋已經(jīng)詳細(xì)的說明了Executors類中常用的工廠方法,這里就不在說明了。
上面的工廠方法直接或者間接的創(chuàng)建ThreadPoolExecutor類的實(shí)例或者ScheduledThreadPoolExecutor類的實(shí)例,那下面我們就來看一下ThreadPoolExecutor和ScheduledThreadPoolExecutor類的構(gòu)造方法:

/**
 *使用給定的初始參數(shù)、默認(rèn)thread factory和RejectedExecutionHandler創(chuàng)建一個
 *ThreadPoolExecutor實(shí)例。 使用Executors的工廠方法而不是這個通用構(gòu)造函數(shù)可能更方便。
 *
 * @param corePoolSize 要保留在池中的核心線程數(shù),即使它們是空閑的,除非設(shè)置{@code allowCoreThreadTimeOut}
 * @param maximumPoolSize 池中允許的最大線程數(shù)
 * @param keepAliveTime 當(dāng)線程數(shù)大于核心線程數(shù)時,
 *        超出核心線程數(shù)的空閑線程在終止前等待新任務(wù)的最大時間。
 * @param unit keepAliveTime參數(shù)的時間單位
 * @param workQueue 在執(zhí)行任務(wù)之前用于保存任務(wù)的隊(duì)列。 此隊(duì)列只保留execute方法提交的Runnable任務(wù)。
 * @throws IllegalArgumentException 如果以下之一成立:<br>
 *         {@code corePoolSize < 0}<br>
 *         {@code keepAliveTime < 0}<br>
 *         {@code maximumPoolSize <= 0}<br>
 *         {@code maximumPoolSize < corePoolSize}
 * @throws NullPointerException if {@code workQueue} is null
 */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}

/**
 * 使用給定的初始參數(shù)和默認(rèn)RejectedExecutionHandler創(chuàng)建ThreadPoolExecutor實(shí)例。
 *
 * @param threadFactory 在創(chuàng)建新線程時使用
 *  其他參數(shù)同上,此處不在贅敘。
 * @throws IllegalArgumentException if one of the following holds:<br>
 *         {@code corePoolSize < 0}<br>
 *         {@code keepAliveTime < 0}<br>
 *         {@code maximumPoolSize <= 0}<br>
 *         {@code maximumPoolSize < corePoolSize}
 * @throws NullPointerException if {@code workQueue}
 *         or {@code threadFactory} is null
 */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         threadFactory, defaultHandler);
}

/**
 * 使用給定的初始參數(shù)和默認(rèn)thread factory創(chuàng)建一個ThreadPoolExecutor實(shí)例。
 *
 * @param handler 當(dāng)執(zhí)行被阻塞時被使用,因?yàn)橐堰_(dá)到線程限制和隊(duì)列容量
 *  其他參數(shù)同上,此處不在贅敘。
 * @throws IllegalArgumentException if one of the following holds:<br>
 *         {@code corePoolSize < 0}<br>
 *         {@code keepAliveTime < 0}<br>
 *         {@code maximumPoolSize <= 0}<br>
 *         {@code maximumPoolSize < corePoolSize}
 * @throws NullPointerException if {@code workQueue}
 *         or {@code handler} is null
 */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), handler);
}

/**
 * 使用給定的初始參數(shù)創(chuàng)建一個ThreadPoolExecutor實(shí)例。
 *
 *  參數(shù)同上,此處不在贅敘。
 * @throws IllegalArgumentException if one of the following holds:<br>
 *         {@code corePoolSize < 0}<br>
 *         {@code keepAliveTime < 0}<br>
 *         {@code maximumPoolSize <= 0}<br>
 *         {@code maximumPoolSize < corePoolSize}
 * @throws NullPointerException if {@code workQueue}
 *         or {@code threadFactory} or {@code handler} is null
 */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

/**
 * 使用給定的核心線程數(shù)創(chuàng)建ScheduledThreadPoolExecutor實(shí)例。
 *
 * @param corePoolSize corePoolSize 要保留在池中的核心線程數(shù),即使它們是空閑的,除非設(shè)置{@code allowCoreThreadTimeOut}
 * @throws IllegalArgumentException if {@code corePoolSize < 0}
 */
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE,
          DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
          new DelayedWorkQueue());
}

/**
 * 使用給定的初始參數(shù)創(chuàng)建ScheduledThreadPoolExecutor實(shí)例。
 *
 * @param threadFactory 在創(chuàng)建新線程時使用
 *  其他參數(shù)同上,此處不在贅敘。
 * @throws IllegalArgumentException if {@code corePoolSize < 0}
 * @throws NullPointerException if {@code threadFactory} is null
 */
public ScheduledThreadPoolExecutor(int corePoolSize,
                                   ThreadFactory threadFactory) {
    super(corePoolSize, Integer.MAX_VALUE,
          DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
          new DelayedWorkQueue(), threadFactory);
}

/**
 * 使用給定的初始參數(shù)創(chuàng)建ScheduledThreadPoolExecutor實(shí)例。
 *
 * @param handler 當(dāng)執(zhí)行被阻塞時被使用,因?yàn)橐堰_(dá)到線程限制和隊(duì)列容量
 *  其他參數(shù)同上,此處不在贅敘。
 * @throws IllegalArgumentException if {@code corePoolSize < 0}
 * @throws NullPointerException if {@code handler} is null
 */
public ScheduledThreadPoolExecutor(int corePoolSize,
                                   RejectedExecutionHandler handler) {
    super(corePoolSize, Integer.MAX_VALUE,
          DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
          new DelayedWorkQueue(), handler);
}

/**
 * 使用給定的初始參數(shù)創(chuàng)建ScheduledThreadPoolExecutor實(shí)例。
 *
 *  參數(shù)同上,此處不在贅敘。
 * @throws IllegalArgumentException if {@code corePoolSize < 0}
 * @throws NullPointerException if {@code threadFactory} or
 *         {@code handler} is null
 */
public ScheduledThreadPoolExecutor(int corePoolSize,
                                   ThreadFactory threadFactory,
                                   RejectedExecutionHandler handler) {
    super(corePoolSize, Integer.MAX_VALUE,
          DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
          new DelayedWorkQueue(), threadFactory, handler);
}

在JDK幫助文檔中,有如此一段話:
“強(qiáng)烈建議程序員使用較為方便的Executors工廠方法Executors.newCachedThreadPool()(無界線程池,可以進(jìn)行自動線程回收)、Executors.newFixedThreadPool(int)(固定大小線程池)、Executors.newSingleThreadExecutor()(單個后臺線程),
它們?yōu)榇蠖鄶?shù)使用場景預(yù)定義了設(shè)置。”

HandlerThread 和 AsyncTask

在開發(fā)的過程中,下面兩種情況相信大家都遇到過。
1> 不定期的執(zhí)行一些不需要與應(yīng)用程序界面交互的任務(wù),通常是通過創(chuàng)建一個具有消息循環(huán)的工作線程來實(shí)現(xiàn)的,當(dāng)我們需要執(zhí)行任務(wù)時,就往該工作線程的消息隊(duì)列中發(fā)送一個消息,然后就可以在工作線程中執(zhí)行對應(yīng)的任務(wù)。

2> 執(zhí)行一些需要與應(yīng)用程序界面交互的任務(wù),比如在網(wǎng)上下載文件并且在下載的同時還要在應(yīng)用程序的界面上顯示下載進(jìn)度,通常會創(chuàng)建一個工作線程來負(fù)責(zé)下載任務(wù)(避免ANR異常),在下載的過程中工作線程向主線程的消息隊(duì)列中發(fā)送包含下載進(jìn)度的消息,主線程處理消息時就可以顯示當(dāng)前的下載進(jìn)度。

上面兩種情況解決方法的描述有些抽象,有興趣的同學(xué)可以參考 Android 消息機(jī)制源碼分析一文最后面的實(shí)例講解。對于上面兩種情況,Google提供了完善的解決方案,前者通過HandlerThread類來解決,后者通過AsyncTask類來解決。

1. HandlerThread詳解

HandlerThread類繼承自Thread類,HandlerThread類用于創(chuàng)建具有消息循環(huán)的線程。 然后通過消息循環(huán)創(chuàng)建Handler類實(shí)例。 注意start方法必須被調(diào)用。
首先先來看一下HandlerThread如何使用:

HandlerThread handlerThread = new HandlerThread("handlerThread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper()) {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
    }
};

首先我們來看一下HandlerThread的構(gòu)造方法:

public HandlerThread(String name) {
    super(name);
    mPriority = Process.THREAD_PRIORITY_DEFAULT;
}

HandlerThread的構(gòu)造方法很簡單,創(chuàng)建一個指定名稱的線程并且設(shè)置mPriority的值為Process.THREAD_PRIORITY_DEFAULT。
接著調(diào)用HandlerThread類的start方法,由于HandlerThread類繼承自Thread類,所以HandlerThread類的run方法會被調(diào)用,源碼如下:

@Override
public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}

HandlerThread的run方法的源碼很簡單,就是用來為HandlerThread對應(yīng)的線程創(chuàng)建消息循環(huán)。
接著就是創(chuàng)建一個向HandlerThread發(fā)送消息的Handler實(shí)例。

2. AsyncTask詳解

AsyncTask被設(shè)計為一個關(guān)于Thread和Handler的幫助類,而不是一個通用的Thread框架。 AsyncTasks應(yīng)該用于短操作(最多幾秒鐘)。如果您需要保持Thread長時間運(yùn)行,強(qiáng)烈建議您使用由 java.util.concurrent包中提供的各種API,例如Executor,ThreadPoolExecutor和FutureTask。
一個asynchronous task被定義成一個在后臺Thread上運(yùn)行(對應(yīng)于doInBackground方法)并且在UI線程上發(fā)布運(yùn)行結(jié)果(對應(yīng)于onProgressUpdate 和 onPostExecute方法)的運(yùn)算。
首先看一下AsyncTask如何使用:

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));
              // Escape early if cancel() is called
              if (isCancelled()) break;
          }
          return totalSize;
      }
 
      protected void onProgressUpdate(Integer... progress) {
          setProgressPercent(progress[0]);
      }
 
      protected void onPostExecute(Long result) {
          showDialog("Downloaded " + result + " bytes");
      }
}

new DownloadFilesTask().execute(url1, url2, url3);

上面的代碼實(shí)現(xiàn)了一個下載文件的AsyncTask。

下面詳細(xì)講解一下AsyncTask中用到的3個類型和4個步驟:
1> Params : 執(zhí)行任務(wù)時發(fā)送到任務(wù)的參數(shù)的類型。
2> Progress : 在后臺任務(wù)執(zhí)行期間發(fā)布的進(jìn)度的類型。
3> Result :任務(wù)執(zhí)行結(jié)果的類型。
并非上面的類型都會被AsyncTask用到。 如果沒有用到可以使用類型Void。

當(dāng)執(zhí)行AsyncTask時,AsyncTask會經(jīng)過如下4個步驟:
1> onPreExecute() :在執(zhí)行任務(wù)之前在UI線程上被調(diào)用。 此步驟通常用于setup 任務(wù),例如在用戶界面中顯示進(jìn)度條。
2> doInBackground:在onPreExecute方法完成執(zhí)行后立即在后臺線程上被調(diào)用。 此步驟用于執(zhí)行可能需要很長時間的后臺計算。 任務(wù)的參數(shù)(Params類型)會被傳遞到此方法。 計算的結(jié)果(Result類型)必須在此方法的最后一步被返回。 此方法還可以使用publishProgress方法發(fā)布一個或多個進(jìn)度值(Progress類型)。 這些進(jìn)度值將在UI線程上的onProgressUpdate方法中被發(fā)布。
3> onProgressUpdate:在調(diào)用publishProgress后會在UI線程上被調(diào)用。 執(zhí)行的時間是不定期的。 此方法用于在后臺計算仍在執(zhí)行的過程中在用戶界面中顯示任何形式的進(jìn)度。 例如,它用于顯示動態(tài)進(jìn)度條或在文本字段中顯示進(jìn)度。
4> onPostExecute:在后臺計算完成(doInBackground方法返回)后在UI線程上被調(diào)用。 后臺計算的結(jié)果作為參數(shù)傳遞到該方法。

注意,還有一個cancel方法也是很常用的,比如執(zhí)行壓縮文件的過程是很漫長的,如果用戶沒有耐心了,想取消壓縮任務(wù),那么該方法就派上用場了,源碼如下:

public final boolean cancel(boolean mayInterruptIfRunning) {
    mCancelled.set(true);
    return mFuture.cancel(mayInterruptIfRunning);
}

該方法嘗試取消被執(zhí)行的任務(wù)。 如果任務(wù)已完成,已取消或者由于某些其他原因無法被取消,則此嘗試將失敗(即該方法返回false)。 如果此嘗試成功(即該方法返回true),并且調(diào)用cancel方法時此任務(wù)尚未開始,則此任務(wù)將不會被執(zhí)行; 如果任務(wù)已經(jīng)啟動,則mayInterruptIfRunning參數(shù)確定正在執(zhí)行此任務(wù)的線程是否應(yīng)在嘗試取消任務(wù)時被中斷(true 正在執(zhí)行此任務(wù)的線程應(yīng)該被中斷; 否則,正在進(jìn)行的任務(wù)被允許完成)。

調(diào)用此方法將導(dǎo)致回調(diào)方法onCancelled(Object)在回調(diào)方法doInBackground(Object [])返回后在UI線程上被調(diào)用。 調(diào)用此方法可確保不會調(diào)用回調(diào)方法onPostExecute(Object)和onProgressUpdate(Progress... values)。 調(diào)用此方法后,您應(yīng)該在回調(diào)方法doInBackground(Object [])中定期檢查isCancelled()返回的值,以盡快結(jié)束任務(wù)。

內(nèi)存可觀察性

AsyncTask保證所有回調(diào)調(diào)用都以這樣一種方式同步,使得以下操作在沒有顯式同步的情況下是安全的。
1> 在構(gòu)造函數(shù)或onPreExecute方法中設(shè)置成員字段,并在doInBackground方法中引用它們。
2> 在doInBackground方法中設(shè)置成員字段,并在onProgressUpdate和onPostExecute方法中引用它們。

執(zhí)行順序

當(dāng)AsyncTask第一次被引入時,AsyncTasks在單個后臺線程上連續(xù)執(zhí)行。 從android.os.Build.VERSION_CODES#DONUT開始,這被更改為允許多個任務(wù)并行執(zhí)行的線程池。 從android.os.Build.VERSION_CODES#HONEYCOMB開始,任務(wù)在單個線程上執(zhí)行,以避免并行執(zhí)行引起的常見應(yīng)用程序錯誤。
如果你真的想并行執(zhí)行,你可以使用THREAD_POOL_EXECUTOR作為參數(shù)調(diào)用executeOnExecutor(java.util.concurrent.Executor,Object [])方法。

源碼分析

我們來看一下AsyncTask的execute方法及其相關(guān)的代碼:

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

/**
 * 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;

@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}

@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.");
            //這個異常相信大家非常熟悉,也就是說一個AsyncTask的實(shí)例只能被執(zhí)行一次
            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;
}

@MainThread
public static void execute(Runnable runnable) {
    sDefaultExecutor.execute(runnable);
}

AsyncTask共有兩個重載的execute方法,上面的代碼很簡單,AsyncTask的兩個execute方法最終都會調(diào)用線程池SERIAL_EXECUTOR的void execute(Runnable command)方法,由源碼中的注釋可以得知在線程池SERIAL_EXECUTOR上執(zhí)行的任務(wù)都是串行執(zhí)行的,從而證明了上面的一句話:從android.os.Build.VERSION_CODES#HONEYCOMB開始,任務(wù)在單個線程上執(zhí)行

上面還提到過線程池THREAD_POOL_EXECUTOR,對應(yīng)的源碼如下:

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;

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

/**
 * 可用于并行執(zhí)行任務(wù)的{@link Executor}。
 */
public static final Executor 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;
}

上面的代碼很簡單,就是創(chuàng)建了一個可以并行執(zhí)行的線程池,那么如果想要并行執(zhí)行任務(wù)的話,就可以使用AsyncTask類的executeOnExecutor方法并且將THREAD_POOL_EXECUTOR作為第一個參數(shù)。

舉例說明

經(jīng)過上面對AsyncTask的分析可知:
AsyncTask默認(rèn)是串行執(zhí)行任務(wù)的,但是也提供了executeOnExecutor來并行的執(zhí)行任務(wù),下面我會寫一個樣例來實(shí)現(xiàn)這兩個功能,代碼如下:

AsyncTaskExecutor.java:

public interface AsyncTaskExecutor {
    <T> AsyncTask<T, ?, ?> submit(Object identifier, AsyncTask<T, ?, ?> task, T... params);
}

AsyncTaskExecutors.java:

/**
 * Created by chenyang on 2017/1/5.
 * Factory methods for creating AsyncTaskExecutors.
 */
public class AsyncTaskExecutors {

    /**
     * Creates an AsyncTaskExecutor that submits tasks to run with
     * {@link AsyncTask#SERIAL_EXECUTOR}.
     */
    public static AsyncTaskExecutor createAsyncTaskExecutor() {
        synchronized (AsyncTaskExecutors.class) {
            return new SimpleAsyncTaskExecutor(AsyncTask.SERIAL_EXECUTOR);
        }
    }

    /**
     * Creates an AsyncTaskExecutor that submits tasks to run with
     * {@link AsyncTask#THREAD_POOL_EXECUTOR}.
     */
    public static AsyncTaskExecutor createThreadPoolExecutor() {
        synchronized (AsyncTaskExecutors.class) {
            return new SimpleAsyncTaskExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
        }
    }

    public static void checkCalledFromUiThread() {
        if (Thread.currentThread() != Looper.getMainLooper().getThread()) {
           throw new RuntimeException("submit method must be called from ui thread, was: " + Thread.currentThread());
        }
    }

    private static class SimpleAsyncTaskExecutor implements AsyncTaskExecutor {
        private final Executor mExecutor;

        public SimpleAsyncTaskExecutor(Executor executor) {
            mExecutor = executor;
        }

        @Override
        public <T> AsyncTask<T, ?, ?> submit(Object identifer, AsyncTask<T, ?, ?> task,
                                             T... params) {
            checkCalledFromUiThread();
            return task.executeOnExecutor(mExecutor, params);
        }
    }
}

FileTaskHelper.java:

/**
 * Created by chenyang on 2017/1/5.
 */
public class FileTaskHelper {

    private static AsyncTaskExecutor sAsyncTaskExecutor = null;

    private static void initTaskExecutor() {
        sAsyncTaskExecutor = AsyncTaskExecutors.createThreadPoolExecutor();
    }

    /** The enumeration of {@link AsyncTask} objects used in this class. */
    public enum Tasks {
        COPY_FILE(0);

        private int value;
        Tasks(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }

        public static Tasks valueOf(int value) {
            Tasks ret = COPY_FILE;

            for (Tasks orientationType : Tasks.values()) {
                if (orientationType.getValue() == value) {
                    ret = orientationType;
                    break;
                }
            }

            return ret;
        }
    }

    public static void copyFile (AsyncTask<String, Object, Object> task, String... params) {
        if (null == sAsyncTaskExecutor) {
            initTaskExecutor();
        }
        sAsyncTaskExecutor.submit(Tasks.COPY_FILE, task, params);
    }
}

由上面的代碼可知,F(xiàn)ileTaskHelper的copyFile方法就是將copy文件的任務(wù)提交到線程池THREAD_POOL_EXECUTOR上執(zhí)行,是串行還是并行是與FileTaskHelper類的實(shí)現(xiàn)有關(guān)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,818評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,185評論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,656評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,647評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,446評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,951評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,041評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,189評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,718評論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,800評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,419評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,420評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,755評論 2 371

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,603評論 25 707
  • 進(jìn)程和線程 來源官方文檔 當(dāng)某個應(yīng)用組件啟動且該應(yīng)用沒有運(yùn)行其他任何組件時,Android 系統(tǒng)會使用單個執(zhí)行線程...
    霧里看花最是迷人閱讀 810評論 0 2
  • 對于數(shù)組的基本操作,可參考PHP數(shù)組的相關(guān)操作 array_filter()過濾數(shù)組元素array_filter(...
    欒呱呱閱讀 593評論 0 2
  • 走在有些清寒的街道,聽風(fēng)與落葉呢喃,在那一抹溫婉中,還是會把你記起。不是因?yàn)橛卸嘞肽悖皇且驗(yàn)椋?jīng)的那份熟悉。 ...
    圣淼閱讀 276評論 0 0
  • 本教程介紹一種比較綜合的人物磨皮主要是用計算、通道、曲線、模糊濾鏡等來去斑。大致思路:先選擇合適的通道并復(fù)制,然后...
    有脾氣的girl閱讀 431評論 0 2