2015年4月11日 星期六

【Android】AsyncTask - Thread 外的另一選擇

轉載 http://oldgrayduck.blogspot.tw/2013/01/androidasynctask-thread.html 
 
AsyncTask (API level 3,所以幾乎所有目前在市面上流通的 Android 版本皆可使用)
是除 Thread 外的另一種選擇,Android 團隊鼓勵主執行緒(UI thread) 專注於操作 & 畫面的流暢呈現,
其餘工作 (如網路資料傳輸、檔案/磁碟/資料存取) 最好都在背景執行;
Thread 通常要搭配 Handler 使用,而 AsyncTask 用意在簡化背景執行 thread 程式碼的撰寫。

如果您預期要執行的工作能在幾秒內完成,就可以選擇使用 AsyncTask,若執行的時間很長,
Android 則強烈建議採用 Executor, ThreadPoolExecutor and FutureTask

要使用 AsyncTask,必定要建立一個繼承自 AsyncTask 的子類別,並傳入 3 項資料:
Params -- 要執行 doInBackground() 時傳入的參數,數量可以不止一個
Progress -- doInBackground() 執行過程中回傳給 UI thread 的資料,數量可以不止一個
Rsesult -- 傳回執行結果,
若您沒有參數要傳入,則填入 Void (注意 V 為大寫)。

AsyncTask 的運作有 4 個階段:

onPreExecute -- AsyncTask 執行前的準備工作,例如畫面上顯示進度表,
doInBackground -- 實際要執行的程式碼就是寫在這裡,
onProgressUpdate -- 用來顯示目前的進度,
onPostExecute -- 執行完的結果 - Result 會傳入這裡。
除了 doInBackground,其他 3 個 method 都是在 UI thread 呼叫
官方範例:
?
1
2
3
4
5
6
7
8
9
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
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long>
{
    // 對照前面提到的 3 個傳入的參數
    // URL 就是 Params 參數的類別
    // Integer 就是 Progress 參數的類別
    // Long 就是 Result 參數的類別
    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() 以更新 UI 畫面,
            // 可藉由此方式更新畫面上的進度表
            publishProgress((int) ((i / (float) count) * 100));
            // Escape early if cancel() is called
            if (isCancelled())
                break;
        }
        // 將 totalSize 傳給 onPostExecute()
        return totalSize;
    }
 
    protected void onProgressUpdate(Integer... progress)
    {
        // 這裡接收傳入的 progress 值, 並更新進度表畫面
        // 參數是 Integer 型態的陣列
        // 但是因為在 doInBackground() 只傳一個參數
        // 所以以 progress[0] 取得傳入參數
        setProgressPercent(progress[0]);
    }
 
    protected void onPostExecute(Long result)
    {
        showDialog("Downloaded " + result + " bytes");
    }
}


根據上面的範例,我們要建立一個名為 DownloadFilesTask 的類別,做為下載檔案用,
URL 就是將來會傳入 doInBackground 的變數型態,再看 doInBackground() 這個 method 傳入參數寫法
doInBackgound(URL... urls)
表示傳入的 url 可以不止 1 個。
一旦建立好類別,要執行的方法很簡單:

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

則 DownloadFilesTask 便會經由 execute() 呼叫 doInBackground(),執行下載 url1, url2, url3 這 3 個檔案,
而 doInBackground() 在處理過程中, 透過呼叫 publishProgress() 來傳送資料給
 onProgressUpdate (),onProgressUpdate() 更新畫面上的進度表(如果您有在您的 app UI 顯示進度表的話),
doInBackground() 執行完畢後,會將結果傳給 onPostExecute()。
關於 AsyncTask 的使用,有幾項原則必須遵守:
 
* AsyncTask 必須在 UI 主執行緒載入. (JELLY_BEAN 版本開始會自動執行此事).
* 必須在 UI 主執行緒建立 AsyncTask.
* 必須在 UI 主執行緒呼叫 AsyncTask.execute().
* 不要自行呼叫 onPreExecute(), onPostExecute(), doInBackground(), onProgressUpdate().
* AsyncTask 只能執行一次.




參考資料 ----
http://developer.android.com/reference/android/os/AsyncTask.html 

沒有留言:

張貼留言