本文將討論Android應用程序的線程模型以及如何使用線程來處理耗時較長的操做,而不是在主線程中執行,保證用戶界面(UI)的流暢運行。本文還將闡述一些用戶界面(UI)中與線程交互的API。
UI用戶界面線程數據庫
當應用程序啓動時,系統會爲應用程序建立一個主線程(main)或者叫UI線程,它負責分發事件到不一樣的組件,包括繪畫事件。完成你的應用程序與Android UI組件交互。
例如,當您觸摸屏幕上的一個按鈕時,UI線程會把觸摸事件分發到組件上,更改狀態並加入事件隊列,UI線程會分發請求和通知到各個組件,完成相應的動做。
單線程模型的性能是很是差的,除非你的應用程序至關的簡單,特別是當全部的操做都在主線程中執行,好比訪問網絡或數據庫之類的耗時操做將會致使用戶界面鎖定,全部的事件將不能分發,應用程序就像死了同樣,更嚴重的是當超過5秒時,系統就會彈出「應用程序無響應」的對話框。
若是你想看看什麼效果,能夠寫一個簡單的應用程序,在一個Button的OnClickListener中寫上Thread.sleep(2000),運行程序你就會看到在應用程序回到正常狀態前按鈕會保持按下狀態2秒,當這種狀況發生時,您就會感受到應用程序反映至關的慢。
總之,咱們須要保證主線程(UI線程)不被鎖住,若是有耗時的操做,咱們須要把它放到一個單獨的後臺線程中執行。
下面是一個點擊按鈕後下載一個圖片,同時顯示到界面的ImageView上的例子:安全
1 public void onClick(View v) 2 { 3 new Thread(new Runnable() 4 { 5 public void run() 6 { 7 Bitmap b = loadImageFromNetwork(); 8 mImageView.setImageBitmap(b); 9 } 10 }).start(); 11 }
起初,上面的代碼彷佛是一個很好的解決方案,由於它不會鎖住用戶界面線程。然面不幸的是,它違反了用戶界面單線程模型:Android的用戶界面工具包不是線程安全的,只能在UI線程中操做它,在上面的代碼中,你在一個工做線程中調用mImageView.setImageBitmap(b)時,將會發生意想不到的錯誤,這種錯誤是很是難跟蹤和調試的。
Android提供了幾種方法來從其餘線程訪問UI線程。您可能已經熟悉他們了,下面是一個較全面的列表:網絡
1 Activity.runOnUiThread(Runnable) 2 View.post(Runnable) 3 View.postDelayed(Runnable, long) 4 Handler
您可使用這些類和方法中的任何一種糾正前面的代碼示例:工具
1 public void onClick(View v) 2 { 3 new Thread(new Runnable() 4 { 5 public void run() 6 { 7 final Bitmap b = loadImageFromNetwork(); 8 mImageView.post(new Runnable() 9 { 10 public void run() 11 { 12 mImageView.setImageBitmap(b); 13 } 14 }); 15 } 16 }).start(); 17 }
不幸的是,這些類和方法也每每使你的代碼更復雜,更難以閱讀。更糟糕的是,它須要頻繁執行復雜的操做界面更新。
爲了解決這個問題,1.5和更高版本的Android平臺提供了一個實用類稱爲AsyncTask,簡化了長時間運行的任務,須要與用戶界面的交互。
相似AsyncTask的一個類UserTask也可用於Android 1.0和1.1版本,它提供了徹底相同的API,全部您須要作的是把它的源代碼複製到你的應用程序中。
AsyncTask的目標是要爲你的線程提供管理服務,咱們前面的例子能夠很容易的用AsyncTask來改寫:post
1 public void onClick(View v) 2 { 3 new DownloadImageTask().execute("http://blog.92coding.com/404.gif"); 4 } 5 private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> 6 { 7 protected Bitmap doInBackground(String... urls) 8 { 9 return loadImageFromNetwork(urls[0]); 10 } 11 12 protected void onPostExecute(Bitmap result) 13 { 14 mImageView.setImageBitmap(result); 15 } 16 }
正如你所看到的,咱們必須經過繼承AsyncTask類來使用它,很是重要的一點是:AsyncTask必須在UI線程中實例化它,而且只能執行一次。
如下是AsyncTask的簡要使用方法:
◆您能夠指定三個參數類型,泛型參數,進度值(執行過程當中返回的值)和最終值(執行完返回的值)。
◆該方法doInBackground()自動執行工做線程(後臺線程)
◆onPreExecute(),onPostExecute()和onProgressUpdate()都是在UI線程調用
◆由doInBackground返回的值()發送到onPostExecute()
◆您能夠在執行doInBackground()時調用publishProgress()而後在UI組程中執行onProgressUpdate()。◆您能夠從任何線程隨時取消任務
無論你是否使用AsyncTask,時刻牢記單一線程模型的兩條規則:
一、不要鎖住用戶界面。
二、確保只在UI線程中訪問Android用戶界面工具包中的組件。
AsyncTask只是可讓你更容易地作這些事情。性能