Android關於線程更新UI的方法

來源:互聯網
上載者:User

在一個Android 程式開始啟動並執行時候,會單獨啟動一個Process。預設的情況下,所有這個程式中的Activity或者Service(Service和 Activity只是Android提供的Components中的兩種,除此之外還有Content Provider和Broadcast Receiver)都會跑在這個Process。
        一個Android 程式預設情況下也只有一個Process,但一個Process下卻可以有許多個Thread。
       在這麼多Thread當中,有一個Thread,我們稱之為UI Thread。UI Thread在Android程式啟動並執行時候就被建立,是一個Process當中的主線程Main Thread,主要是負責控制UI介面的顯示、更新和控制項互動。在Android程式建立之初,一個Process呈現的是單執行緒模式,所有的任務都在一個線程中運行。因此,我們認為,UI Thread所執行的每一個函數,所花費的時間都應該是越短越好。而其他比較費時的工作(訪問網路,下載資料,查詢資料庫等),都應該交由子線程去執行,以免阻塞主線程。
        那麼,UI Thread如何和其他Thread一起工作呢?常用方法是:
        誕生一個主線程的Handler物件,當做Listener去讓子線程能將訊息Push到主線程的Message Quene裡,以便觸發主線程的handlerMessage()函數,讓主線程知道子線程的狀態,並在主線程更新UI。
       例如,在子線程的狀態發生變化時,我們需要更新UI。如果在子線程中直接更新UI,通常會拋出下面的異常:
        11-07 13:33:04.393: ERROR/JavaBinder(1029):android.view.ViewRoot$CalledFromWrongThreadException:Only the original thread that created a view hierarchy can touch its views.
       意思是,無法在子線程中更新UI。為此,我們需要通過Handler物件,通知主線程Ui Thread來更新介面。
        如下,首先建立一個Handler,來監聽Message的事件:
       private final int UPDATE_UI = 1;
       private Handler mHandler = new MainHandler();
      private class MainHandler extends Handler {
         @Override
             public void handleMessage(Message msg) {
             switch (msg.what) {
                 case UPDATE_UI: {
                Log.i("TTSDeamon", "UPDATE_UI");
                showTextView.setText(editText.getText().toString());
                ShowAnimation();
                     break;
                 }
                 default:
                     break;
             }
         }
       }
     或者
      private Handler mHandler = new Handler(){
         @Override
             public void handleMessage(Message msg) {
             switch (msg.what) {
                 case UPDATE_UI: {
                Log.i("TTSDeamon", "UPDATE_UI");
                showTextView.setText(editText.getText().toString());
                ShowAnimation();
                     break;
                 }
                 default:
                     break;
             }
          }
      }
       當子線程的狀態發生變化,則在子線程中發出Message,通知更新UI。
       mHandler.sendEmptyMessageDelayed(UPDATE_UI, 0);
      在我們的程式中,很多Callback方法有時候並不是運行在主線程當中的,所以如果在Callback方法中更新UI失敗,也可以採用上面的方法。
第二篇
Android的UI設計與後台線程互動
2010-09-14 09:38 佚名 ideasandroid 我要評論(0) 字型大小:T | T
本文將討論Android應用程式的執行緒模式以及如何使用線程來處理耗時較長的操作,而不是在主線程中執行,保證使用者介面(UI)的流暢運行。
標籤:Android  UI
AD: 限時報名參加“甲骨文全球大會·2010·北京”及“JavaOne和甲骨文開發人員大會2010”
索 引
[顯示]
在 Android的UI設計方面我們講過“Android UI”設計官方教程。本文將討論Android應用程式的執行緒模式以及如何使用線程來處理耗時較長的操作,而不是在主線程中執行,保證使用者介面(UI)的流暢運行。本文還將闡述一些使用者介面(UI)中與線程互動的API。
UI使用介面執行緒
當應用程式啟動時,系統會為應用程式建立一個主線程(main)或者叫UI線程,它負責分發事件到不同的組件,包括繪畫事件。完成你的應用程式與Android UI組件互動。
例如,當您觸控螢幕幕上的一個按鈕時,UI線程會把觸摸事件分發到組件上,更改狀態並加入事件隊列,UI線程會分發請求和通知到各個組件,完成相應的動作。
單執行緒模式的效能是非常差的,除非你的應用程式相當的簡單,特別是當所有的操作都在主線程中執行,比如訪問網路或資料庫之類的耗時操作將會導致使用者介面鎖定,所有的事件將不能分發,應用程式就像死了一樣,更嚴重的是當超過5秒時,系統就會彈出“應用程式無響應”的對話方塊。
如果你想看看什麼效果,可以寫一個簡單的應用程式,在一個Button的OnClickListener中寫上Thread.sleep(2000),運行程式你就會看到在應用程式回到正常狀態前按鈕會保持按下狀態2秒,當這種情況發生時,您就會感覺到應用程式反映相當的慢。
總之,我們需要保證主線程(UI線程)不被鎖住,如果有耗時的操作,我們需要把它放到一個單獨的後台線程中執行。
下面是一個點擊按鈕後下載一個圖片,同時顯示到介面的ImageView上的例子:
public void onClick(View v) {  
  new Thread(new Runnable() {  
    public void run() {  
      Bitmap b = loadImageFromNetwork();  
      mImageView.setImageBitmap(b);  
    }  
  }).start();  
}  
起初,上面的代碼似乎是一個很好的解決方案,因為它不會鎖住使用介面執行緒。然面不幸的是,它違反了使用者介面單執行緒模式:Android的使用者介面工具包不是安全執行緒的,只能在UI線程中操作它,在上面的代碼中,你在一個背景工作執行緒中調用mImageView.setImageBitmap(b)時,將會發生意想不到的錯誤,這種錯誤是非常難跟蹤和調試的。
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();  

不幸的是,這些類和方法也往往使你的代碼更複雜,更難以閱讀。更糟糕的是,它需要頻繁執行複雜的操作介面更新。
為瞭解決這個問題,1.5和更高版本的Android平台提供了一個實用類稱為AsyncTask,簡化了長時間啟動並執行任務,需要與使用者介面的互動。
類似AsyncTask的一個類UserTask也可用於Android 1.0和1.1版本,它提供了完全相同的API,所有您需要做的是把它的原始碼複製到你的應用程式中。
AsyncTask的目標是要為你的線程提供管理服務,我們前面的例子可以很容易的用AsyncTask來改寫:
public void onClick(View v) {  
  new DownloadImageTask().execute("http://www.ideasandroid.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()都是在UI線程調用
◆由doInBackground返回的值()發送到onPostExecute()
◆您可以在執行doInBackground()時調用publishProgress()然後在UI組程中執行onProgressUpdate()。
◆您可以從任何線程隨時取消任務
不管你是否使用AsyncTask,時刻牢記單一執行緒模式的兩條規則:
1、不要鎖住使用者介面。
2、確保只在UI線程中訪問Android使用者介面工具包中的組件。
AsyncTask只是可以讓你更容易地做這些事情。

相關文章

聯繫我們

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

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

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.