Android--多线程之Handler

原文地址:http://www.cnblogs.com/shirley-1019/p/3557800.html

前言

  Android的消息传递机制是另外一种形式的“事件处理”,这种机制主要是为了解决Android应用中多线程的问题,在Android中不 允许Activity新启动的线程访问该Activity里的UI组件,这样会导致新启动的线程无法改变UI组件的属性值。但实际开发中,很多地方需要在 工作线程中改变UI组件的属性值,比如下载网络图片、动画等等。本篇博客主要介绍Handler是如何发送与处理线程上传递来的消息,并讲解 Message的几种传递数据的方式,最后均会以小Demo来演示。

Handler

  Handler, 它直接继承自Object,一个Handler允许发送和处理Message或者Runnable对象,并且会关联到主线程的MessageQueue 中。每个Handler具有一个单独的线程,并且关联到一个消息队列的线程,就是说一个Handler有一个固有的消息队列。当实例化一个Handler 的时候,它就承载在一个线程和消息队列的线程,这个Handler可以把Message或Runnable压入到消息队列,并且从消息队列中取出 Message或Runnable,进而操作它们。

  Handler主要有两个作用:

  • 在工作线程中发送消息。
  • 在UI线程中获取、处理消息。

  上面介绍到Handler可以把一个Message对象或者Runnable对象压入到消息队列中,进而在UI线程中获取Message或者执行Runnable对象,所以Handler把压入消息队列有两大体系,Post和sendMessage:

  • Post:Post允许把一个Runnable对象入队到消息队列中。它的方法有:post(Runnable)、postAtTime(Runnable,long)、postDelayed(Runnable,long)。
  • sendMessage:sendMessage允许把一个包含消息数据的Message对象压入到消息队列中。它的方法 有:sendEmptyMessage(int)、sendMessage(Message)、 sendMessageAtTime(Message,long)、sendMessageDelayed(Message,long)。

  从上面的各种方法可以看出,不管是post还是sendMessage都具有多种方法,它们可以设定Runnable对象和Message对象被入队到消息队列中,是立即执行还是延迟执行。

  

