便於使用線程(Painless Threading)
這篇文章介紹了Android的執行緒模式和AsyncTask等內容,值得一看。以下的翻譯的內容:
此文章討論的是用於Android應用程式的執行緒模式,和應用程式怎樣利用Worker Thread代替主線程執行長時間的操作,用以確保最好的UI表現。該文章同樣解釋了應用程式在主線程與worker Thread中和Android UI工具包組件互動的相關API。
UI線程(The UI Thread)
當應用程式啟動並執行時候,系統會為應用程式建立一個主線程。它同樣被稱作UI 線程,它管理著指派事件給合適的組件,包括繪製事件。同樣它也是你的應用程式和Android UI 控制項互動的線程。
例如:當你觸控螢幕幕上的按鈕,UI線程會指派觸摸事件給按鈕,設定按下狀態和發送一個更新要求到事件隊列。UI線程讓請求出隊列並且通知群組件重畫自己。
如果你的應用程式設計不合理 單執行緒模式會造成極低的效率。特別的,如果任何事都在一個線程中執行,在UI線程執行長時間的操作例如網路連接和資料查詢會阻塞整個使用者介面。這樣會造成沒有事件會被分發,包括繪製事件。使用者會認為程式出現了掛載。甚至更糟,UI線程阻塞超過大概5秒就會出現臭名昭著的”application not responding”(ANR)對話方塊。
如果你想看看情況有多壞,寫一個簡單的應用程式,繪製一個button,在點擊事件裡調用Thread.sleep(2000)。點擊按鈕,它會在按下狀態停留了2秒再回到正常狀態。這樣會讓使用者感覺到應用程式反應很慢。
做個總結:保證UI線程不被阻塞與你應用程式流程暢性關係重大。你應該確保長時間的操作運行在其他的線程(background or worker threads)。
下面是一個下載圖片然後呈現在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在worker線程中被操作會導致很多奇怪的問題。跟蹤和改正這樣的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,簡化了長時間需要和使用者介面交流的任務。
在Android1.0和1.1中UserTask的作用相當於AsyncTask。
AsyncTask的目標是協助你更輕鬆的管理線程。上一個例子可以利用AsyncTask重寫:
public void onClick(View v) {
new DownloadImageTask().execute("http://www.bkjia.com/uploadfile/2011/1013/20111013014500861.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()方法是在workerthread中自動調用的
onPreExecut(),onPostExecut()和onProgressUpdate()是在UI線程中調用的
doInBackground()的傳回值傳遞給onPostExecute()
你可以在doInBackground()中任何時間調用publishProgress()方法使得在UI線程中執行onProgressUpdate()方法
你能夠在任何線程和任何時候取消任務
在官方文檔中,你可以閱讀幾個複雜的例子的原始碼,如:Shelves(ShelvesActivity.java 和AddBookActivity.java) 和 Photostream(LoginActivity.java,PhototstreamActivity.java和ViewPhotoActivity.java).強烈建議閱讀Shelves的原始碼來學習如何配置改變中保持任務和如何在activity銷毀時適當的取消它們。
無論你是否使用AsyncTask,記住單執行緒模式的兩個規則:
1:不要阻塞UI線程
2:確保只在UI線程中訪問AndroidUI控制項
AsyncTask使這兩項變的更簡單去做。
以上就是全部內容,歡迎大家和我交流。
摘自:ldj299的專欄