Android 非同步更新UI----handler+thread

來源:互聯網
上載者:User

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>  

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.