Post

  对于Handler的Post方式来说,它会传递一个Runnable对象到消息队列中,在这个Runnable对象中,重写run()方法。一般在这个run()方法中写入需要在UI线程上的操作。

  在Handler中,关于Post方式的方法有:

  • boolean post(Runnable r):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,立即执行。
  • boolean postAtTime(Runnable r,long uptimeMillis):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,在特定的时间执行。
  • boolean postDelayed(Runnable r,long delayMillis):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,延迟delayMills秒执行
  • void removeCallbacks(Runnable r):从消息队列中移除一个Runnable对象。

  下面通过一个Demo,讲解如何通过Handler的post方式在新启动的线程中修改UI组件的属性:

  1. package com.bgxt.datatimepickerdemo;
  2.  
  3. import android.app.Activity;
  4. import android.os.Bundle;
  5. import android.os.Handler;
  6. import android.view.View;
  7. import android.widget.Button;
  8. import android.widget.TextView;
  9.  
  10. public class HandlerPostActivity1 extends Activity {
  11. private Button btnMes1,btnMes2;
  12. private TextView tvMessage;
  13. // 声明一个Handler对象
  14. private static Handler handler=new Handler();
  15.  
  16. @Override
  17. protected void onCreate(Bundle savedInstanceState) {
  18. super.onCreate(savedInstanceState);
  19. setContentView(R.layout.message_activity);
  20.  
  21. btnMes1=(Button)findViewById(R.id.btnMes1);
  22. btnMes2=(Button)findViewById(R.id.btnMes2);
  23. tvMessage=(TextView)findViewById(R.id.tvMessage);
  24. btnMes1.setOnClickListener(new View.OnClickListener() {
  25.  
  26. @Override
  27. public void onClick(View v) {
  28. // 新启动一个子线程
  29. new Thread(new Runnable() {
  30. @Override
  31. public void run() {
  32. // tvMessage.setText("...");
  33. // 以上操作会报错,无法再子线程中访问UI组件,UI组件的属性必须在UI线程中访问
  34. // 使用post方式修改UI组件tvMessage的Text属性
  35. handler.post(new Runnable() {
  36. @Override
  37. public void run() {
  38. tvMessage.setText("使用Handler.post在工作线程中发送一段执行到消息队列中,在主线程中执行。");
  39. }
  40. });
  41. }
  42. }).start();
  43. }
  44. });
  45.  
  46. btnMes2.setOnClickListener(new View.OnClickListener() {
  47.  
  48. @Override
  49. public void onClick(View v) {
  50. new Thread(new Runnable() {
  51. @Override
  52. public void run() {
  53. // 使用postDelayed方式修改UI组件tvMessage的Text属性值
  54. // 并且延迟3S执行
  55. handler.postDelayed(new Runnable() {
  56.  
  57. @Override
  58. public void run() {
  59. tvMessage.setText("使用Handler.postDelayed在工作线程中发送一段执行到消息队列中,在主线程中延迟3S执行。");
  60.  
  61. }
  62. }, 3000);
  63. }
  64. }).start();
  65.  
  66. }
  67. });
  68. }
  69. }

 效果展示:

  有一点值得注意的是,对于Post方式而言,它其中Runnable对象的run()方法的代码,均执行在UI线程上,所以对于这段代码而言, 不能执行在UI线程上的操作,一样无法使用post方式执行,比如说访问网络,下面提供一个例子,使用post方式从互联网上获取一张图片,并且显示在 ImageView中。

  1. package com.bgxt.datatimepickerdemo;
  2.  
  3. import org.apache.http.HttpResponse;
  4. import org.apache.http.client.HttpClient;
  5. import org.apache.http.client.methods.HttpGet;
  6. import org.apache.http.impl.client.DefaultHttpClient;
  7. import org.apache.http.util.EntityUtils;
  8.  
  9. import android.app.Activity;
  10. import android.app.ProgressDialog;
  11. import android.graphics.Bitmap;
  12. import android.graphics.BitmapFactory;
  13. import android.os.Bundle;
  14. import android.os.Handler;
  15. import android.view.View;
  16. import android.widget.Button;
  17. import android.widget.ImageView;
  18.  
  19. public class HandlerPostActivity2 extends Activity {
  20. private Button btnDown;
  21. private ImageView ivImage;
  22. private static String image_path = "http://ww4.sinaimg.cn/bmiddle/786013a5jw1e7akotp4bcj20c80i3aao.jpg";
  23. private ProgressDialog dialog;
  24. // 一个静态的Handler,Handler建议声明为静态的
  25. private static Handler handler=new Handler();
  26. @Override
  27. protected void onCreate(Bundle savedInstanceState) {
  28. super.onCreate(savedInstanceState);
  29. setContentView(R.layout.asynctask_activity);
  30.  
  31. btnDown = (Button) findViewById(R.id.btnDown);
  32. ivImage = (ImageView) findViewById(R.id.ivSinaImage);
  33.  
  34. dialog = new ProgressDialog(this);
  35. dialog.setTitle("提示");
  36. dialog.setMessage("正在下载,请稍后...");
  37. dialog.setCancelable(false);
  38.  
  39. btnDown.setOnClickListener(new View.OnClickListener() {
  40. @Override
  41. public void onClick(View v) {
  42. // 开启一个子线程,用于下载图片
  43. new Thread(new MyThread()).start();
  44. // 显示对话框
  45. dialog.show();
  46. }
  47. });
  48. }
  49.  
  50. public class MyThread implements Runnable {
  51.  
  52. @Override
  53. public void run() {
  54. // 下载一个图片
  55. HttpClient httpClient = new DefaultHttpClient();
  56. HttpGet httpGet = new HttpGet(image_path);
  57. HttpResponse httpResponse = null;
  58. try {
  59. httpResponse = httpClient.execute(httpGet);
  60. if (httpResponse.getStatusLine().getStatusCode() == 200) {
  61. byte[] data = EntityUtils.toByteArray(httpResponse
  62. .getEntity());
  63. // 得到一个Bitmap对象,并且为了使其在post内部可以访问,必须声明为final
  64. final Bitmap bmp=BitmapFactory.decodeByteArray(data, 0, data.length);
  65. handler.post(new Runnable() {
  66. @Override
  67. public void run() {
  68. // 在Post中操作UI组件ImageView
  69. ivImage.setImageBitmap(bmp);
  70. }
  71. });
  72. // 隐藏对话框
  73. dialog.dismiss();
  74. }
  75. } catch (Exception e) {
  76. e.printStackTrace();
  77. }
  78. }
  79.  
  80. }
  81. }

