Android应用的开发过程中需要把繁重的任务(IO,网络连接等)放到其他线程中异步执行,达到不阻塞UI的效果。

  下面将由浅入深介绍Android进行异步处理的实现方法和系统底层的实现原理。

本文介绍Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面:

即如何使用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:

  1. public class ThreadHandlerActivity extends Activity {
  2. /** Called when the activity is first created. */
  3. private static final int MSG_SUCCESS = 0;//获取图片成功的标识
  4. private static final int MSG_FAILURE = 1;//获取图片失败的标识
  5. private ImageView mImageView;
  6. private Button mButton;
  7. private Thread mThread;
  8. private Handler mHandler = new Handler() {
  9. public void handleMessage (Message msg) {//此方法在ui线程运行
  10. switch(msg.what) {
  11. case MSG_SUCCESS:
  12. mImageView.setImageBitmap((Bitmap) msg.obj);//imageview显示从网络获取到的logo
  13. Toast.makeText(getApplication(), getApplication().getString(R.string.get_pic_success), Toast.LENGTH_LONG).show();
  14. break;
  15. case MSG_FAILURE:
  16. Toast.makeText(getApplication(), getApplication().getString(R.string.get_pic_failure), Toast.LENGTH_LONG).show();
  17. break;
  18. }
  19. }
  20. };
  21. @Override
  22. public void onCreate(Bundle savedInstanceState) {
  23. super.onCreate(savedInstanceState);
  24. setContentView(R.layout.main);
  25. mImageView= (ImageView) findViewById(R.id.imageView);//显示图片的ImageView
  26. mButton = (Button) findViewById(R.id.button);
  27. mButton.setOnClickListener(new OnClickListener() {
  28. @Override
  29. public void onClick(View v) {
  30. if(mThread == null) {
  31. mThread = new Thread(runnable);
  32. mThread.start();//线程启动
  33. }
  34. else {
  35. Toast.makeText(getApplication(), getApplication().getString(R.string.thread_started), Toast.LENGTH_LONG).show();
  36. }
  37. }
  38. });
  39. }
  40. Runnable runnable = new Runnable() {
  41. @Override
  42. public void run() {//run()在新的线程中运行
  43. HttpClient hc = new DefaultHttpClient();
  44. HttpGet hg = new HttpGet("http://csdnimg.cn/www/images/csdnindex_logo.gif");//获取csdn的logo
  45. final Bitmap bm;
  46. try {
  47. HttpResponse hr = hc.execute(hg);
  48. bm = BitmapFactory.decodeStream(hr.getEntity().getContent());
  49. } catch (Exception e) {
  50. mHandler.obtainMessage(MSG_FAILURE).sendToTarget();//获取图片失败
  51. return;
  52. }
  53. mHandler.obtainMessage(MSG_SUCCESS,bm).sendToTarget();//获取图片成功,向ui线程发送MSG_SUCCESS标识和bitmap对象
  54. //          mImageView.setImageBitmap(bm); //出错!不能在非ui线程操作ui元素
  55. //          mImageView.post(new Runnable() {//另外一种更简洁的发送消息给ui线程的方法。
  56. //
  57. //              @Override
  58. //              public void run() {//run()方法会在ui线程执行
  59. //                  mImageView.setImageBitmap(bm);
  60. //              }
  61. //          });
  62. }
  63. };
  64. }

main.xml布局文件:

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

strings.xml

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

Manifest.xml:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="com.zhuozhuo"
  4. android:versionCode="1"
  5. android:versionName="1.0">
  6. <uses-sdk android:minSdkVersion="9" />
  7. <uses-permission android:name="android.permission.INTERNET"></uses-permission><!--不要忘记设置网络访问权限-->
  8. <application android:icon="@drawable/icon" android:label="@string/app_name">
  9. <activity android:name=".ThreadHandlerActivity"
  10. android:label="@string/app_name">
  11. <intent-filter>
  12. <action android:name="android.intent.action.MAIN" />
  13. <category android:name="android.intent.category.LAUNCHER" />
  14. </intent-filter>
  15. </activity>
  16. </application>
  17. </manifest>

运行结果:

为了不阻塞ui线程,我们使用mThread从网络获取了CSDN的LOGO

,并用bitmap对象存储了这个Logo的像素信息。

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

  1. mImageView.setImageBitmap(bm)

会出现:CalledFromWrongThreadException:only the original thread that created a view hierarchy can touch its views。原因是run()方法是在新开的线程中执行的,我们上面提到不能直接在非ui线程中操作ui元素。

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

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

通过使用Handler的

  1. Message obtainMessage(int what,Object object)

构造一个Message对象,这个对象存储了是否成功获取图片的标识what和bitmap对象,然后通过message.sendToTarget()方法把这条message放到消息队列中去。

二、处理发送到UI线程的消息

在ui线程中,我们覆盖了handler的

  1. public void handleMessage (Message msg)

这个方法是处理分发给ui线程的消息,判断msg.what的值可以知道mThread是否成功获取图片,如果图片成功获取,那么可以通过msg.obj获取到这个对象。

最后,我们通过

  1. mImageView.setImageBitmap((Bitmap) msg.obj);

设置ImageView的bitmap对象,完成UI的更新。

补充:

事实上,我们还可以调用

View的post方法来更新ui

  1. mImageView.post(new Runnable() {//另外一种更简洁的发送消息给ui线程的方法。
  2. @Override
  3. public void run() {//run()方法会在ui线程执行
  4. mImageView.setImageBitmap(bm);
  5. }
  6. });

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

