原文链接:Android中的Handler详解以及和Thread的区别

使用Thread+Handler实现非UI线程更新UI界面

一、Handler的定义:

主要接受子线程发送的数据,并用此数据配合主线程更新UI。

解释::

当应用程序启动时,Android首先会开启一个主线程(也就是UI线程),主线程负责管理界面中的UI控件,并进行事件分发。比如说,当你点击一个Button,Android会分发点击事件到Button上,来响应你的操作。

如果此时需要一个耗时的操作,比如联网读取数据,或者读取本地较大的一个文件时,最好不要把这些操作放在主线程中,如果放在主线程中的话,可能会造成线程拥堵,界面会出现假死现象。如果5秒钟还没有完成的话,会收到Android系统的一个错误提示“强制关闭”。

这个时候我们需要把这些耗时的操作,放在一个子线程中,如果子线程的工作涉及到UI更新(如从网上获取图片并先是到UI界面),而由于Android采用UI单线程模型,所以只能在主线程中对UI元素进行操作。如果在非UI线程直接对UI进行了操作,则会报错:

CalledFromWrongThreadException:onlytheoriginalthreadthatcreatedaviewhierarchycantouchitsviews.

这个时候,Handler就出现了。我们可以利用Handler实现线程间通信。

由于Handler运行在主线程中(UI线程中),它与子线程可以通过Message对象来传递数据。这个时候,Handler就承担着接受子线程用sendMessage()方法传递过来的Message对象(里面包含数据),通过消息处理实现UI更新。

二、获取CSDNlogo的例子

ThradHandlerActivity.java:

public class ThreadHandlerActivity extends Activity {/** Called when the activity is first created. */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() {//此方法在ui线程运行public void handleMessage (Message msg){switch(msg.what){case MSG_SUCCESS://imageview显示从网络获取到的logomImageView.setImageBitmap((Bitmap) msg.obj);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;}}};@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);//显示图片的ImageViewmImageView= (ImageView) findViewById(R.id.imageView);mButton = (Button) findViewById(R.id.button);mButton.setOnClickListener(new OnClickListener(){@Overridepublic 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() {@Overridepublic void run() {//run()在新的线程中运行HttpClient hc = new DefaultHttpClient();HttpGet hg = new HttpGet("http://csdnimg.cn/www/images/csdnindex_logo.gif");//获取csdn的logofinal Bitmap bm;try {HttpResponse hr = hc.execute(hg);bm = BitmapFactory.decodeStream(hr.getEntity().getContent());} catch (Exception e) {//获取图片失败mHandler.obtainMessage(MSG_FAILURE).sendToTarget();return;}//获取图片成功,向ui线程发送MSG_SUCCESS标识和bitmap对象mHandler.obtainMessage(MSG_SUCCESS,bm).sendToTarget();}};}

main.xml布局文件:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical" android:layout_width="fill_parent"android:layout_height="fill_parent"><Button android:id="@+id/button" android:text="@string/button_name" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button><ImageView android:id="@+id/imageView" android:layout_height="wrap_content"android:layout_width="wrap_content" /></LinearLayout>

strings.xml:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical" android:layout_width="fill_parent"android:layout_height="fill_parent"><Button android:id="@+id/button" android:text="@string/button_name" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button><ImageView android:id="@+id/imageView" android:layout_height="wrap_content"android:layout_width="wrap_content" /></LinearLayout>

Manifest.xml:

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.zhuozhuo"android:versionCode="1"android:versionName="1.0"><uses-sdk android:minSdkVersion="9" /><uses-permission android:name="android.permission.INTERNET"></uses-permission><!--不要忘记设置网络访问权限--><application android:icon="@drawable/icon" android:label="@string/app_name"><activity android:name=".ThreadHandlerActivity"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,并用bitmap对象存储了这个Logo的像素信息。

此时,如果在这个线程的run()方法中调用:

mImageView.setImageBitmap(bm);

会出现:CalledFromWrongThreadException:onlytheoriginalthreadthatcreatedaviewhierarchycantouchitsviews。原因是run()方法是在新开的线程中执行的,我们上面提到不能直接在非ui线程中操作ui元素。

非UI线程发送消息到UI线程分为两个步骤

一、发送消息到UI线程的消息队列

通过使用Handler的:

   Message obtainMessage(int what,Object object) 

构造一个Message对象,这个对象存储了是否成功获取图片的标识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线程的方法。@Overridepublic void run() {//run()方法会在ui线程执行     mImageView.setImageBitmap(bm);}   });

这种方法会把Runnable对象发送到消息队列,ui线程接收到消息后会执行这个runnable对象。

更多相关文章

  1. 子线程新建Handler为什么会报错?——浅谈Handler、Looper、Messag
  2. Android初识多线程
  3. android 线程优先级设置
  4. android C++ 和 jni,根据JNIEnv的FindClass获取java类,包括多线程
  5. Android处理线程暂停与恢复
  6. android如何在子线程中更新UI
  7. android webview 添加内置对象

随机推荐

  1. 【边做项目边学Android】知识点:Adapter适
  2. [Android L]SEAndroid增强Androd安全性背
  3. android jni 理解
  4. HOWTO install and setup Android NDK fo
  5. android中使用线程池和临时缓存优化网络
  6. android 中 Error:Could not find suppor
  7. [Android]获取未安装的APK图标
  8. Android 系统 目录 分析
  9. Android使用selector自定义按钮
  10. Android手机端加载电脑端网页