效果展示:

Message

  Handler如果使用sendMessage的方式把消息入队到消息队列中,需要传递一个Message对象,而在Handler中,需要重 写handleMessage()方法,用于获取工作线程传递过来的消息,此方法运行在UI线程上。下面先介绍一下Message。

  Message是一个final类,所以不可被继承。Message封装了线程中传递的消息,如果对于一般的数据,Message提供了getData()和setData()方法来获取与设置数据,其中操作的数据是一个Bundle对 象,这个Bundle对象提供一系列的getXxx()和setXxx()方法用于传递基本数据类型的键值对,对于基本数据类型,使用起来很简单,这里不 再详细讲解。而对于复杂的数据类型,如一个对象的传递就要相对复杂一些。在Bundle中提供了两个方法,专门用来传递对象的,但是这两个方法也有相应的 限制,需要实现特定的接口,当然,一些Android自带的类,其实已经实现了这两个接口中的某一个,可以直接使用。方法如下:

  • putParcelable(String key,Parcelable value):需要传递的对象类实现Parcelable接口。
  • pubSerializable(String key,Serializable value):需要传递的对象类实现Serializable接口。

  还有另外一种方式在Message中传递对象,那就是使用Message自带的obj属性传值,它是一个Object类型,所以可以传递任意类型的对象,Message自带的有如下几个属性:

  • int arg1:参数一,用于传递不复杂的数据,复杂数据使用setData()传递。
  • int arg2:参数二,用于传递不复杂的数据,复杂数据使用setData()传递。
  • Object obj:传递一个任意的对象。
  • int what:定义的消息码,一般用于设定消息的标志。

  对于Message对象,一般并不推荐直接使用它的构造方法得到,而是建议通过使用Message.obtain()这个静态的方法或者 Handler.obtainMessage()获取。Message.obtain()会从消息池中获取一个Message对象,如果消息池中是空的, 才会使用构造方法实例化一个新Message,这样有利于消息资源的利用。并不需要担心消息池中的消息过多,它是有上限的,上限为10个。 Handler.obtainMessage()具有多个重载方法,如果查看源码,会发现其实Handler.obtainMessage()在内部也是 调用的Message.obtain()。  

  既然Message是在线程间传递消息,那么先以一个Demo讲解一下Message的使用,还是常规的从互联网上下载一张图片的Demo,下载后使用ImageView控件展示:

  1. package com.bgxt.datatimepickerdemo;
  2.  
  3. import org.apache.http.HttpResponse;
  4. import org.apache.http.client.HttpClient;
  5. import org.apache.http.client.methods.HttpGet;
  6. import org.apache.http.impl.client.DefaultHttpClient;
  7. import org.apache.http.util.EntityUtils;
  8.  
  9. import android.app.Activity;
  10. import android.app.ProgressDialog;
  11. import android.graphics.Bitmap;
  12. import android.graphics.BitmapFactory;
  13. import android.os.Bundle;
  14. import android.os.Handler;
  15. import android.os.Message;
  16. import android.view.View;
  17. import android.widget.Button;
  18. import android.widget.ImageView;
  19.  
  20. public class HandlerMessageActivity1 extends Activity {
  21. private Button btnDown;
  22. private ImageView ivImage;
  23. private static String image_path = "http://ww4.sinaimg.cn/bmiddle/786013a5jw1e7akotp4bcj20c80i3aao.jpg";
  24. private ProgressDialog dialog;
  25. private static int IS_FINISH = 1;
  26.  
  27. @Override
  28. protected void onCreate(Bundle savedInstanceState) {
  29. super.onCreate(savedInstanceState);
  30. setContentView(R.layout.asynctask_activity);
  31.  
  32. btnDown = (Button) findViewById(R.id.btnDown);
  33. ivImage = (ImageView) findViewById(R.id.ivSinaImage);
  34.  
  35. dialog = new ProgressDialog(this);
  36. dialog.setTitle("提示信息");
  37. dialog.setMessage("正在下载,请稍后...");
  38. dialog.setCancelable(false);
  39.  
  40. btnDown.setOnClickListener(new View.OnClickListener() {
  41. @Override
  42. public void onClick(View v) {
  43. new Thread(new MyThread()).start();
  44. dialog.show();
  45. }
  46. });
  47. }
  48.  
  49. private Handler handler = new Handler() {
  50. // 在Handler中获取消息,重写handleMessage()方法
  51. @Override
  52. public void handleMessage(Message msg) {
  53. // 判断消息码是否为1
  54. if(msg.what==IS_FINISH){
  55. byte[] data=(byte[])msg.obj;
  56. Bitmap bmp=BitmapFactory.decodeByteArray(data, 0, data.length);
  57. ivImage.setImageBitmap(bmp);
  58. dialog.dismiss();
  59. }
  60. }
  61. };
  62.  
  63. public class MyThread implements Runnable {
  64.  
  65. @Override
  66. public void run() {
  67. HttpClient httpClient = new DefaultHttpClient();
  68. HttpGet httpGet = new HttpGet(image_path);
  69. HttpResponse httpResponse = null;
  70. try {
  71. httpResponse = httpClient.execute(httpGet);
  72. if (httpResponse.getStatusLine().getStatusCode() == 200) {
  73. byte[] data = EntityUtils.toByteArray(httpResponse
  74. .getEntity());
  75. // 获取一个Message对象,设置what为1
  76. Message msg = Message.obtain();
  77. msg.obj = data;
  78. msg.what = IS_FINISH;
  79. // 发送这个消息到消息队列中
  80. handler.sendMessage(msg);
  81. }
  82. } catch (Exception e) {
  83. e.printStackTrace();
  84. }
  85. }
  86. }
  87. }

