Android執行緒模式

來源:互聯網
上載者:User

Android中的執行緒模式

這篇文章將討論Android應用程式中使用的執行緒模式,並討論如何確保應用程式最佳的UI呈現(通過建立工作者線程來處理耗時的操作,而不是在主線程裡處理)。這篇文章還將闡述與運行在主線程中的UI組件互動的API以及建立託管的工作者線程的API。

UI線程

當應用程式啟動後,系統建立了一個叫做“main”的線程。主線程,也叫UI線程,非常重要,因為它負責分發事件給構件,包括繪製事件。也是這個線程,在這裡才能與Android UI工具包中的組件進行互動。

例如,當你觸控螢幕幕上的一個按鈕時,UI線程會分發一個觸摸事件給構件,然後,構件會設定自己為被按下的狀態,並拋出一個顯示無效的請求給事件隊列。UI線程隊列請求並通知構件繪製自己。

單執行緒模式會導致效能低下,除非你的程式很好地實現。特別是,當所有的操作都在單一的線程中進行,耗時的操作(如網路訪問、資料查詢)會阻塞UI。在耗時操作執行時,沒有任何事件可以分發,包括繪製的事件。從使用者的視覺來看,應用程式被掛起了。更糟糕的是,如果UI線程阻塞超過一定的時間(現在大約是5秒鐘),系統會給使用者呈現一個糟糕的“應用程式無響應”(ANR)對話方塊。

如果你想看這有多糟糕,你可以寫一個簡單的應用程式,在一個Button的OnClickListener函數中調用Thread.sleep(2000)。按鈕在回到它正常狀態之前,保持被按下的狀態2秒鐘。當這種情況發生時,使用者很容易認為應用程式慢。

總之,對於應用程式UI的響應性來說,保證UI線程不被阻塞是至關重要的。如果你有耗時的操作,你應該確保在另外的線程(後台或工作者線程)中執行。

下面有一個例子,點擊事件處理函數中,從網路上下載一個圖片,並顯示到ImageView上:

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

乍一看,這段代碼能很好的解決你的問題,因為它不會阻塞UI線程。遺憾的是,它違背了UI的單執行緒模式:Android UI工具包不是安全執行緒的,必須在UI線程中進行操作。在上面的程式碼片段裡,ImageView是在工作者線程中操作的,因此,這會引發可拍的問題。跟蹤和修正這些Bug可能是困難且耗時的。

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

不幸的是,這些類和方法可能會使你的代碼變得更加複雜並難以閱讀。特別是,當你實現一個複雜的操作,而在這個操作中,需要頻繁地更新UI。

為瞭解決這個問題,Android 1.5和它之後的平台提供了一個通用的類——AsyncTask,其簡化了長時間運行任務的建立過程,而這些任務還能做到與UI進行互動。

在Android 1.0和1.1上,也有與AsyncTask類似的東西,叫做UserTask。它提供了相同的API,而你需要做的只是拷貝其中的代碼。

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必須繼承使用。此外,還必須記住的是AsyncTask的執行個體必須在UI線程中建立並只能執行一次。你可以閱讀AsyncTask的文檔來全面瞭解如何使用,但這裡,只是簡要地描述它以及它的工作過程:

· 你可以使用泛型來指定參數、進度值和最終結果的類型

· doInBackground() 方法自動在工作者線程中執行

· onPreExecute(),onPostExecute()和onProgressUpdate()在主線程中調用

· doInBackground()中返回的值會發送給onPostExecute()方法

· 你可以在doInBackground()中隨時調用publishProgress()來執行onProgressUpdate()

· 你可以任何時候從任何線程中取消任務

除了官方的文檔,你還可以參考幾個複雜例子的原始碼,如Shelves(ShelvesActivity.java和AddBookActivity.java)和Photostream(LoginActivity,PhotostreamActivity.java和ViewPhotoActivity.java)。我們強烈地建議你閱讀Shelves的原始碼,來瞭解配置變更時任務的儲存以及Activity銷毀時如何正確地取消任務。

不管你是否使用AsyncTask,在單執行緒模式中始終要記住兩條法則:

1. 不要阻塞UI線程

2. 確保只在UI線程中訪問Android UI工具包

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

Tags Index: