分享

Android 开发 Handler的基本使用

 戴维图书馆 2019-10-15

转载请注明出处:http://blog.csdn.net/vnanyesheshou/article/details/72677227

深入理解Handler、Looper、Messagequeue

Android HandlerThread详解

最近看了一篇关于Handler的博客,感觉写的很不错,涉及了Handler的基本使用方法,及Handler、Looper、MessageQueue的深入分析。
原文地址:http://blog.csdn.net/zrf1335348191/article/details/50716983
虽然平常用Handler比较多,但是并没有深刻理解其内部原理,知其然而不知其所以然,为了知其所以然,还是应该好好研究下Handler的。先整理一篇Handler的基本使用。
Andorid是单线程模型的,当一个程序第一次启动时,Android会同时启动一个主线程(Main Thread),主线程主要负责处理与UI相关的事件。Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。
在开发中,我们经常会需要做一些耗时的操作:比如下载图片、打开网页、下载视频等。如果将这些耗时的操作放在主线程(UI线程),长时间的阻塞导致应用ANR。必然应该将这些操作放在子线程中处理,这些操作处理过程中,我们需要更新UI界面以告知用户现在具体的进度、状态等信息。
Handler提供了三种方式解决我们这个问题(在一个新线程中更新主线程中的UI控件),一种是调用sendMessage方法,一种是通过post方法,另一种是obtainMessage。


sendMessage

  1. package cn.vn.hand;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.os.Handler;
  5. import android.os.Message;
  6. import android.view.View;
  7. import android.view.View.OnClickListener;
  8. import android.widget.Button;
  9. import android.widget.TextView;
  10. public class MainActivity extends Activity implements OnClickListener {
  11. private final String TAG = "MainActivity";
  12. private TextView mTipTv;
  13. private Button mDownloadBt;
  14. private boolean isDownloading = false;
  15. public final int MSG_DOWN_FAIL = 1;
  16. public final int MSG_DOWN_SUCCESS = 2;
  17. public final int MSG_DOWN_START = 3;
  18. private Handler mHandler = new Handler() {
  19. public void handleMessage(Message msg) {
  20. switch(msg.what){
  21. case MSG_DOWN_FAIL:
  22. mTipTv.setText("download fial");
  23. break;
  24. case MSG_DOWN_SUCCESS:
  25. mTipTv.setText("download success");
  26. break;
  27. case MSG_DOWN_START:
  28. mTipTv.setText("download start");
  29. break;
  30. }
  31. };
  32. };
  33. @Override
  34. protected void onCreate(Bundle savedInstanceState) {
  35. super.onCreate(savedInstanceState);
  36. setContentView(R.layout.activity_main);
  37. initView();
  38. }
  39. private void initView() {
  40. mTipTv = (TextView) findViewById(R.id.tv_tip);
  41. mDownloadBt = (Button) findViewById(R.id.bt_start);
  42. mDownloadBt.setOnClickListener(this);
  43. }
  44. @Override
  45. public void onClick(View view) {
  46. switch (view.getId()) {
  47. case R.id.bt_start:
  48. if(!isDownloading){
  49. new MyThread().start();
  50. }
  51. break;
  52. default:
  53. break;
  54. }
  55. }
  56. class MyThread extends Thread {
  57. @Override
  58. public void run() {
  59. isDownloading = true;
  60. Log.d(TAG,"MyThread start run");
  61. //发送消息给mHander
  62. mHandler.sendEmptyMessage(MSG_DOWN_START);
  63. try { //让线程睡眠3s。
  64. Thread.sleep(3000);
  65. } catch (InterruptedException e) {
  66. e.printStackTrace();
  67. }
  68. Message msg = new Message();
  69. msg.what = MSG_DOWN_SUCCESS;
  70. //msg.arg1 = 111; 可以设置arg1、arg2、obj等参数,传递这些数据
  71. //msg.arg2 = 222; msg.obj = obj;
  72. mHandler.sendMessage(msg);
  73. isDownloading = false;
  74. Log.d(TAG,"MyThread stop run");
  75. }
  76. }
  77. }

使用步骤:

  1. 在UI线程中创建handler对象mHandler,并实现handleMessage方法,根据Message的what值进行不同的处理操作。
  2. 创建Message对象
  3. 根据需要设置Message的参数,Message.what一般都是必要的,用来区分不同的Message,做出不同的操作。还可以设置Message两个int型字段arg1、arg2。当然除了这简单的数据外,还可以设置携带复杂数据,其obj字段类型为Object类型,可以为任意类类型的数据。也可以通过Message的setData方法设置Bundle类型的数据,可以通过getData方法获取该Bundle数据。
  4. mHandler.sendMessage(Message)方法将Message传入Handler中的消息队列中,然后handleMessage中对消息进行处理。

