天天看點

android之背景線程(UI與線程互動)

本文将讨論android應用程式的線程模型以及如何使用線程來處理耗時較長的操作,而不是在主線程中執行,保證使用者界面(UI)的流暢運作。本文還将闡述一些使用者界面(UI)中與線程互動的API。

UI使用者界面線程

當應用程式啟動時,系統會為應用程式建立一個主線程(main)或者叫UI線程,它負責分發事件到不同的元件,包括繪畫事件。完成你的應用程式與android UI元件互動。

例如,當您觸摸螢幕上的一個按鈕時,UI線程會把觸摸事件分發到元件上,更改狀态并加入事件隊列,UI線程會分發請求和通知到各個元件,完成相應的動作。

單線程模型的性能是非常差的,除非你的應用程式相當的簡單,特别是當所有的操作都在主線程中執行,比如通路網絡或資料庫之類的耗時操作将會導緻使用者界面鎖定,所有的事件将不能分發,應用程式就像死了一樣,更嚴重的是當超過5秒時,系統就會彈出“應用程式無響應”的對話框。

如果你想看看什麼效果,可以寫一個簡單的應用程式,在一個Button的OnClickListener中寫上Thread.sleep(2000),運作程式你就會看到在應用程式回到正常狀态前按鈕會保持按下狀态2秒,當這種情況發生時,您就會感覺到應用程式反映相當的慢。

總之,我們需要保證主線程(UI線程)不被鎖住,如果有耗時的操作,我們需要把它放到一個單獨的背景線程中執行。

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

<SPAN>public</SPAN> <SPAN>void</SPAN> onClick<SPAN> 

(</SPAN><SPAN>View</SPAN> v<SPAN>)</SPAN> <SPAN>{</SPAN> 

 <SPAN>new</SPAN> <SPAN>Thread</SPAN><SPAN> 

(</SPAN><SPAN>new</SPAN> <SPAN>Runnable</SPAN><SPAN> 

(</SPAN><SPAN>)</SPAN> <SPAN>{</SPAN>   

<SPAN>public</SPAN> <SPAN>void</SPAN> run<SPAN> 

(</SPAN><SPAN>)</SPAN> <SPAN>{</SPAN> Bitmap b   

<SPAN>=</SPAN> loadImageFromNetwork<SPAN>(</SPAN><SPAN>)  

</SPAN><SPAN>;</SPAN>   

mImageView.<SPAN>setImageBitmap</SPAN><SPAN> 

(</SPAN>b<SPAN>)</SPAN><SPAN>;</SPAN> <SPAN>}</SPAN>   

<SPAN>}</SPAN><SPAN>)</SPAN>.<SPAN>start</SPAN><SPAN> 

(</SPAN><SPAN>)</SPAN><SPAN>;</SPAN> <SPAN>}</SPAN>  

起初,上面的代碼似乎是一個很好的解決方案,因為它不會鎖住使用者界面線程。然面不幸的是,它違反了使用者界面單線程模型:android的使用者界面工具包不是線程安全的,隻能在UI線程中操作它,在上面的代碼中,你在一個工作線程中調用mImageView.setImageBitmap(b)時,将會發生意想不到的錯誤,這種錯誤是非常難跟蹤和調試的。

android提供了幾種方法來從其他線程通路UI線程。您可能已經熟悉他們了,下面是一個較全面的清單:

Activity.<SPAN>runOnUiThread</SPAN><SPAN>  

(</SPAN><SPAN>Runnable</SPAN><SPAN>)</SPAN>   

<SPAN>View</SPAN>.<SPAN>post</SPAN><SPAN>  

<SPAN>View</SPAN>.<SPAN>postDelayed</SPAN><SPAN>  

(</SPAN><SPAN>Runnable</SPAN>, <SPAN>long</SPAN><SPAN>)  

</SPAN> Handler    

您可以使用這些類和方法中的任何一種糾正前面的代碼示例:

