線程的改進--------AsyncTask 介紹

當android應用程序啓動的時候,android爲應用程序開啓了一個線程,稱爲主線程也稱UI線程.這個線程負責分發用戶響應並與用戶進行交互.

如果在應用程序中只有主線程一個線程運行,在有些情況下會出現問題:比如請求網絡數據,與數據庫進行交互等一些比較耗時的操作進行時,此時,主線程被阻塞,任何消息都不能發送出去.從用戶的角度上來看,應用程序被掛起了.更糟糕的是,如果UI線程被阻塞5秒以上就會出現ANR現象.

在這種情況下,我們可以考慮開啓額外的線程,在線程裏面處理耗時操作.考慮下面的代碼:

public void onClick(View v) {
 
new Thread(new Runnable() {
   
public void run() {
     
Bitmap b = loadImageFromNetwork();
      mImageView
.setImageBitmap(b);
   
}
 
}).start();
}

 在onClick方法裏面下載網絡圖片,並設置到本地的ImageView.

這看起來是一個不錯的處理方案,並沒有阻塞UI線程,但是由於在UI線程裏面修改主線程裏面的資源,這是線程不安全的.

android提供了以下幾種方法在其他線程中訪問主線程:

Activity.runOnUiThread(Runnable)

View.post(Runnable)

View.postDelayed(Runnable, long)

Handler

以上代碼可以修改成:

public void onClick(View v) {
 
new Thread(new Runnable() {
   
public void run() {
     
final Bitmap b = loadImageFromNetwork();
      mImageView
.post(new Runnable() {
       
public void run() {
          mImageView
.setImageBitmap(b);
       
}
     
});
   
}
 
}).start();
}

但不幸的是,這些方法和類會降低代碼的可讀性.並且當我們需要進行復雜的操作和頻繁進行UI更新時,這樣的方法會變得更糟糕.

爲了彌補這樣的缺陷,android1.5版本之後,提供了AsyncTask類,這個類提供了一個長時間運行的任務並,其中提供了與用戶進行交互的接口.

AsyncTask 可以替你進行線程管理.我們可以把上面的例子進行以下改寫:

public void onClick(View v) {
 
new DownloadImageTask().execute("http://example.com/image.png");
}

private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
     
protected Bitmap doInBackground(String... urls) {
         
return loadImageFromNetwork(urls[0]);
     
}

     
protected void onPostExecute(Bitmap result) {
         mImageView
.setImageBitmap(result);
     
}
 
}

AsyncTask 必須要通過繼承後才能使用.以下幾點需要注意

1.一個AsyncTask實例必須要創建在UI線程上,並僅僅只執行一次,

2.doInBackground方法是自動在用戶線程被執行的,而onPreExecute(), onPostExecute()和 onProgressUpdate() 回調方法被 UI 線程調用.

3.doInBackground()的返回值被髮送到 onPostExecute()

4.任意時刻在doInBackground()裏,我們可以通過調用所有的publishProgress() 使UI線程執行onProgressUpdate()方法

5.你可以在任意時間任意線程結束任務

一個小例子:

首先定義類DownloadFilesTask 繼承自AsyncTask,AsyncTask中傳入的三個參數類型URL, Integer, Long分別與三個回調方法的參數類型對應.

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

此時傳入的三個參數被doInBackground獲取到,它會調用publishProgress,這個方法裏面可以傳多個參數,publishProgress會觸發onProgressUpdate,該方法取出了第一個參數.doInBackground方法會返回一個Long 類型的totalSize,這個作爲參數傳入onPostExecute.

1.任務執行後,onPreExecute()方法馬上被主線程喚醒,

2.onPreExecute()方法執行完後,doInBackground(Params...)方法馬上被其所在的線程喚醒,

3.在後臺線程計算結束後,onPostExecute(Result)方法立即被主線程喚醒.

4. cancel(boolean)方法可以在任意時間結束任務,該方法可以導致 isCancelled() 返回true.此時,doInBackground(Object[])調用結束後會調用onCancelled(Object)方法,爲了保證任務儘快的結束,需要週期性的在doInBackground內部檢查isCancelled()的返回值,如果可能的話,使用looper

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