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:

  1. package com.example.thread;
  2. import org.apache.http.HttpResponse;
  3. import org.apache.http.client.HttpClient;
  4. import org.apache.http.client.methods.HttpGet;
  5. import org.apache.http.impl.client.DefaultHttpClient;
  6. import com.example.test.R;
  7. import android.annotation.SuppressLint;
  8. import android.app.Activity;
  9. import android.graphics.Bitmap;
  10. import android.graphics.BitmapFactory;
  11. import android.os.Bundle;
  12. import android.os.Handler;
  13. import android.os.Message;
  14. import android.view.View;
  15. import android.view.View.OnClickListener;
  16. import android.widget.Button;
  17. import android.widget.ImageView;
  18. import android.widget.Toast;
  19. public class ThreadHandlerActivity extends Activity{
  20. private static final int MSG_SUCCESS = 0;
  21. private static final int MSG_FAILURE = 1;
  22. private ImageView mImageView;
  23. private Button mButton;
  24. private Thread mThread;
  25. @SuppressLint("HandlerLeak")
  26. private Handler mHandler = new Handler(){
  27. @Override
  28. public void handleMessage(Message msg) {
  29. switch (msg.what) {
  30. case MSG_SUCCESS:
  31. mImageView.setImageBitmap((Bitmap)msg.obj);
  32. Toast.makeText(getApplication(), "成功获取图片", Toast.LENGTH_LONG).show();
  33. break;
  34. case MSG_FAILURE:
  35. Toast.makeText(getApplication(), "获取图片失败", Toast.LENGTH_LONG).show();
  36. break;
  37. }
  38. super.handleMessage(msg);
  39. }
  40. };
  41. @Override
  42. protected void onCreate(Bundle savedInstanceState) {
  43. super.onCreate(savedInstanceState);
  44. setContentView(R.layout.thread_layout);
  45. mImageView= (ImageView) findViewById(R.id.logo);//显示图片的ImageView
  46. mButton = (Button) findViewById(R.id.click);
  47. mButton.setOnClickListener(new OnClickListener() {
  48. @Override
  49. public void onClick(View v) {
  50. if (mThread == null) {
  51. mThread = new Thread(runnable);
  52. mThread.start();
  53. }else {
  54. Toast.makeText(getApplication(), "线程已经运行", Toast.LENGTH_LONG).show();
  55. }
  56. }
  57. });
  58. }
  59. Runnable runnable = new Runnable() {
  60. @Override
  61. public void run() {
  62. HttpClient hc = new DefaultHttpClient();
  63. HttpGet hg = new HttpGet("http://csdnimg.cn/www/images/csdnindex_logo.gif");
  64. final Bitmap bm;
  65. try {
  66. HttpResponse hr = hc.execute(hg);
  67. bm = BitmapFactory.decodeStream(hr.getEntity().getContent());
  68. } catch (Exception e) {
  69. e.printStackTrace();
  70. mHandler.obtainMessage(MSG_FAILURE).sendToTarget();
  71. return;
  72. }
  73. mHandler.obtainMessage(MSG_SUCCESS, bm).sendToTarget();
  74. //          mImageView.setImageBitmap(bm); //出错!不能在非ui线程操作ui元素
  75. //           mImageView.post(new Runnable() {//另外一种更简洁的发送消息给ui线程的方法。
  76. //             @Override
  77. //             public void run() {//run()方法会在ui线程执行
  78. //                 mImageView.setImageBitmap(bm);
  79. //             }
  80. //         });
  81. }
  82. };
  83. }

对于上面的方法,我们使用的是handler+Thread来实现更新UI,在里面也有一条注意的就是

  1. mImageView.setImageBitmap(bm); //出错!不能在非ui线程操作ui元素</span>

其实我们上面提到一个方法Activity.runOnUiThread( Runnable ),将这个Runnable以UI线程的方式启动

  1. /**
  2. * Runs the specified action on the UI thread. If the current thread is the UI
  3. * thread, then the action is executed immediately. If the current thread is
  4. * not the UI thread, the action is posted to the event queue of the UI thread.
  5. *
  6. * @param action the action to run on the UI thread
  7. */
  8. public final void runOnUiThread(Runnable action) {
  9. if (Thread.currentThread() != mUiThread) {
  10. mHandler.post(action);
  11. } else {
  12. action.run();
  13. }
  14. }

