標籤:thread handler 非ui線程 更新ui介面
概述:每個Android應用程式都運行在一個dalvik虛擬機器進程中,進程開始的時候會啟動一個主線程(MainThread),主線程負責處理和ui相關的事件,因此主線程通常又叫UI線程。而由於Android採用UI單執行緒模式,所以只能在主線程中對UI元素進行操作。如果在非UI線程直接對UI進行了操作,則會報錯:
CalledFromWrongThreadException only the original thread that created a view hierarchy can touch its views
。
Android為我們提供了訊息迴圈的機制,我們可以利用這個機制來實現線程間的通訊。那麼,我們就可以在非UI線程發送訊息到UI線程,最終讓Ui線程來進行ui的操作。
對於運算量較大的操作和IO操作,我們需要新開線程來處理這些繁重的工作,以免阻塞ui線程。
例子:下面我們以擷取CSDN logo的例子,示範如何使用Thread+Handler的方式實現在非UI線程發送訊息通知UI線程更新介面。
ThradHandlerActivity.java:
package com.lc.androidasyntask;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 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; private Handler mHandler = new Handler() { public void handleMessage(Message msg) {// 此方法在ui線程運行 switch (msg.what) { case MSG_SUCCESS: mImageView.setImageBitmap((Bitmap) msg.obj);// imageview顯示從網路擷取到的logo Toast.makeText(getApplication(), getApplication().getString(R.string.get_pic_success), Toast.LENGTH_LONG).show(); break; case MSG_FAILURE: Toast.makeText(getApplication(), getApplication().getString(R.string.get_pic_failure), Toast.LENGTH_LONG).show(); break; } } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mImageView = (ImageView) findViewById(R.id.imageView);// 顯示圖片的ImageView mButton = (Button) findViewById(R.id.button); mButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { if (mThread == null) { mThread = new Thread(runnable); mThread.start();// 線程啟動 } else { Toast.makeText( getApplication(), getApplication().getString(R.string.thread_started), Toast.LENGTH_LONG).show(); } } }); } Runnable runnable = new Runnable() { public void run() {// run()在新的線程中運行 HttpClient httpClient = new DefaultHttpClient(); HttpGet httpGet = new HttpGet( "http://csdnimg.cn/www/images/csdnindex_logo.gif");// 擷取csdn的logo final Bitmap bitmap; try { HttpResponse httpResponse = httpClient.execute(httpGet); bitmap = BitmapFactory.decodeStream(httpResponse.getEntity().getContent()); } catch (Exception e) { mHandler.obtainMessage(MSG_FAILURE).sendToTarget();// 擷取圖片失敗 return; } // 擷取圖片成功,向ui線程發送MSG_SUCCESS標識和bitmap對象 mHandler.obtainMessage(MSG_SUCCESS, bitmap).sendToTarget(); // mImageView.setImageBitmap(bm); //出錯!不能在非ui線程操作ui元素 // mImageView.post(new Runnable() {//另外一種更簡潔的發送訊息給ui線程的方法。 // // @Override // public void run() {//run()方法會在ui線程執行 // mImageView.setImageBitmap(bm); // } // }); } };}
main.xml檔案:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="擷取圖片" > </Button> <ImageView android:id="@+id/imageView" android:layout_width="wrap_content" android:layout_height="wrap_content" /></LinearLayout>
strings.xml檔案:
<string name="thread_started">Thread started</string> <string name="get_pic_success">Get pic success</string> <string name="get_pic_failure">Get pic failure</string>
在資訊清單檔中加入訪問網路許可權:
<!-- 不要忘記設定網路存取權限 --> <uses-permission android:name="android.permission.INTERNET" />
然後將”com.lc.androidasyntask.ThreadHandlerActivity” 設定為啟動頁面
結果如下:
案例中我們使用Handler和Thread,線上程執行的結構中通知handler,在handler中進行 圖片的設定。
Android非同步機制一:使用Thread+Handler實現非UI線程更新UI介面