<SPAN>public</SPAN> <SPAN>void</SPAN> onClick<SPAN>(</SPAN><SPAN>View</SPAN> v<SPAN>)</SPAN> <SPAN>{</SPAN> <SPAN>new</SPAN> <SPAN>Thread</SPAN><SPAN>(</SPAN><SPAN>new</SPAN> <SPAN>Runnable</SPAN><SPAN>(</SPAN><SPAN>)</SPAN> <SPAN>{</SPAN> <SPAN>public</SPAN> <SPAN>void</SPAN> run<SPAN>  

(</SPAN><SPAN>)</SPAN> <SPAN>{</SPAN> <SPAN>final</SPAN>   

Bitmap b <SPAN>=</SPAN> loadImageFromNetwork<SPAN>  

(</SPAN><SPAN>)</SPAN><SPAN>;</SPAN>   

mImageView.<SPAN>post</SPAN><SPAN>  

(</SPAN><SPAN>new</SPAN> <SPAN>Runnable</SPAN><SPAN>  

<SPAN>public</SPAN> <SPAN>void</SPAN> run<SPAN>  

mImageView.<SPAN>setImageBitmap</SPAN><SPAN>  

<SPAN>}</SPAN><SPAN>)</SPAN><SPAN>;</SPAN> <SPAN>}  

</SPAN> <SPAN>}</SPAN><SPAN>)  

</SPAN>.<SPAN>start</SPAN><SPAN>(</SPAN><SPAN>)  

</SPAN><SPAN>;</SPAN> <SPAN>}</SPAN>    

不幸的是,這些類和方法也往往使你的代碼更複雜,更難以閱讀。更糟糕的是,它需要頻繁執行複雜的操作界面更新。

為了解決這個問題,1.5和更高版本的Android平台提供了一個實用類稱為AsyncTask,簡化了長時間運作的任務,需要與使用者界面的互動。

類似AsyncTask的一個類UserTask也可用于Android 1.0和1.1版本,它提供了完全相同的API,所有您需要做的是把它的源代碼複制到你的應用程式中。

AsyncTask的目标是要為你的線程提供管理服務,我們前面的例子可以很容易的用AsyncTask來改寫:

<SPAN>public</SPAN> <SPAN>void</SPAN> onClick<SPAN>  

(</SPAN><SPAN>View</SPAN> v<SPAN>)</SPAN> <SPAN>{</SPAN>  

 <SPAN>new</SPAN> DownloadImageTask<SPAN>(</SPAN><SPAN>)  

</SPAN>.<SPAN>execute</SPAN><SPAN>  

(</SPAN><SPAN>"http://www.ideasandroid.com/image.png"</SP  

AN><SPAN>)</SPAN><SPAN>;</SPAN> <SPAN>}</SPAN>   

  <SPAN>private</SPAN> <SPAN>class</SPAN>   

DownloadImageTask <SPAN>extends</SPAN>   

AsyncTask<SPAN><</SPAN>String,   

<SPAN>Void</SPAN>,Bitmap<SPAN>></SPAN> <SPAN>{</SPAN>  

 <SPAN>protected</SPAN> Bitmap doInBackground<SPAN>  

(</SPAN><SPAN>String</SPAN>... <SPAN>urls</SPAN><SPAN>)  

</SPAN> <SPAN>{</SPAN> <SPAN>return</SPAN>   

loadImageFromNetwork<SPAN>(</SPAN>urls<SPAN>  

[</SPAN><SPAN>0</SPAN><SPAN>]</SPAN><SPAN>)  

</SPAN><SPAN>;</SPAN> <SPAN>}</SPAN>     

<SPAN>protected</SPAN> <SPAN>void</SPAN>   

onPostExecute<SPAN>(</SPAN>Bitmap result<SPAN>)</SPAN>  

 <SPAN>{</SPAN>   

(</SPAN>result<SPAN>)</SPAN><SPAN>;</SPAN> <SPAN>}  

</SPAN> <SPAN>}</SPAN>   

正如你所看到的,我們必須通過繼承AsyncTask類來使用它,非常重要的一點是:AsyncTask必須在UI線程中執行個體化它,并且隻能執行一次。

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

•您可以指定三個參數類型,泛型參數,進度值(執行過程中傳回的值)和最終值(執行完傳回的值)。

•該方法doInBackground()自動執行工作線程(背景線程)

•onPreExecute(),onPostExecute()和onProgressUpdate()都是在UI線程調用

•由doInBackground傳回的值()發送到onPostExecute()

•您可以在執行doInBackground()時調用publishProgress()然後在UI組程中執行onProgressUpdate()。

•您可以從任何線程随時取消任務

不管你是否使用AsyncTask,時刻牢記單一線程模型的兩條規則:

1、不要鎖住使用者界面。

2、確定隻在UI線程中通路android使用者界面工具包中的元件。

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

本文非原創作品,全是從洋文翻譯過來的,如有不對的地方請拍磚!!

     本文轉自xyz_lmn51CTO部落格,原文連結:,http://blog.51cto.com/xyzlmn/818235如需轉載請自行聯系原作者