AsyncTask與Thread+Handler簡要分析

gamebus發表於2021-09-09

   !個人研究探索整理

非同步處理

目的:提高程式響應性

體現:

耗時操作

網路資料(圖片)載入

資料庫查詢

複雜業務邏輯處理

開發中涉及UI的操作必須遵守單執行緒模型的原則,因為Android的UI操作並非執行緒安全,並且這些操作必須在UI執行緒中執行。

單執行緒模型的兩條法則:

1. 不要阻塞UI執行緒

2. 確保只在UI執行緒中訪問Android UI工具包

 

幾種其他執行緒訪問主執行緒的方法

[程式碼]java程式碼:

1

2

3

4

5

6

7

1.Activity.runOnUiThread( Runnable   )  

 

2.View.post( Runnable )  

 

3.View.postDelayed( Runnable, long )  

 

4.Hanlder

 

當需要實現一些很複雜的操作並需要頻繁地更新UI時以上的方法會變的很繁瑣。 所以,Android 1.5提供了一個工具類:AsyncTask,方便處理長時間執行的任務,並操作UI。相對來說AsyncTask更輕量級一些,不需要藉助執行緒和Handler即可實現簡單的非同步處理。 但AsyncTask也並不完美,如當網路情況較差,非同步任務不能儘快完成執行的情況下,新開的執行緒會造成介面不流暢,當開啟的執行緒過多時,還可能出現FC。

 

AsyncTask

特點:

AsyncTask的規範型很強,能夠時時反映更新的情況,Task在主執行緒之外執行,回撥方法在主執行緒執行。

內部實現原理:

本質是一個執行緒池,所有提交的非同步任務都會在這個執行緒池中的工作執行緒內執行,當工作執行緒需要跟UI執行緒互動時,工作執行緒會傳遞訊息到UI執行緒建立的Handler,由Handler呼叫相關的回撥方法,實現UI介面的更新。

成員變數:

[程式碼]java程式碼:

01

02

03

04

05

06

07

08

09

10

11

12

13

private static final int CORE_POOL_SIZE = 5;                      //5個核心工作執行緒

 

    private static final int MAXIMUM_POOL_SIZE = 128;              //最多128個工作執行緒

 

    private static final int KEEP_ALIVE = 10;                           //空閒時執行緒的超市時間為10s

 

    private static final BlockingQueue   sWorkQueue =

 

            new LinkedBlockingQueue(10);               //等待佇列

 

private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,

 

            MAXIMUM_POOL_SIZE,   KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue,   sThreadFactory);

 

//執行緒池是靜態變數,所有的非同步任務都會放到這個執行緒池的工作執行緒中執行

 

我們在new AsyncTask();例項化的時候,會呼叫父類的AsyncTask()建構函式

建構函式:

建構函式引數:三種泛型型別

1.Params:傳遞給後臺任務的引數型別,比如HTTP請求的URL

2.Progress:後臺計算執行過程中,進度單位的型別(就是後臺程式已經執行了百分之幾了)。

3.Result:後臺執行返回的結果的型別,比如String,List等

通常我們使用Void標識不使用的型別

任務的當前狀態:

在一個任務的生命週期內,每個狀態只會設定一次

PENDING  尚未執行

RUNNING 正在執行

FINISHED 表示onPostExecute已經完成

後臺執行緒執行的五個狀態:

1、準備執行,2、正在後臺執行,3、進度更新,4、完成後臺任務,5、取消任務

對應的回撥方法(我們需要做的就是實現這些方法):

1、準備執行:onPreExecute(),該回撥方法在任務被execute()提交到執行緒池之後立即由UI執行緒呼叫。這個步驟通常用來做一些準備工作,在UI上顯示進度條等。

2、正在後臺執行:doInBackground(Params...),該回撥方法由後臺執行緒在onPreExecute()方法執行結束後立即呼叫。通常在這裡執行耗時的後臺計算,計算的結果必須由該方法返回,並被傳遞到onPostExecute()中。在該方法內也可使用publishProgress(Progress...)來釋出一個或多個進度單位(units of progress),這些值將會在onProgressUpdate(Progress...)中被髮布到UI執行緒,該方法為抽象方法,子類必須實現。