展示效果:

  Message.obtain()方法具有多个重载方法,大致可以分为为两类,一类是无需传递Handler对象,对于这类的方法,当填充好消 息后,需要调用Handler.sendMessage()方法来发送消息到消息队列中。第二类需要传递一个Handler对象,这类方法可以直接使用 Message.sendToTarget()方法发送消息到消息队列中,这是因为在Message对象中有一个私有的Handler类型的属性 Target,当时obtain方法传递进一个Handler对象的时候,会给Target属性赋值,当调用sendToTarget()方法的时候,实 际在它内部还是调用的Target.sendMessage()方法。

  在Handler中,也定义了一些发送空消息的方法,如:sendEmptyMessage(int what)、sendEmptyMessageDelayed(int what,long delayMillis),看似这些方法没有使用Message就可以发送一个消息,但是如果查看源码就会发现,其实内部也是从 Message.obtain()方法中获取一个Message对象,然后给属性赋值,最后使用sendMessage()发送消息到消息队列中。

  Handler中,与Message发送消息相关的方法有:

  • Message obtainMessage():获取一个Message对象。
  • boolean sendMessage():发送一个Message对象到消息队列中,并在UI线程取到消息后,立即执行。
  • boolean sendMessageDelayed():发送一个Message对象到消息队列中,在UI线程取到消息后,延迟执行。
  • boolean sendEmptyMessage(int what):发送一个空的Message对象到队列中,并在UI线程取到消息后,立即执行。
  • boolean sendEmptyMessageDelayed(int what,long delayMillis):发送一个空Message对象到消息队列中,在UI线程取到消息后,延迟执行。
  • void removeMessage():从消息队列中移除一个未响应的消息。

  下面通过一个小Demo演示一下各种发送Message的方式:

  1. package com.bgxt.datatimepickerdemo;
  2.  
  3. import android.app.Activity;
  4. import android.os.Bundle;
  5. import android.os.Handler;
  6. import android.os.Message;
  7. import android.view.View;
  8. import android.widget.Button;
  9. import android.widget.TextView;
  10.  
  11. public class HandlerMessageActivity2 extends Activity {
  12. private Button btn1, btn2, btn3, btn4,btn5;
  13. private static TextView tvMes;
  14. private static Handler handler = new Handler() {
  15. @Override
  16. public void handleMessage(android.os.Message msg) {
  17. if (msg.what == 3||msg.what==5) {
  18. tvMes.setText("what=" + msg.what + ",这是一个空消息");
  19. } else {
  20. tvMes.setText("what=" + msg.what + "," + msg.obj.toString());
  21. }
  22.  
  23. };
  24. };
  25.  
  26. @Override
  27. protected void onCreate(Bundle savedInstanceState) {
  28. // TODO Auto-generated method stub
  29. super.onCreate(savedInstanceState);
  30. setContentView(R.layout.message_activity2);
  31. tvMes = (TextView) findViewById(R.id.tvMes);
  32. btn1 = (Button) findViewById(R.id.btnMessage1);
  33. btn2 = (Button) findViewById(R.id.btnMessage2);
  34. btn3 = (Button) findViewById(R.id.btnMessage3);
  35. btn4 = (Button) findViewById(R.id.btnMessage4);
  36. btn5 = (Button) findViewById(R.id.btnMessage5);
  37.  
  38. btn1.setOnClickListener(new View.OnClickListener() {
  39. @Override
  40. public void onClick(View v) {
  41. // 使用Message.Obtain+Hander.sendMessage()发送消息
  42. new Thread(new Runnable() {
  43. @Override
  44. public void run() {
  45. Message msg = Message.obtain();
  46. msg.what = 1;
  47. msg.obj = "使用Message.Obtain+Hander.sendMessage()发送消息";
  48. handler.sendMessage(msg);
  49. }
  50. }).start();
  51. }
  52. });
  53.  
  54. btn2.setOnClickListener(new View.OnClickListener() {
  55.  
  56. @Override
  57. public void onClick(View v) {
  58. // 使用Message.sendToTarget发送消息
  59. new Thread(new Runnable() {
  60. @Override
  61. public void run() {
  62. Message msg = Message.obtain(handler);
  63. msg.what = 2;
  64. msg.obj = "使用Message.sendToTarget发送消息";
  65. msg.sendToTarget();
  66. }
  67. }).start();
  68. }
  69. });
  70.  
  71. btn3.setOnClickListener(new View.OnClickListener() {
  72. // 发送一个延迟消息
  73. @Override
  74. public void onClick(View v) {
  75. new Thread(new Runnable() {
  76. @Override
  77. public void run() {
  78. handler.sendEmptyMessage(3);
  79. }
  80. }).start();
  81. }
  82. });
  83.  
  84. btn4.setOnClickListener(new View.OnClickListener() {
  85.  
  86. @Override
  87. public void onClick(View v) {
  88. new Thread(new Runnable() {
  89. @Override
  90. public void run() {
  91. Message msg = Message.obtain();
  92. msg.what =4;
  93. msg.obj = "使用Message.Obtain+Hander.sendMessage()发送延迟消息";
  94. handler.sendMessageDelayed(msg, 3000);
  95. }
  96. }).start();
  97. }
  98. });
  99.  
  100. btn5.setOnClickListener(new View.OnClickListener() {
  101. // 发送一个延迟的空消息
  102. @Override
  103. public void onClick(View v) {
  104. new Thread(new Runnable() {
  105. @Override
  106. public void run() {
  107. handler.sendEmptyMessageDelayed(5, 3000);
  108. }
  109. }).start();
  110. }
  111. });
  112. }
  113. }