创建Handler的线程和其handleMessage运行的线程是同一线程,mHandler是在主线程中创建的,所以其handleMessage方法也是在主线程中运行。mHandler.sendMessage(Message)可以在主线程中也可以在子线程中,发送消息的线程与其执行的线程没有联系,最终都会在其创建的线程中处理这些消息。
sendMessage还有许多变形,可以发送空message(只携带what参数)、延时消息、定时消息等。使用方式很简单。
这里写图片描述
对于延时、定时消息,有时我们可能会想取消消息,这就可以通过removeMessages(int what)、或removeMessages(int what, Object object)、removeCallbacksAndMessages(Object token)将指定消息移除。

post

  1. package cn.vn.hand;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.os.Handler;
  5. import android.os.Message;
  6. import android.util.Log;
  7. import android.view.View;
  8. import android.view.View.OnClickListener;
  9. import android.widget.Button;
  10. import android.widget.TextView;
  11. public class MainActivity extends Activity implements OnClickListener {
  12. private final String TAG = "MainActivity";
  13. private TextView mTipTv;
  14. private Button mDownloadBt;
  15. private boolean isDownloading = false;
  16. public final int MSG_DOWN_FAIL = 1;
  17. public final int MSG_DOWN_SUCCESS = 2;
  18. public final int MSG_DOWN_START = 3;
  19. private Handler mHandler = new Handler() {
  20. public void handleMessage(Message msg) {
  21. Log.d(TAG, "handlemessage what="+msg.what);
  22. switch(msg.what){
  23. case MSG_DOWN_FAIL:
  24. mTipTv.setText("download fial");
  25. break;
  26. case MSG_DOWN_SUCCESS:
  27. mTipTv.setText("download success");
  28. break;
  29. case MSG_DOWN_START:
  30. mTipTv.setText("download start");
  31. break;
  32. }
  33. };
  34. };
  35. @Override
  36. protected void onCreate(Bundle savedInstanceState) {
  37. super.onCreate(savedInstanceState);
  38. setContentView(R.layout.activity_main);
  39. initView();
  40. }
  41. private void initView() {
  42. mTipTv = (TextView) findViewById(R.id.tv_tip);
  43. mDownloadBt = (Button) findViewById(R.id.bt_start);
  44. mDownloadBt.setOnClickListener(this);
  45. }
  46. @Override
  47. public void onClick(View view) {
  48. switch (view.getId()) {
  49. case R.id.bt_start:
  50. if(!isDownloading){
  51. //new MyThread().start();
  52. new postThread().start();
  53. }
  54. break;
  55. default:
  56. break;
  57. }
  58. }
  59. class postThread extends Thread{
  60. @Override
  61. public void run() {
  62. isDownloading = true;
  63. Log.d(TAG,"run threadid="+Thread.currentThread().getId()+
  64. ",name="+Thread.currentThread().getName());
  65. try { //让线程睡眠3s。
  66. Thread.sleep(3000);
  67. } catch (InterruptedException e) {
  68. e.printStackTrace();
  69. }
  70. mHandler.post(new Runnable() {
  71. @Override
  72. public void run() {
  73. Log.d(TAG, "Runnable threadid="+Thread.currentThread().getId()
  74. +",name="+Thread.currentThread().getName());
  75. //更新ui
  76. mTipTv.setText("download success");
  77. }
  78. });
  79. isDownloading = false;
  80. }
  81. }
  82. }

Handler的post方法参数为Runnable对象,mHandler是在主线程中创建的,所以Runnalbe会在主线中运行(与Runnable创建的线程无关、与mHandler.post方法调用的线程无关)。

  1. 05-26 03:49:20.877: D/MainActivity(1297): run threadid=99,name=Thread-99
  2. 05-26 03:49:23.877: D/MainActivity(1297): Runnable threadid=1,name=main

有打印可确定Runnable的run方法确实是在主线程中运行的,可以更新UI。
post方法与sendMessage类似也有多个相似方法:
这里写图片描述
post延时、定时处理Runnable也可以进行取消,可以通过removeCallbacks(Runnable r)、removeCallbacks(Runnable r, Object token)、removeCallbacksAndMessages(Object token)方法进行取消。


obtainMessage

obtainMessage与sendmessage类似,也可以看成一种。

  1. class obtainThread extends Thread{
  2. @Override
  3. public void run() {
  4. isDownloading = true;
  5. mHandler.obtainMessage(MSG_DOWN_START).sendToTarget();
  6. try { //让线程睡眠3s。
  7. Thread.sleep(3000);
  8. } catch (InterruptedException e) {
  9. e.printStackTrace();
  10. }
  11. mHandler.obtainMessage(MSG_DOWN_SUCCESS).sendToTarget();
  12. isDownloading = false;
  13. }
  14. }

mHandler.obtainMessage()生成Message对象,此对象携带其target对象,直接调用sendToTarget方法就可以将该消息发送到mHandler对应的消息队列中,然后在mHandler的handleMessage中进行处理。使用和sendMessage类型,都是发送Message对象。

这就差不多说完了,handler的一些基本使用方法,下篇分析下其原理。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多