3. 進度更新:onProgressUpdate(Progress...),該方法由UI執行緒在publishProgress(Progress...)方法呼叫完後被呼叫,一般用於動態地顯示一個進度條。

4. 完成後臺任務:onPostExecute(Result),當後臺計算結束後呼叫。後臺計算的結果會被作為引數傳遞給該方法。

5、取消任務:onCancelled (),執行在UI執行緒,在AsyncTask的cancel()方法後執行,可在取消執行緒操作時呼叫

 

要取消正在執行的任務時:檢查asynctask狀態,可以為RUNNING, FINISHED,PENDING ,只能在非FINISHED狀態取消

示例:

主執行緒中:

[程式碼]java程式碼:

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

private AsyncTask asynctask;

 

//啟動新的執行緒

 

asynctask= new AsyncTask();// 例項化

 

asynctask.execute();// 將task放入執行緒池,執行task

 

//如果Task還在執行,則先取消它

 

if (asynctask!= null && asynctask.getStatus() != AsyncTask.Status.FINISHED) {

 

asynctask.cancel(true);

 

}

 

protected Void doInBackground(Void... params) {

 

// Task被取消了,馬上退出迴圈

 

if(isCancelled()){//如果task在正常完成前被取消則返回ture

 

 return null;

 

}

 

}

 

protected void onProgressUpdate(Void...   values) {

 

super.onProgressUpdate(values);

 

//Task被取消了,不再繼續執行後面的程式碼

 

if(isCancelled()){

 

return;

 

}

 

}

 

AsyncTask執行過程

 onPreExecute(),當任務執行之前開始呼叫此方法,可以不用實現。 doInBackground(Params…) 此方法在後臺執行緒執行,完成任務的主要工作,通常需要較長的時間。在執行過程中呼叫 publicProgress(Progress…)更新任務的進度。 onProgressUpdate(Progress…) 此方法在主執行緒執行,用於顯示任務執行的進度。 onPostExecute(Result) 此方法在主執行緒執行,任務執行的結果作為此方法的引數返回

當任務的狀態發生改變時(1、執行成功2、取消執行3、進度更新),工作執行緒會向UI執行緒的Handler傳遞訊息。Handler要處理其他執行緒傳遞過來的訊息。在AsyncTask中,InternalHandler是在UI執行緒上建立的,它接收來自工作執行緒的訊息,並呼叫相關的回撥函式。

AsyncTask的呼叫規則:

 1.必須在UI執行緒中建立Task例項

 2.必須在UI執行緒執行execute

 3.不能手動呼叫onPreExecute()、onPostExecute、doInBackground、onProgressUpdate

 4.只能呼叫一次execute,如果嘗試重複呼叫,則會丟擲異常( 當任務正在執行或者已經完成,會丟擲IllegalStateException)

 

AsyncTask總結:

1、 AsyncTask的本質是一個靜態的執行緒池,AsyncTask派生出的子類可以實現不同的非同步任務,這些任務都是提交到靜態的執行緒池中執行。

2、執行緒池中的工作執行緒執行doInBackground(mParams)方法去執行非同步任務

3、當任務狀態改變之後,工作執行緒會向UI執行緒傳送訊息,AsyncTask內部的InternalHandler接收這些訊息,並呼叫相關的回撥函式進行處理

 

Thread+handler 非UI執行緒更新介面

本質是子執行緒傳送訊息到UI執行緒,通知UI執行緒進行介面更新,由UI執行緒中的handler物件負責相應的處理。

需注意:不能直接在Thread中操作UI,如

[程式碼]java程式碼:

1

2

3

4

5

6

7

8

9

new Thread(new Runnable){

 

public void run(){

 

view.invalidate();//違背了單執行緒模型

 

}

 

}.start();

 

Android採用UI單執行緒模型,所以只能在主執行緒中對UI元素進行操作。如果在非UI執行緒直接對UI進行了操作,