效果展示:

Android-——多线程之Handler(转)的更多相关文章

  1. Android——多线程之Handler

    Why? 因为在Android系统中UI操作并不是线程安全的,如果多个线程并发的去操作同一个组件,可能导致线程安全问题.为了解决这一个问题, android制定了一条规则:只允许UI线程来修改UI组件 ...

  2. Android 多线程之IntentService 完全详解

    关联文章: Android 多线程之HandlerThread 完全详解 Android 多线程之IntentService 完全详解 android多线程-AsyncTask之工作原理深入解析(上) ...

  3. Android 多线程之HandlerThread 完全详解

    关联文章: Android 多线程之HandlerThread 完全详解 Android 多线程之IntentService 完全详解 android多线程-AsyncTask之工作原理深入解析(上) ...

  4. Android开发--多线程之Handler

    前言 Android的消息传递机制是另外一种形式的“事件处理”,这种机制主要是为了解决Android应用中多线程的问题,在Android中不 允许Activity新启动的线程访问该Activity里的 ...

  5. Android--多线程之Handler

    前言 Android的消息传递机制是另外一种形式的“事件处理”,这种机制主要是为了解决Android应用中多线程的问题,在Android中不允许Activity新启动的线程访问该Activity里的U ...

  6. Android--多线程之Handler(转)

    前言 Android的消息传递机制是另外一种形式的“事件处理”,这种机制主要是为了解决Android应用中多线程的问题,在Android中不 允许Activity新启动的线程访问该Activity里的 ...

  7. Android--多线程之Handler 前言

    前言 Android的消息传递机制是另外一种形式的“事件处理”,这种机制主要是为了解决Android应用中多线程的问题,在Android中不 允许Activity新启动的线程访问该Activity里的 ...

  8. [转]Android--多线程之Handler

    原文:http://www.cnblogs.com/plokmju/p/android_Handler.html 前言 Android的消息传递机制是另外一种形式的“事件处理”,这种机制主要是为了解决 ...

  9. MTK Android 耳机线控的实现方法

    android 耳机线控的实现方法 keycodeonkeydownkeyevent 耳机线控的功能 耳机线控是一种很好用,并且能提升用户体验的功能.可以用来实现一些常用和基本的功能.比如:实现音乐播 ...

