1. 程式人生 > >Android的UI設計與後臺執行緒互動

Android的UI設計與後臺執行緒互動

本文將討論Android應用程式的執行緒模型以及如何使用執行緒來處理耗時較長的操作,而不是在主執行緒中執行,保證使用者介面(UI)的流暢執行。

本文還將闡述一些使用者介面(UI)中與執行緒互動的API。

UI使用者介面執行緒
當應用程式啟動時,系統會為應用程式建立一個主執行緒(main)或者叫UI執行緒,它負責分發事件到不同的元件,包括繪畫事件。完成你的應用程式與Android UI元件互動。
例如,當您觸控式螢幕幕上的一個按鈕時,UI執行緒會把觸控事件分發到元件上,更改狀態並加入事件佇列,UI執行緒會分發請求和通知到各個元件,完成相應的動作。
單執行緒模型的效能是非常差的,除非你的應用程式相當的簡單,特別是當所有的操作都在主執行緒中執行,比如訪問網路或資料庫之類的耗時操作將會導致使用者介面鎖定,所有的事件將不能分發,應用程式就像死了一樣,更嚴重的是當超過5秒時,系統就會彈出“應用程式無響應”的對話方塊。
如果你想看看什麼效果,可以寫一個簡單的應用程式,在一個Button的OnClickListener中寫上Thread.sleep(2000),執行程式你就會看到在應用程式回到正常狀態前按鈕會保持按下狀態2秒,當這種情況發生時,您就會感覺到應用程式反映相當的慢。
總之,我們需要保證主執行緒(UI執行緒)不被鎖住,如果有耗時的操作,我們需要把它放到一個單獨的後臺執行緒中執行。

下面是一個點選按鈕後下載一個圖片,同時顯示到介面的ImageView上的例子:

 
public void onClick(View v) {  
  new Thread(new Runnable() {  
    public void run() {  
      Bitmap b = loadImageFromNetwork();  
      mImageView.setImageBitmap(b);  
    }  
  }).start();  
}  
起初,上面的程式碼似乎是一個很好的解決方案,因為它不會鎖住使用者介面執行緒。然面不幸的是,它違反了使用者介面單執行緒模型:Android的使用者介面工具包不是執行緒安全的,只能在UI執行緒中操作它,在上面的程式碼中,你在一個工作執行緒中呼叫mImageView.setImageBitmap(b)時,將會發生意想不到的錯誤,這種錯誤是非常難跟蹤和除錯的。Android提供了幾種方法來從其他執行緒訪問UI執行緒。您可能已經熟悉他們了,下面是一個較全面的列表:
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();  
} 
不幸的是,這些類和方法也往往使你的程式碼更復雜,更難以閱讀。更糟糕的是,它需要頻繁執行復雜的操作介面更新。為了解決這個問題,1.5和更高版本的Android平臺提供了一個實用類稱為AsyncTask,簡化了長時間執行的任務,需要與使用者介面的互動。
類似AsyncTask的一個類UserTask也可用於Android 1.0和1.1版本,它提供了完全相同的API,所有您需要做的是把它的原始碼複製到你的應用程式中。AsyncTask的目標是要為你的執行緒提供管理服務,我們前面的例子可以很容易的用AsyncTask來改寫:
public void onClick(View v) {  
  new DownloadImageTask().execute("http://www.ideasandroid.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類來使用它,非常重要的一點是:AsyncTask必須在UI執行緒中例項化它,並且只能執行一次。

以下是AsyncTask的簡要使用方法:

◆您可以指定三個引數型別,泛型引數,進度值(執行過程中返回的值)和最終值(執行完返回的值)。

◆該方法doInBackground()自動執行工作執行緒(後臺執行緒)

◆onPreExecute(),onPostExecute()和onProgressUpdate()都是在UI執行緒呼叫

◆由doInBackground返回的值()傳送到onPostExecute()

◆您可以在執行doInBackground()時呼叫publishProgress()然後在UI組程中執行onProgressUpdate()。

◆您可以從任何執行緒隨時取消任務不管你是否使用AsyncTask,時刻牢記單一執行緒模型的兩條規則:

1、不要鎖住使用者介面。2、確保只在UI執行緒中訪問Android使用者介面工具包中的元件。

AsyncTask只是可以讓你更容易地做這些事情。

本文非原創作品,如有不對的地方請指正!!