報錯:CalledFromWrongThreadException:only the original thread that created a view hierarchy can touch its views

此時,我們可以使用handler物件的post(Runnable)方法傳送一個Runnable物件到主執行緒中,Handler的handleMessage方法實際是由關聯有該訊息佇列的UI thread呼叫,因此,並不違反單執行緒模型

如:

[程式碼]java程式碼:

01

02

03

04

05

06

07

08

09

10

11

12

13

    handler.post(new Runnable() {

 

@Override

 

public void run()   {

 

// TODO Auto-generated method stub

 

view.invalidate();  //在與handler相關聯的UI執行緒中操作UI

 

}

 

});



Thread : Thread.start()方法,開啟新的執行緒,當前執行緒(主執行緒)併發執行;run()方法會被新開啟的執行緒執行

   Thread.run(),用於封裝執行緒執行的任務程式碼,直接用建立的執行緒物件呼叫,並沒有產生新的執行緒,在當前正在執行的執行緒(如,主執行緒)執行run方法

Handler:接收子執行緒傳送的物件,並負責處理該物件,用來更新介面,連線其他執行緒與主執行緒的橋樑,實現執行緒間通訊。提供非同步訊息處理機制,包含兩個佇列,一個是執行緒佇列,一個是訊息佇列。使用post方法將執行緒物件新增到執行緒佇列中,使用sendMessage(message)方法將訊息新增到訊息佇列中。

非UI執行緒傳送訊息到UI執行緒分為兩部:

子執行緒傳送訊息到UI執行緒的訊息佇列

處理傳送到UI執行緒的訊息,子類化handler,實現public void handleMessage (Message msg)方法

Handler中分發訊息的方法:(long物件為時間)

[程式碼]java程式碼:

01

02

03

04

05

06

07

08

09

10

11

12

13

post(Runnable) //立即執行runnable物件

 

       postAtTime(Runnable,long)     //指定時間執行

 

       postDelayed(Runnable   long)  //延時執行

 

       sendEmptyMessage(int)     //傳送只包含what值的訊息

 

       sendMessage(Message)      //立即傳送

 

       sendMessageAtTime(Message,long)   //指定時間傳送

 

       sendMessageDelayed(Message,long)   //延時傳送

 

 以上post類方法傳送一個Runnable物件到主執行緒佇列中,sendMessage類方法, 傳送一個Message物件到佇列中,等待UI執行緒處理,

使用post方法將Runnable物件放到Handler的執行緒佇列中,該Runnable的執行其實並未單獨開啟執行緒,而是仍然在當前Activity的UI執行緒中執行,Handler只是呼叫了Runnable物件的run方法。

handler:預設執行在當前的Looper物件中,一個Looper只有在處理完一個message物件後,才會處理下一個,因此,如需併發處理,可考慮使用不同的Looper

message與runnable:

message是訊息,可以傳遞一些引數,Handler獲取這些資訊並做出處理,而Runnable則是直接給出處理的方法

停止執行緒:呼叫Handler物件的removeCallbacks(Runnable r)從執行緒佇列中移除執行緒物件,使執行緒停止執行

清除訊息:Handler物件的handler.removeMessages(what)/handler.removeMessages(what,object)

當我們不清楚在UI執行緒還是在其他執行緒執行的時候,可以使用runOnUiThread(action);//....

Thread+handler於AsyncTask

兩者的區別在於:

AsyncTask可以透過實現onPostExecute方法直接操作UI,該方法在UI執行緒呼叫,耗時操作在後臺執行緒doInBackground,不適合無限的迴圈

Thread.start(),開啟一個新的執行緒與UI執行緒並行,並不能直接操作UI(由於單執行緒模型的原因),因此,傳送handler物件的

message/runnable物件到訊息佇列/主執行緒佇列中,由handler接收訊息並進行處理,而runnable物件則直接進行處理

原文連結:http://www.apkbus.com/blog-35555-68766.html

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4687/viewspace-2813698/,如需轉載,請註明出處,否則將追究法律責任。

相關文章