总结:从例子中我们可以看到handler既有发送消息和处理消息的作用,会误以为handler实现了消息循环和消息分发,其实Android为了让我们的代码看起来更加简洁,与UI线程的交互只需要使用在UI线程创建的handler对象就可以了。

http://blog.csdn.net/mylzc/article/details/6736988

Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面的更多相关文章

  1. Android异步处理系列文章四篇之一使用Thread+Handler实现非UI线程更新UI界面

    目录: Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面Android异步处理二:使用AsyncTask异步更新UI界面Android异步处理三:Handler+L ...

  2. 学习通过Thread+Handler实现非UI线程更新UI组件

    [Android线程机制] 出于性能考虑,Android的UI操作并不是线程安全的,这就意味着如果有多个线程并发操作UI组件,可能导致线程安全问题.为了解决这个问题,Android制定了一条简单的规则 ...

  3. 学习通过Thread+Handler实现非UI线程更新UI组件(转)

    [Android线程机制] 出于性能考虑,Android的UI操作并不是线程安全的,这就意味着如果有多个线程并发操作UI组件,可能导致线程安全问题.为了解决这个问题,Android制定了一条简单的规则 ...

  4. android 在非UI线程更新UI仍然成功原因深入剖析

    ”只能在UI主线程中更新View“. 这句话很熟悉吧? 来来,哥们,看一下下面的例子 @Override       protected void onCreate(Bundle savedInsta ...

  5. Android子线程更新UI的方法总结

    版权声明:本文为博主原创文章,转载请注明出处:https://i.cnblogs.com/EditPosts.aspx?postid=6121280 消息机制,对于Android开发者来说,应该是非常 ...

  6. 安卓 异步线程更新Ui

    异步跟新UI: 1.handler+Thread(runnable):如果handler和Thread都写在了一个Java文件中,就不说了,如果runnable定义在了一个单独的类文件中,可以通过在构 ...

  7. android子线程更新UI

    参考:https://www.cnblogs.com/joy99/p/6121280.html 子线程是不能直接更新UI的.Android实现View更新有两组方法,分别是invalidate和pos ...

  8. [Android学习笔记]子线程更新UI线程方法之Handler

    关于此笔记 不讨论: 1.不讨论Handler实现细节 2.不讨论android线程派发细节 讨论: 子线程如何简单的使用Handler更新UI 问题: android开发时,如何在子线程更新UI? ...

  9. Android开发——实现子线程更新UI

    Android中线程按功能分的话,可以分为两个,一个是主线程(UI线程),其他的都是子线程 主线程不能执行那些耗时过长的代码或任务(执行耗时过长的代码会出现应用未响应的提示),所以都是使用子线程来执行 ...

随机推荐

  1. JavaScript之模块化编程

    前言 模块是任何大型应用程序架构中不可缺少的一部分,模块可以使我们清晰地分离和组织项目中的代码单元.在项目开发中,通过移除依赖,松耦合可以使应用程序的可维护性更强.与其他传统编程语言不同,在当前Jav ...

  2. offsetLeft,Left,clientLeft的区别

    offsetLeft,Left,clientLeft的区别 假设 obj 为某个 HTML 控件. obj.offsetTop 指 obj 相对于版面或由 offsetParent 属性指定的父坐标的 ...

  3. java--常用类summary(三)

    /* 1:正则表达式(理解) (1)就是符合一定规则的字符串 (2)常见规则 A:字符 x 字符 x.举例:'a'表示字符a \\ 反斜线字符. \n 新行(换行)符 ('\u000A') \r 回车 ...

  4. httpClient 4.x post get方法

    public static String doPost(String url, String encoding, String contentType, String sendData) throws ...

  5. h5 本地存储和读取信息

    总结:cookie的缺点很明显,最多只能存储4KB的数据,每个HTTP请求都会被传送回服务器,明文传输(除非你使用SSL)对于购物网站而言,cookie是非常重要的,为了实现购物车功能,把已选物品加入 ...

  6. .NET开发知识体系

    记得几年前写过一篇关于.NET开发方面的知识总结,但是随着技术的发展以及自己技术理解的提升,觉得有必要对那篇文章加以更新和完善. 最近在园子里也看到有人写关于.NET知识体系的文章,特别是灵感之源写的 ...

  7. JS访问剪切板中的图片

    google出来一个html2canvas,它利用canvas来渲染读取的DOM树,也就是说它只能截取document里的内容,如果要像qq截图那样,应该怎么做?用过百度的Ueditor编辑器的朋友都 ...

  8. 常见JS(JavaScript)冲突解决方法

    1.一般JS冲突解决办法 a.最容易出现的就是js的命名冲突 ①.变量名冲突 变量有全局变量和局部变量当全局变量变量和局部变量名称一致时,就会js冲突,由于变量传递数值或地址不同就会产生JavaScr ...

  9. ros与下位机通信常用的c++ boost串口应用

    一.首先移植c++ boost 库: 1. 先去 Boost官网 下载最新的Boost版本, 我下载的是boost_1_6_0版本, 解压. 2. 进入解压后目录: cd boost_1_6_0, 执 ...

  10. 2440 lcd10分钟休眠修改

    在我们的系统中,LCD的虚拟控制台和控制台TTY不是同一个设备,也就是说,如果在程序里单纯的printf是不行的!这样只能修改你正在使用的TTY的blankinterval,而你用的却是文本方式的设备 ...