随机推荐

  1. Emacs 的版本控制功能

    All operations: C-x v + vc-update C-x v = vc-diff C-x v D vc-root-diff C-x v I vc-log-incoming C-x v ...

  2. java开发_eclipse导出为war文件,热部署到tomcat运行总结[转]

    在Myeclipse中,我们很容易做到这一步:把一个web项目生成war文件 其实在eclipse中,实现这样的功能,也是很简单的. 下面就看一下是怎样操作的吧! 新建一个web项目: 取名为:ecl ...

  3. 理解over()函数

    1.1.两个order by的执行时机分析函数(以及与其配合的开窗函数over())是在整个sql查询结束后(sql语句中的order by的执行比较特殊)再进行的操作, 也就是说sql语句中的ord ...

  4. android调节屏幕亮度

    一:只改变当前程序android屏幕亮度(1)方法:lp.screenBrightness 取值 0.0 -- 1.0 ※设定值(float)的范围,默认小于 0(系统设定).0.0(暗)-1.0(亮 ...

  5. android自动弹出软键盘(输入键盘)

        很多应用中对于一个界面比如进入搜索界面或者修改信息等等情况,为了用户体验应该自动弹出软键盘而不是让用户主动点击输入框才弹出(因为用户进入该界面必然是为了更改信息).具体实现这种效果如下: [代 ...

  6. 群智能优化算法-测试函数matlab源码

    群智能优化算法测试函数matlab源代码 global M; creatematrix(2); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %画ackley图. %%%% ...

  7. OpenGL cullface

    opengl cullface是根据顶点顺逆时针来判断正反面的.而不是根据法线判断的.所以有可能法线是正确的,但cullface效果却是反的.

  8. Creating the Help Page in ASP.NET Web API

    Introduction In this article we will define the process of creating the help page in the ASP .NET We ...

  9. Hadoop本地库介绍及相关问题解决方法汇总

    1.hadoop本地库的作用是什么?2.哪两个压缩编码器必须使用hadoop本地库才能运行?3.hadoop的使用方法?4.hadoop本地库与系统版本不一致会引起什么错误?5.$ export HA ...

  10. mini2440裸机音乐播放器(非常久曾经的笔记)

    [这是好久曾经写的.有点乱,没时间整理.当做记录用的.] 图片粘贴失效.没上传图,想要的直接下载文档吧. 项目目的:通过IIS,触摸屏,LCD模块实现音乐播放器功能(button上一首.下一首.播放. ...