Android線程與線程池《Android開發藝術探索》筆記


參考文章:要點提煉|開發藝術之線程

概述

線程是CPU調度的最小單元。

線程是一種受限的資源,不能多次的創建與停止。

分類:
主線程:一般一個線程只有一個主線程。主線程中一般用於UI操作,用於與用戶交互。
子線程:子線程中一般執行耗時操作,比如網絡操作和IO操作。

線程形態:
AysncTask:一種異步執行類,便於執行後臺任務以及在主線程更新UI。其中封裝了兩個線程池,一個Handler,其中一個線程池用於任務的排序,另一個線程池用於處理任務。Handler用於切換到主線程。

HandlerThread:可以持續的循環從消息隊列中取消息的線程。(其實內部調用了Looper.prepare和Looper.loop方法)。

IntentService:是一個異步並會自動停止的服務,好處就是優先級比較高,不容易被回收。內部封裝了一個HandlerThread和一個Handler,Handler與HandlerThread中的Looper綁定。

在這裏插入圖片描述

線程形態

AsyncTask

概述與使用

在Android中實現異步任務機制有兩種方式:Handler和AsyncTask

Handler機制存在的問題:代碼相對臃腫;多任務同時執行時不易精確控制線程

引入AsyncTask的好處:創建異步任務更簡單,直接繼承它可方便實現後臺異步任務的執行和進度的回調更新UI,而無需編寫任務線程和Handler實例就能完成相同的任務。

AsyncTask是一個泛型類,其中包括三個泛型參數 public abstract class AsyncTask<Void, Integer, Boolean>

  • params:傳入的數據。(一般是傳入URL,類型對應是String)
  • progress:任務進度(類型一般是Integer)
  • result:結果。

包含五個重要方法

  • onPreExecute運行在主線程中,在後臺任務執行前會調用進行一些初始化操作
  • doInBackground(…params)運行在子線程中後臺任務具體在這裏會執行。如果在其中想要知道任務執行的進度需要調用publishProgress(progress)方法該方法會回調onProgressUpdate方法
  • onProgressUpdate(progress)運行在主線程,可以根據progress的值來更新UI
  • onPostExcute(result)運行在主線程,當任務運行結束後會調用,可以根據result值來更新UI。
  • onCanceled:運行在主線程,當任務被取消時會調用

開始和結束異步任務的方法

  • 開始異步任務:AysncTask.execute(Params...params)必須在主線程中使用,同時注意一個任務只能調用一次execute方法否則會報錯。
  • 結束異步任務:AysncTask.cancel必須在主線程中使用,表示停止一個服務

使用注意:
(1)不要直接調用AysncTask中的方法比如直接調用onProgressUpdate方法來獲取當前進度,而是應該使用其中的progress來更新UI。
(2)AysncTask的實例必須在主線程中創建。(具體原因在於其中有一個Handler,是用於轉換到主線程的,它的一系列handle方法要在主線程使用,因此Handler必須在主線程中創建,進而AysncTask也必須在主線程創建)。

接下來是一個自定義AysncTask的實例

class DownloadTask extends AsyncTask<Void, Integer, Boolean> {  
  
    @Override//初始化一個ProgressDialog  
    protected void onPreExecute() {  
        progressDialog.show();  
    }  
  
    @Override//具體的下載邏輯
    protected Boolean doInBackground(Void... params) {  
        try {  
            while (true) {  
                int downloadPercent = doDownload();  
                publishProgress(downloadPercent);  
                if (downloadPercent >= 100) {  
                    break;  
                }  
            }  
        } catch (Exception e) {  
            return false;  
        }  
        return true;  
    }  
  
    @Override//顯示當前的下載進度
    protected void onProgressUpdate(Integer... values) {  
        progressDialog.setMessage("當前下載進度:" + values[0] + "%");  
    }  
  
    @Override//提示任務的執行結果  
    protected void onPostExecute(Boolean result) {  
        progressDialog.dismiss();  
        if (result) {  
            Toast.makeText(context, "下載成功", Toast.LENGTH_SHORT).show();  
        } else {  
            Toast.makeText(context, "下載失敗", Toast.LENGTH_SHORT).show();  
        }  
    }  
}  

開啓和停止的代碼如下:

// 開始任務  
DownloadTask mDownloadTask  = new DownloadTask();  
mDownloadTask .execute();  
// 停止任務  
mDownloadTask .cancel(true);  

原理

AysncTask包括兩個線程池:SerialExecutor與THREAD_POOL_EXECUTOR;他們的作用分別是任務的排隊與任務的實際執行。

AysncTask包含一個Handler,該Handler的作用是將執行環境由線程池切換至主線程並通過它來發送任務的執行情況的消息。

任務排隊的原理是:
(1)params被封裝爲FutureTask;

(2)調用SerialExecutor.execute()將FutureTask插入到任務隊列tasks

(3)判斷當前是否沒有任務在執行,沒有的話會調用scheduleNext方法去執行下一個任務,同時當任務執行完畢後,也會調用scheduleNext方法去執行下一個任務。可以看出AysncTask是以串行的方式執行任務。

任務執行的原理:
(1)FutureTask的run函數會調用mWorker的call函數

(2)在call函數中調用doInbackground方法,接着將doInbackground的返回值傳入postResult方法