上面Activity的runOnUiThread(Runnable)方法实现。
利用Activity.runOnUiThread(Runnable)把更新ui的代码创建在Runnable中,然后在需要更新ui时,把这个Runnable对象传给Activity.runOnUiThread(Runnable)。 这样Runnable对像就能在ui程序中被调用。如果当前线程是UI线程,那么行动是立即执行。如果当前线程不是UI线程,操作是发布到事件队列的UI线程。
使用示例:

  1. current_activity.this. runOnUiThread(new Runnable()
  2. @Override
  3. public void run() {
  4. // refresh ui 的操作代码
  5. }
  6. });

这里需要注意的是runOnUiThread是Activity中的方法,在线程中我们需要告诉系统是哪个activity调用,所以前面显示的指明了activity.

所以我们修改一下上面的代码:

  1. package com.example.thread;
  2. import org.apache.http.HttpResponse;
  3. import org.apache.http.client.HttpClient;
  4. import org.apache.http.client.methods.HttpGet;
  5. import org.apache.http.impl.client.DefaultHttpClient;
  6. import com.example.test.R;
  7. import android.annotation.SuppressLint;
  8. import android.app.Activity;
  9. import android.graphics.Bitmap;
  10. import android.graphics.BitmapFactory;
  11. import android.os.Bundle;
  12. import android.os.Handler;
  13. import android.os.Message;
  14. import android.view.View;
  15. import android.view.View.OnClickListener;
  16. import android.widget.Button;
  17. import android.widget.ImageView;
  18. import android.widget.Toast;
  19. public class ThreadHandlerActivity extends Activity{
  20. private static final int MSG_SUCCESS = 0;
  21. private static final int MSG_FAILURE = 1;
  22. private ImageView mImageView;
  23. private Button mButton;
  24. @SuppressLint("HandlerLeak")
  25. private Handler mHandler = new Handler(){
  26. @Override
  27. public void handleMessage(Message msg) {
  28. switch (msg.what) {
  29. case MSG_SUCCESS:
  30. mImageView.setImageBitmap((Bitmap)msg.obj);
  31. Toast.makeText(getApplication(), "成功获取图片", Toast.LENGTH_LONG).show();
  32. break;
  33. case MSG_FAILURE:
  34. Toast.makeText(getApplication(), "获取图片失败", Toast.LENGTH_LONG).show();
  35. break;
  36. }
  37. super.handleMessage(msg);
  38. }
  39. };
  40. @Override
  41. protected void onCreate(Bundle savedInstanceState) {
  42. super.onCreate(savedInstanceState);
  43. setContentView(R.layout.thread_layout);
  44. mImageView= (ImageView) findViewById(R.id.logo);//显示图片的ImageView
  45. mButton = (Button) findViewById(R.id.click);
  46. mButton.setOnClickListener(new OnClickListener() {
  47. @Override
  48. public void onClick(View v) {
  49. ThreadHandlerActivity.this.runOnUiThread(runnable);
  50. }
  51. });
  52. }
  53. Runnable runnable = new Runnable() {
  54. @Override
  55. public void run() {
  56. HttpClient hc = new DefaultHttpClient();
  57. HttpGet hg = new HttpGet("http://csdnimg.cn/www/images/csdnindex_logo.gif");
  58. final Bitmap bm;
  59. try {
  60. HttpResponse hr = hc.execute(hg);
  61. bm = BitmapFactory.decodeStream(hr.getEntity().getContent());
  62. } catch (Exception e) {
  63. e.printStackTrace();
  64. mHandler.obtainMessage(MSG_FAILURE).sendToTarget();
  65. return;
  66. }
  67. mImageView.setImageBitmap(bm);
  68. }
  69. };
  70. }

也可以在线程里面直接更新UI。

有人会说我传递一个当前的Activity到一个线程中,然后实现UI更新,那我就是调用的当前的Activity的内容,其实这个也是不对的也会提示

    1. android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

