AsyncTask非同步任務,或稱異步任務,是一個相當常用的類別,是專門用來處理背景任務與UI的類別。
Android 4.0 之後,有明文規定所有的網路行為都不能在主執行緒(Main Thread)執行,
主執行緒又稱UI執行緒(UI Thread),任何有關UI的東西都在主執行緒中執行,若是你的程式佔據主執行緒很久,使用者體驗會非常的差。
AsyncTask
想像一下,按了一個按鈕後,整個App停住五秒會是怎樣的感覺,因此許多耗時的程式建議寫在背景執行,而其中最常見的就是網路的功能。
在此先介紹一下有關ANR(Application Not Responding)的問題,也就是應用程式沒有回應。
你可以試著加入一個Button
,在onClick
事件裡面做一件很花時間的事情。
|
|
- 跑了一個很大的迴圈,過一下子你的手機會跑出這個警告。
這就是典型的ANR,因為onClick事件是在主執行緒,你佔據主執行緒太久的時間,因此跳出了這個警告,若是按下確定就會關閉你的程式。
要怎麼知道我是不是在主執行緒呢,你可以用以下的程式碼來判斷。Thread.currentThread().getId()`` 像是你在onClick裡面加入這個Log,
Log.d(“onClick = “ , String.valueOf(Thread.currentThread().getId()));` 然後試著印出來,你會發現他會寫1,1就是主執行緒的ID,也是UI Thread。 這樣你大概了解其中一種會產生ANR的問題。 接下來來談談
AsyncTask的用法吧,這次舉的例子是從網路下載圖片。 你可能會想,只是下載一張小圖片應該不會花費太久時間,那我寫在主執行緒就好了。 然後就在
onClick`裡面寫了以下的程式碼。
12345678910 try {URL url = new URL("http://i.imgur.com/Uki7N9T.jpg");//取得圖片的URLBitmap bitmap = BitmapFactory.decodeStream(url.openConnection().getInputStream());//透過BitmapFactory來下載URL的圖片imageView.setImageBitmap(bitmap);//設置圖片到ImageView之中} catch (IOException e) {e.printStackTrace();}
透過BitmapFactory
來下載圖片,在使用Try Catch
來捕捉一些可能的例外,看起來滿正確的,但是實際執行會發現有一個例外。
- android.os.NetworkOnMainThreadException
意思是,你不能在主執行緒做網路的事情,還是乖乖用AsyncTask吧XD
AsyncTask<Params
,Progress
,Result
>,這是基本的架構,使用泛型來定義參數,
泛型意思是,你可以定義任意的資料型態給他。
Params : 參數,你要餵什麼樣的參數給它。
Progress : 進度條,進度條的資料型態要用哪種
Result : 結果,你希望這個背景任務最後會有什麼樣的結果回傳給你。
此外,AsyncTask
會有四個步驟。
onPreExecute : 執行前,一些基本設定可以在這邊做。
doInBackground : 執行中,在背景做任務。
onProgressUpdate : 執行中,當你呼叫publishProgress的時候會到這邊,可以告知使用者進度。
onPostExecute : 執行後,最後的結果會在這邊。
拿下載圖片的例子來寫,繼承AsyncTask
,並實作四個步驟,
- 參數說明 : 丟入網址(String),進度條用整數(Integer),拿到圖片(Bitmap)
|
|
這邊你可能不明白String...
是什麼意思,這東西的意思是你可以傳單一一個String
,
或者是一個String
陣列都可以。
假如你丟一個String
進去,你只要取得第一個元素即可。
String urlStr = params[0];
接著在把剛才下載圖片的程式改寫到doInBackground
之中。
|
|
如此一來,你就會在背景下載圖片,當沒有例外的時候就會回傳。
此時,你可能會想,那我不要等回傳,我直接在doInBackground去改我的圖片就好了。
|
|
這時候會跳出一個例外:
- Only the original thread that created a view hierarchy can touch its views.
意思是,你只能在UI Thread去修改UI,因為你現在是在背景,因此你必須回到UI Thread才能對UI做事情,很勤勞的跑去Google找解,最後你的程式碼可能變成這樣。
|
|
事實上,這樣是可以運作沒錯,但是有點太多此一舉。
主執行緒沒辦法用網路 -> 用背景執行 -> 背景執行沒辦法改UI -> 在回去主執行緒。
我們有提到,AsyncTask
有四個步驟,我們試著將這四個步驟的執行緒ID都印出來。
|
|
除了背景任務以外都回到主執行緒了,因此你可以在結果的部分在對UI做修改,不用特定在背景那邊在呼叫回主執行緒,這樣太多此一舉了。
如此一來基本的認識應該有了,這裏提供兩個範例。
##範例##
傳入一個網址
1.傳入一個網址,下載網路圖片後顯示在
ImageView
之中
|
|
執行背景程式的方法
|
|
實作進度條的功能
模擬下載三張圖,可是下載完後沒有回傳圖片回去,這個例子是說明怎麼使用進度條。
|
|
當你呼叫publishProgress時,丟入一個值,會到onProgressUpdate之中,在這邊更新進度條的進度。
當完成之時,在呼叫dismiss將進度條除去。
看完這篇文章你應該對AsyncTask有一些基本的認識了。