標籤:des android c class blog code
使用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線程更新介面。
UIupdateActivity.java
public class UIupdateActivity extends Activity{ private static final int MSG_SUCCESS = 0;//擷取圖片成功的標識 private static final int MSG_FAILURE = 1;//擷取圖片失敗的標識 protected static final String TAG = "Raylee "; protected static final String TITLE = "UIupdate through workerThread"; private ImageView mImageView; private Button mButton; private Thread mThread; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.example2); mImageView= (ImageView) findViewById(R.id.imageView);//顯示圖片的ImageView mButton = (Button) findViewById(R.id.button); mButton.setOnClickListener(new OnClickListener() { @Override 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(); } } }); } @SuppressLint("HandlerLeak") private Handler mHandler = new Handler() { public void handleMessage (Message msg) {//此方法在ui線程運行 switch(msg.what) { case MSG_SUCCESS: mImageView.setImageBitmap((Bitmap) msg.obj);//imageview顯示從網路擷取到的logo Log.i(TAG, "Handler Success ---> " + msg.obj); Toast.makeText(getApplication(), getApplication() .getString(R.string.get_pic_success),Toast.LENGTH_LONG).show(); break; case MSG_FAILURE: Log.i(TAG, "Handler Failure!"); Toast.makeText(getApplication(), getApplication(). getString(R.string.get_pic_failure),Toast.LENGTH_LONG).show(); break; } } }; Runnable runnable = new Runnable() { @Override public void run() {//run()在新的線程中運行 HttpClient hc = new DefaultHttpClient(); HttpGet hg = new HttpGet("http://csdnimg.cn/www/images/csdnindex_logo.gif");//擷取csdn的logo Log.i(TAG + TITLE, "hg Not null ---> " + hg); Bitmap bm = null; try { HttpResponse hr = hc.execute(hg); bm = BitmapFactory.decodeStream(hr.getEntity().getContent()); Log.i(TAG + TITLE, "bm Not null ---> " + bm); } catch (Exception e) { mHandler.obtainMessage(MSG_FAILURE).sendToTarget();//擷取圖片失敗 Log.i(TAG + TITLE, "Failure!"); return; } Log.i(TAG + TITLE, "Success ---> " + bm); mHandler.obtainMessage(MSG_SUCCESS,bm).sendToTarget();//擷取圖片成功,向ui線程發送MSG_SUCCESS標識和bitmap對象 } }; }
example2.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="match_parent" android:orientation="vertical" > <Button android:id="@+id/button" android:text="@string/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="30dp" /> <ImageView android:id="@+id/imageView" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_margin="100dp" android:layout_width="wrap_content" android:contentDescription="@android:string/untitled" /> </LinearLayout>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="home.lee.example2UIupdate" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="10" android:targetSdkVersion="10" /> <uses-permission android:name="android.permission.INTERNET"/> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:name=".UIupdateActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
運行結果:
為了不阻塞ui線程,我們使用mThread從網路擷取了CSDN的LOGO
,並用bitmapObject Storage Service了這個Logo的像素資訊。
此時,如果在這個線程的run()方法中調用
mImageView.setImageBitmap(bm)
會出現:CalledFromWrongThreadException:only the original thread that created a view hierarchy can touch its views。原因是run()方法是在新開的線程中執行的,我們上面提到不能直接在非ui線程中操作ui元素。
非UI線程發送訊息到UI線程分為兩個步驟
一、發送訊息到UI線程的訊息佇列
通過使用Handler的
Message obtainMessage(int what,Object object)
構造一個Message對象,這個Object Storage Service了是否成功擷取圖片的標識what和bitmap對象,然後通過message.sendToTarget()方法把這條message放到訊息佇列中去。
二、處理髮送到UI線程的訊息
在ui線程中,我們覆蓋了handler的
public void handleMessage (Message msg)
這個方法是處理分發給ui線程的訊息,判斷msg.what的值可以知道mThread是否成功擷取圖片,如果圖片成功擷取,那麼可以通過msg.obj擷取到這個對象。最後,我們通過
mImageView.setImageBitmap((Bitmap) msg.obj);
設定ImageView的bitmap對象,完成UI的更新。補充:
事實上,我們還可以調用View的post方法來更新ui
mImageView.post(new Runnable() {//另外一種更簡潔的發送訊息給ui線程的方法。 @Override public void run() {//run()方法會在ui線程執行 mImageView.setImageBitmap(bm); } });
這種方法會把Runnable對象發送到訊息佇列,ui線程接收到訊息後會執行這個runnable對象。從例子中我們可以看到handler既有發送訊息和處理訊息的作用,會誤以為handler實現了訊息迴圈和訊息分發,其實Android為了讓我們的代碼看起來更加簡潔,與UI線程的互動只需要使用在UI線程建立的handler對象就可以了。
http://blog.csdn.net/mylzc/article/details/6736988