(3)postResult方法中會調用sendMessage方法。

(4)在handleMessage方法中根據發送的msg可以判斷當前的情況,進而去調用onPostExcute或onCancel或onProgressUpdate方法

HandlerThread

該類是一個線程類,與普通線程的區別在於,內部對Looper進行了創建,因此可以持續的接收和查找消息。內部調用了Looper.prepare;Looper.loop方法。

實現方法:
1.創建一個HandlerThread實例,並傳入該線程的名字。

2.通過 HandlerThread.start()開啓線程;

3.創建Handler,並在構造函數中傳入HandlerThread的Looper對象,完成與Handler的綁定。

4.當不需要HandlerThread時,通過HandlerThread.quit()/quitSafely()方法來終止線程的執行

IntentService

IntentService是一個服務,可以在其中執行耗時的任務,IntentService內部包含了一個HandlerThread和一個Handler。
優點:由於是服務,優先級比較高,不會輕易被殺死,所以適合去做一些優先級高的耗時任務;
和普通線程相比,可自動創建子線程來執行任務,且任務執行完畢後自動退出

工作原理

1.在服務onCreate中會首先創建一個HandlerThread的對象並開啓線程,之後通過傳入Looper對象的方式創建Handler,將Handler與HandlerThread綁定。
在這裏插入圖片描述
2.當調用onStartCommand方法後,會調用Handler的sendMessage(將intent對象放入message的obj字段中),將消息發送到MessageQueue中。Looper持續調用MessageQueue的next函數將msg取出,並找到msg對應的Handler。

3.handleMessage中包含兩個函數:onHandleIntent(intent),以及stopself(startId)

onHandleIntent方是具體處理Intent的異步請求,stopself是用於停止服務的

而無參數stopSelf方法時立刻停止服務,有參數stopSelf則不同,該方法會在嘗試停止服務之前會判斷啓動服務的次數與startId,是否相等。相等纔會停止服務,這樣做的好處在於可以當所有消息對處理之後才停止服務。

具體流程如下:
在這裏插入圖片描述
IntentService是一個抽象類,需要我們自己實現其中的onHandleIntent抽象方法,下面是一個實例。
在使用上,我們需要對Intent實例,然後進行intent.putExtra("task_action","具體任務")操作,最後在調用startService(intent)

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");
        Log.i(TAG,"receiver action:" + action);
        SystemClock.sleep(3000);
        if("com.lgl.test.ACTION".equals(action)){
            Log.i(TAG,"handler action:" + action);
        }
    }

    @Override
    public void onDestroy() {
        Log.i(TAG,"onDestroy");
        super.onDestroy();
    }
}

線程池

線程池的優點與構成

線程池的優點:
(1)可以重用線程池中的線程,避免線程的多次創建和銷燬以節省資源。
(2)可以控制線程池中的最大併發數,避免大量的線程之間因互相搶佔系統資源而導致阻塞現象。
(3)進行線程管理,提供定時/循環間隔執行等功能

線程池實現了Executor接口,Android中的一個具體實現是ThreadPoolExcutor,其中包含了一些參數。

//構造參數
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {

1.CorePoolSize核心線程數,空閒下的核心線程常規情況下是不會被銷燬的,但是可以設置一個boolean變量allowCoreThreadTimeOut,如果該變量爲true的情況下,核心線程空閒時間超過AliveTime就會被銷燬。

2.maxiumPoolSize:最大線程數,當活動線程超過這個數時,新任務會進入阻塞。

3.keepAliveTime非核心線程下的允許空閒時間,非核心線程如果超過該時間會銷燬該線程。當allowCoreThreadTimeOut爲true時,也會對核心線程有效。

4.workQueue任務隊列通過線程池的execute()方法提交的Runnable對象會存儲在這個參數中

5.threadFactory線程工廠。是一個抽象類,用於創建線程。其中只包含了一個方法Thread newThread(Runnable r)

6.handler當線程池無法執行新任務時進行調度,一般會拋出異常

ThreadPoolExecutor的工作原理:
當一個任務到來後,
如果當前所使用的線程數小於核心線程數,就直接開啓一個核心線程來執行這個任務;
否則噹噹前所使用的線程數大於等於核心線程數,則讓任務進入任務隊列等待;
如果任務隊列已經滿了的話,會啓動一個非核心線程;
如果當前所使用的線程數等於最大線程數,那麼就拒絕執行該任務,並通過Handler來拋出異常。

線程池的分類

1.FixThreadPool:線程數量固定的線程池,其中全部是核心線程,核心線程始終存活。
能快速響應外界請求。

2.:CachedThreadPool:該線程池全部爲非核心線程,最大線程量爲Integer.MAX_VALUE。時間超過60s線程會被回收。當整個線程池處於閒置狀態時,所有線程都會因爲超時而被回收,就變成了一個空的線程池,幾乎不會佔系統資源;
適合於執行大量的耗時短的任務。

3.ScheduledThreadPool:核心線程數量固定,非核心線程數量不固定,非核心線程只要處於空閒就會銷燬。
適合於定時任務和具有固定週期的任務。

4.SingeleThreadExcutor:只有一個核心線程,確保所有的任務都在一個線程中按順序進行
將外界任務統一到一個線程中,無需考慮線程同步問題。

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