Android 异步更新UI----handler+thread的更多相关文章

  1. Android异步更新UI的四种方式

    Android异步更新UI的四种方式 2015-09-06 09:23 segmentfault 字号:T | T 大家都知道由于性能要求,android要求只能在UI线程中更新UI,要想在其他线程中 ...

  2. Android 通过广播来异步更新UI

    之前的项目里要做一个异步更新UI的功能,可是结果出现了ANR,所以想写个demo来測试究竟是哪个地方出现了问题,结果发现原来的思路是没有问题,郁闷~~ 如今这个demo 就是模拟项目里面 的步骤 1. ...

  3. Android异步处理系列文章四篇之二 使用AsyncTask异步更新UI界面

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

  4. Android异步处理二:使用AsyncTask异步更新UI界面

    在<Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面>中,我们使用Thread+Handler的方式实现了异步更新UI界面,这一篇中,我们介绍一种更为简 ...

  5. Android 异步更新UI-线程池-Future-Handler实例分析

    前言: 我们在开发Android过程中,在处理耗时任务和UI交互的过程中,都会将耗时任务放到子线程处理并刷新. 下面我提出的两个问题,相信大多数开发者都会碰到: 1. 数据经常需要读取更新,并且比较耗 ...

  6. 使用AsyncTask异步更新UI界面及原理分析

    概述: AsyncTask是在Android SDK 1.5之后推出的一个方便编写后台线程与UI线程交互的辅助类.AsyncTask的内部实现是一个线程池,所有提交的异步任务都会在这个线程池中的工作线 ...

  7. Android多线程更新UI的方式

    Android下,对于耗时的操作要放到子线程中,要不然会残生ANR,本次我们就来学习一下Android多线程更新UI的方式. 首先我们来认识一下anr: anr:application not rep ...

  8. Winform实现多线程异步更新UI(进度及状态信息)

    引言 在进行Winform程序开发需要进行大量的数据的读写操作的时候,往往会需要一定的时间,然在这个时间段里面,界面ui得不到更新,导致在用户看来界面处于假死的状态,造成了不好的用户体验.所以在大量数 ...

  9. Flutter学习笔记(31)--异步更新UI

    如需转载,请注明出处:Flutter学习笔记(31)--异步更新UI 大家都知道,子线程不能操作UI控件,在我们Android的日常开发中,经常会遇到网络请求数据通过线程间通信,将数据发送到UI线程中 ...

  10. handler机制和异步更新UI页面

    Android 提供了Handler和Looper来满足线程之间的通行,Handler是先进先出原则,Looper类用来管理特定线程内对象之间的消息互换,也可以使用Runnable来完成页面异步更新 ...

随机推荐

  1. js进阶 13-1 jquery动画中的显示隐藏函数有哪些

    js进阶 13-1 jquery动画中的显示隐藏函数有哪些 一.总结 一句话总结:show(),hide(),toggle(),这三个. 1.jquery动画中显示隐藏效果函数有哪些? show()h ...

  2. 【重拾Effective Java】一

    之前看这本<Effective Java(第二版)>都是非常早曾经了.这本书确实是本好书.须要细嚼慢咽,每次看都有不同的体验. 在此写博客巩固一下. 第一章.创建和销毁对象 考虑用静态工厂 ...

  3. 关于db2的一点记录

    近期听搞db2的兄弟说:db2数据库软件的license 不区分平台(os). 先记下来.像db2这么高大上的软件,接触的机会是比較少的. 另外:db2 的license是须要打的,不打的话,超过一段 ...

  4. Oracle-18-select语句初步&amp;SQL中用算术表达式&amp;别名的使用&amp;连接运算符%distinct&amp;where子句

    一.一般SELECT语句的格式例如以下: 1.查询指定表的全部列 select * from 表名 [where 条件] [group by 分组列名] [having 聚合函数] [order by ...

  5. Summary Day30

    1.内存管理 1.1 进程中的内存区域划分 代码区   仅仅读常理区    全局区    BSS     堆   栈 1.2 字符串存储形式之间的比較 字符指针,字符数组.字符动态内存 1.3 虚拟内 ...

  6. Topological Spaces(拓扑空间)

    拓扑空间的定义有多种形式,通过 open sets(开集)的形式定义是最为常见的拓扑空间定义形式. 1. 通过开集(open sets)定义 拓扑空间由一个有序对 (X,τ) 表示,X 表示非空集合, ...

  7. Spring中@Async用法详解及简单实例

    Spring中@Async用法 引言: 在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的:但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类 ...

  8. Eclipse RCP 中创建自己定义首选项,并能读取首选项中的值

    Eclipse RCP的插件中若想自定义首选项须要扩展扩展点: org.eclipse.core.runtime.preferences //该扩展点用于初始化首选项中的值 org.eclipse.u ...

  9. [CSS] Easily Reset Styles With a Single CSS value

    There are times where you need to reset a an element’s styles. Instead of overwriting it with even m ...

  10. inflate, findViewById与setContentView的区别与联系 分类: H1_ANDROID 2014-04-18 22:54 1119人阅读 评论(0) 收藏

    protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentV ...