當你第一次啟動一個Android程式的時候,一個被稱為"mian"的線程就被自動建立了。它被稱為主線程或者UI線程,它是非常重要的因為負責分發事件給對應的widget,還包含畫圖的事件。主線程貫穿使用者和Android widget的互動的整個過程。例如,你觸摸了螢幕上的按鈕(Button),UI線程派發(dispatch)觸摸(touch)事件給widget,widget設定為按下狀態並向事件隊列發送一個無效的請求。UI線程把這個請求彈出棧並且通知widget去重畫它自己。 單執行緒模式導致Android程式低效。因為每次單線程去執行長時間的操作如網路請求,資料庫查詢和drawing event(繪圖事件),在這個過程中會阻塞程式的介面(UI)。當長時間的任務正在執行的時候,沒有事件(Event)會被派發(dispatch),包括drawing event(繪圖事件)。從使用者的角度來看,改程式出現了終止。甚至更糟糕的是,如果UI程式被阻塞幾秒後(大約5s)就會出現臭名昭著的ANR
對話方塊。 如果想看一下有多糟糕,你可以寫一個簡單的帶有按鈕的程式,按鈕的點擊事件執行Thread.sleep(2000)代碼。該按鈕將會保持2秒的按下狀態然後恢複正常的狀態。這樣很容易讓使用者感到程式很慢。 現在你知道了一定避免在主線程中執行長時間的操作,你可能會使用額外的線程(後台線程或者背景工作執行緒)去執行操作。讓我們來看看點擊從網路下載圖片到ImageView裡的例子:
public void onClick(View v) { new Thread(new Runnable() { public void run() { Bitmap b = loadImageFromNetwork(); mImageView.setImageBitmap(b); } }).start();}首先,代碼看上去很好的解決我們的問題,因為它不會阻塞UI線程,不幸的是,它違背了單一執行緒模式:Android UI工具箱(toolkit)不是一個安全執行緒的,並且它總是被放在主線程上操作。這個ImageView被一個背景工作執行緒操作,這導致非常不可思議的問題。跟蹤和修複這樣一個bug很難並且也耗時。 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();}不幸的是,這些類和方法導致我們的代碼變得複雜和可讀性差。當你實現複雜的操作來頻繁的更新介面,使用這種方式變得更加糟糕。為瞭解決這個問題,Android1.5提供了一個公用類叫做AsyncTask,它簡化了任務線程和主線程之間的通訊。 在Android1.0和1.1也可使用AsyncTask只不過它的名字為UserTask。 AsyncTask的目的就是協助你管理線程。我們之前的例子很容易被改寫如下形式:
public void onClick(View v) { new DownloadImageTask().execute("http://example.com/image.png");}private class DownloadImageTask extends AsyncTask { protected Bitmap doInBackground(String... urls) { return loadImageFromNetwork(urls[0]); } protected void onPostExecute(Bitmap result) { mImageView.setImageBitmap(result); } }AsyncTask通過它的子類才能使用。要記住,一個AsyncTask執行個體必須在主線程建立並且只能被執行一次。完全理解和使用這個類,你可以閱讀AsyncTask文檔。這裡快速的說一下AsyncTask是怎麼工作的:
1>可以通過泛型指定它的類型:參數,進度值,任務的結果值。2>doInBackGround()方法自動在背景工作執行緒中只想能夠。3>onPreExecute(),onPostExecute(),onProgressUpdate()方法都在UI線程中執行。4>doInBackground()方法返回的值被當作參數傳遞給onPostExecute()方法。5>你能夠在doInBackground()方法裡任何時候調用publishProgress()方法在UI線程中去執行onProgressUpdate()方法。
除了官方文檔,你可以閱讀幾個複雜的例子原始碼如Shelves(ShelvesActivity.java和AddBookActivity.java)和Photostream(LoginActivity.java,PhotostreamActivity.java和ViewPhotoActivity.java)。我強烈建議閱讀Shelves的原始碼,看它在配置改變(configuration
changes)的時候是如何儲存任務的(persist task),當Activity銷毀的時候是怎樣取消任務的。不要管它是否使用AsyncTask,總之要記住單執行緒模式的兩個原則(rule):不要阻塞(block)UI線程;確保Android UI toolkit 只能在UI線程中被訪問(access)。AsyncTask使得做這些事情變得更簡單。如果你想學習更酷的技術,加入Google I/O(http://www.google.com/intl/zh-CN/events/io/2011/)。Android團隊的成員將在這裡進行一些列深層次技術的會議(http://www.google.com/intl/zh-CN/events/io/2010/sessions.html),並且回答你所有的問題。翻譯自 http://android-developers.blogspot.jp/2009/05/painless-threading.html