Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 異步更新UI----handler+thread

Android 異步更新UI----handler+thread

編輯:關於Android編程

android應用是單線程模式的。     單線程模式需要記住兩條:   一、防止UI線程阻塞   二、確保只在UI線程中訪問Android UI工具包   在開發Android應用時必須遵守單線程模型的原則:Android UI操作並不是線程安全的並且這些操作必須在UI線程中執行。 每個Android應用程序都運行在一個dalvik虛擬機進程中,進程開始的時候會啟動一個主線程(MainThread),主線程負責處理和ui相關的事件,因此主線程通常又叫UI線程。而由於Android采用UI單線程模型,所以只能在主線程中對UI元素進行操作。   開一個線程或者在後台線程中來執行耗時的操作,如下面的例子:   public void onClick( View v ) {     new Thread( new Runnable() {          public void run() {     Bitmap b = loadImageFromNetwork();   //從網絡上下載圖片   mImageView.setImageBitmap( b );  //把圖片設置給ImageView   }       }).start()    }   上面的代碼會報錯,你可能會說邏輯很正確啊,但是它違背了Android單線程模型:Android UI操作並不是線程安全的並且這些操作必須在UI線程中執行.   例如: 如果在非UI線程直接對UI進行了操作,則會報錯:   CalledFromWrongThreadException:only the original thread that created a view hierarchy can touch its views   Android為我息循環們提供了消的機制,我們可以利用這個機制來實現線程間的通信。那麼,我們就可以在非UI線程發送消息到UI線程,最終讓Ui線程來進行ui的操作。   Andriod提供了幾種在其他線程中訪問UI線程的方法:     Activity.runOnUiThread( Runnable )   View.post( Runnable )      View.postDelayed( Runnable, long )      Hanlder   對於運算量較大的操作和IO操作,我們需要新開線程來處理這些繁重的工作,以免阻塞ui線程。   例子:下面我們以獲取CSDN logo的例子,演示如何使用Thread+Handler的方式實現在非UI線程發送消息通知UI線程更新界面   ThradHandlerActivity.java: [java]   <span style="font-size:18px">package com.example.thread;      import org.apache.http.HttpResponse;   import org.apache.http.client.HttpClient;   import org.apache.http.client.methods.HttpGet;   import org.apache.http.impl.client.DefaultHttpClient;      import com.example.test.R;      import android.annotation.SuppressLint;   import android.app.Activity;   import android.graphics.Bitmap;   import android.graphics.BitmapFactory;   import android.os.Bundle;   import android.os.Handler;   import android.os.Message;   import android.view.View;   import android.view.View.OnClickListener;   import android.widget.Button;   import android.widget.ImageView;   import android.widget.Toast;      public class ThreadHandlerActivity extends Activity{          private static final int MSG_SUCCESS = 0;       private static final int MSG_FAILURE = 1;              private ImageView mImageView;       private Button mButton;              private Thread mThread;              @SuppressLint("HandlerLeak")       private Handler mHandler = new Handler(){           @Override           public void handleMessage(Message msg) {               switch (msg.what) {               case MSG_SUCCESS:                   mImageView.setImageBitmap((Bitmap)msg.obj);                   Toast.makeText(getApplication(), "成功獲取圖片", Toast.LENGTH_LONG).show();                     break;                   case MSG_FAILURE:                     Toast.makeText(getApplication(), "獲取圖片失敗", Toast.LENGTH_LONG).show();                     break;               }               super.handleMessage(msg);           }                  };          @Override       protected void onCreate(Bundle savedInstanceState) {           super.onCreate(savedInstanceState);           setContentView(R.layout.thread_layout);           mImageView= (ImageView) findViewById(R.id.logo);//顯示圖片的ImageView             mButton = (Button) findViewById(R.id.click);           mButton.setOnClickListener(new OnClickListener() {               @Override               public void onClick(View v) {                   if (mThread == null) {                       mThread = new Thread(runnable);                       mThread.start();                   }else {                         Toast.makeText(getApplication(), "線程已經運行", Toast.LENGTH_LONG).show();                     }                }           });       }              Runnable runnable = new Runnable() {           @Override           public void run() {               HttpClient hc = new DefaultHttpClient();               HttpGet hg = new HttpGet("http://csdnimg.cn/www/images/csdnindex_logo.gif");               final Bitmap bm;               try {                   HttpResponse hr = hc.execute(hg);                   bm = BitmapFactory.decodeStream(hr.getEntity().getContent());               } catch (Exception e) {                   e.printStackTrace();                   mHandler.obtainMessage(MSG_FAILURE).sendToTarget();                   return;               }               mHandler.obtainMessage(MSG_SUCCESS, bm).sendToTarget();   //          mImageView.setImageBitmap(bm); //出錯!不能在非ui線程操作ui元素   //           mImageView.post(new Runnable() {//另外一種更簡潔的發送消息給ui線程的方法。     //             @Override     //             public void run() {//run()方法會在ui線程執行     //                 mImageView.setImageBitmap(bm);     //             }     //         });             }       };   }   </span>   對於上面的方法,我們使用的是handler+Thread來實現更新UI,在裡面也有一條注意的就是   [java]  <span style="font-size:18px">mImageView.setImageBitmap(bm); //出錯!不能在非ui線程操作ui元素</span>   其實我們上面提到一個方法Activity.runOnUiThread( Runnable ),將這個Runnable以UI線程的方式啟動 [java]   <span style="font-size:18px">/**       * Runs the specified action on the UI thread. If the current thread is the UI       * thread, then the action is executed immediately. If the current thread is       * not the UI thread, the action is posted to the event queue of the UI thread.       *       * @param action the action to run on the UI thread       */       public final void runOnUiThread(Runnable action) {           if (Thread.currentThread() != mUiThread) {               mHandler.post(action);           } else {               action.run();           }       }</span>     上面Activity的runOnUiThread(Runnable)方法實現。 利用Activity.runOnUiThread(Runnable)把更新ui的代碼創建在Runnable中,然後在需要更新ui時,把這個Runnable對象傳給Activity.runOnUiThread(Runnable)。 這樣Runnable對像就能在ui程序中被調用。如果當前線程是UI線程,那麼行動是立即執行。如果當前線程不是UI線程,操作是發布到事件隊列的UI線程。 使用示例:   [java]   <span style="font-size:18px">current_activity.this. runOnUiThread(new Runnable()                        @Override                      public void run() {                              // refresh ui 的操作代碼                      }   });</span>   這裡需要注意的是runOnUiThread是Activity中的方法,在線程中我們需要告訴系統是哪個activity調用,所以前面顯示的指明了activity. 所以我們修改一下上面的代碼:     [java]   <span style="font-size:18px">package com.example.thread;      import org.apache.http.HttpResponse;   import org.apache.http.client.HttpClient;   import org.apache.http.client.methods.HttpGet;   import org.apache.http.impl.client.DefaultHttpClient;      import com.example.test.R;      import android.annotation.SuppressLint;   import android.app.Activity;   import android.graphics.Bitmap;   import android.graphics.BitmapFactory;   import android.os.Bundle;   import android.os.Handler;   import android.os.Message;   import android.view.View;   import android.view.View.OnClickListener;   import android.widget.Button;   import android.widget.ImageView;   import android.widget.Toast;      public class ThreadHandlerActivity extends Activity{          private static final int MSG_SUCCESS = 0;       private static final int MSG_FAILURE = 1;              private ImageView mImageView;       private Button mButton;              @SuppressLint("HandlerLeak")       private Handler mHandler = new Handler(){           @Override           public void handleMessage(Message msg) {               switch (msg.what) {               case MSG_SUCCESS:                   mImageView.setImageBitmap((Bitmap)msg.obj);                   Toast.makeText(getApplication(), "成功獲取圖片", Toast.LENGTH_LONG).show();                     break;                   case MSG_FAILURE:                     Toast.makeText(getApplication(), "獲取圖片失敗", Toast.LENGTH_LONG).show();                     break;               }               super.handleMessage(msg);           }                  };          @Override       protected void onCreate(Bundle savedInstanceState) {           super.onCreate(savedInstanceState);           setContentView(R.layout.thread_layout);           mImageView= (ImageView) findViewById(R.id.logo);//顯示圖片的ImageView             mButton = (Button) findViewById(R.id.click);           mButton.setOnClickListener(new OnClickListener() {               @Override               public void onClick(View v) {                   ThreadHandlerActivity.this.runOnUiThread(runnable);               }           });       }              Runnable runnable = new Runnable() {           @Override           public void run() {               HttpClient hc = new DefaultHttpClient();               HttpGet hg = new HttpGet("http://csdnimg.cn/www/images/csdnindex_logo.gif");               final Bitmap bm;               try {                   HttpResponse hr = hc.execute(hg);                   bm = BitmapFactory.decodeStream(hr.getEntity().getContent());               } catch (Exception e) {                   e.printStackTrace();                   mHandler.obtainMessage(MSG_FAILURE).sendToTarget();                   return;               }               mImageView.setImageBitmap(bm);           }       };          }   </span>   也可以在線程裡面直接更新UI。 有人會說我傳遞一個當前的Activity到一個線程中,然後實現UI更新,那我就是調用的當前的Activity的內容,其實這個也是不對的也會提示   [java]  <span style="font-size:18px">android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.</span>  
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved