为了更好地理解Handler的工作原理,先介绍一下与Handler一起工作的几个组件。

  • Message:Handler接收和处理的消息对象。
  • Looper:每个线程只能拥有一个Looper。它的loop方法负责读取MessageQueue中的消息,读取到消息之后就把消息交给发送该消息的Handler进行处理。
  • MessageQueue:消息队列,它采用先进先出的方式来管理Message。程序创建Looper对象时会在它的构造器中创建MessageQueue对象。Looper提供的构造器源代码如下:
  1. private Looper(boolean quitAllowed) {
  2. mQueue = new MessageQueue(quitAllowed);
  3. mRun = true;
  4. mThread = Thread.currentThread();
  5. }

该构造器使用了private修饰,表明程序员无法通过构造器创建Looper对象。从上面的代码不难看出,程序在初始化Looper时会创建一个与之关联的MessageQueue,这个MessageQueue就负责管理消息。

  • Handler:它的作用有两个——发送消息和处理消息,程序使用Handler发送消息,被Handler发送的消息必须被送到指定的MessageQueue。也就是说,如果希望Handler正常工作,必须在当前线程中有一个MessageQueue,否则消息就没有MessageQueue进行保存了。不过MessageQueue是由Looper负责管理的,也就是说,如果希望Handler正常工作,必须在当前线程中有一个Looper对象。为了保证当前线程中有Looper对象,可以分如下两种情况处理。
  1. 主UI线程中,系统已经初始化了一个Looper对象,因此程序直接创建Handler即可,然后就可通过Handler来发送消息、处理消息。
  2. 程序员自己启动的子线程,程序员必须自己创建一个Looper对象,并启动它 。创建Looper对象调用它的prepare()方法即可。

prepare()方法保证每个线程最多只有一个Looper对象。prepare()方法的源代码如下:

  1. private static void prepare(boolean quitAllowed) {
  2. if (sThreadLocal.get() != null) {
  3.  
  4. throw new RuntimeException("Only one Looper may be created per thread");
  5.  
  6. }
  7. sThreadLocal.set(new Looper(quitAllowed));
  8. }

然后调用Looper的静态loop()方法来启动它。loop()方法使用一个死循环不断取出MessageQueue中的消息,并将取出的消息分给该消息对应的Handler进行处理。下面是Looper类的loop()方法的源代码:

  1. public static void loop() {
  2. final Looper me = myLooper();
  3. if (me == null) {
  4. throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
  5. }
  6. final MessageQueue queue = me.mQueue;
  7.  
  8. // Make sure the identity of this thread is that of the local process,
  9. // and keep track of what that identity token actually is.
  10. Binder.clearCallingIdentity();
  11.  
  12. final long ident = Binder.clearCallingIdentity();
  13. for (;;) {
  14. Message msg = queue.next(); // might block
  15. if (msg == null) {
  16.  
  17. // No message indicates that the message queue is quitting.
  18. return;
  19. }
  20.  
  21. // This must be in a local variable, in case a UI event sets the logger
  22. Printer logging = me.mLogging;
  23. if (logging != null) {
  24. logging.println(">>>>> Dispatching to " + msg.target + " " +
  25. msg.callback + ": " + msg.what);
  26. }
  27. msg.target.dispatchMessage(msg);
  28. if (logging != null) {
  29. logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
  30. }
  31. // Make sure that during the course of dispatching the
  32. // identity of the thread wasn't corrupted.
  33. final long newIdent = Binder.clearCallingIdentity();
  34. if (ident != newIdent) {
  35. Log.wtf(TAG, "Thread identity changed from 0x"
  36. + Long.toHexString(ident) + " to 0x"
  37. + Long.toHexString(newIdent) + " while dispatching to "
  38. + msg.target.getClass().getName() + " "
  39. + msg.callback + " what=" + msg.what);
  40. }
  41. msg.recycle();
  42. }
  43. }

归纳起来,Looper、MessageQueue、Handler各自的作用如下。

  • Looper:每个线程只有一个Looper,它负责管理MessageQueue,会不断地从MessageQueue中取出消息,并将消息分给对应的Handler处理。
  • MessageQueue:由Looper负责管理。它采用先进先出的方式来管理Message。
  • Handler:它能把消息发送给Looper管理的MessageQueue,并负责处理Looper分给它的消息。

在线程中使用Handler的步骤如下。

  1. 调用Looper的prepare()方法为当前线程创建Looper对象,创建Looper对象时,它的构造器会创建于之配套的MessageQueue。
  2. 有了Looper之后,创建Handler子类的实例,重写handleMessage()方法,该方法负责处理来自于其他线程的消息。
  3. 调用Looper的loop()方法启动Looper。

下面通过实例来介绍Looper与Handler的用法。

实例:使用新线程计算质数        

该实例允许用户输入一个数值上限,当用户单击“计算”按钮时,该应用会将上限数值发送到新启动的线程,该线程来计算该范围内的所有质数。

之所以不直接在UI线程中计算该范围的所有质数,是因为UI线程需要相应用户动作,如果在UI线程中执行一个”耗时“操作,将会导致UI线程被阻塞,从而让应用程序失去响应。比如在该实例中,如果用户输入的数值太大,系统可能需要较长时间才能计算出所有质数,这就可能导致UI线程失去响应。

为了将用户在UI界面输入的数值上限动态的传给新启动的线程,本实例将会在线程中创建一个Handler对象,然后UI线程的事件处理方法就可以通过该Handler向新线程发送消息了。

该实例的界面布局文件如下:

  1. <LinearLayout
  2. xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:orientation="vertical">
  6. <EditText
  7. android:id="@+id/etNum"
  8. android:inputType="number"
  9. android:layout_width="match_parent"
  10. android:layout_height="wrap_content"
  11. android:hint="请输入上限"/>
  12. <Button
  13. android:layout_width="match_parent"
  14. android:layout_height="wrap_content"
  15. android:onClick="cal"
  16. android:text="计算"/>
  17. </LinearLayout>

该实例的Activity代码如下。

  1. package com.example.studyevent;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.List;
  5.  
  6. import android.os.Bundle;
  7. import android.os.Handler;
  8. import android.os.Looper;
  9. import android.os.Message;
  10. import android.app.Activity;
  11. import android.view.Menu;
  12. import android.view.View;
  13. import android.widget.EditText;
  14. import android.widget.Toast;
  15.  
  16. public class CalPrime extends Activity {
  17. private final String UPPER_NUM="upper";
  18. EditText etNum;
  19. CalThread calThread;
  20. //定义一个线程类
  21. class CalThread extends Thread{
  22. public Handler mHandler;
  23. public void run()
  24. {
  25. Looper.prepare();
  26. mHandler=new Handler()
  27. {
  28. //定义处理消息的方法
  29. @Override
  30. public void handleMessage(Message msg) {
  31. // TODO Auto-generated method stub
  32. if(msg.what==0x123)
  33. {
  34. int upper=msg.getData().getInt(UPPER_NUM);
  35. List<Integer> nums=new ArrayList<Integer>();
  36. //计算从2开始、到upper的所有质数
  37. outer:
  38. for(int i=2;i<=upper;i++)
  39. {
  40. //用i除以从2开始、到i的平方根的所有数
  41. for(int j=2;j<=Math.sqrt(i);j++)
  42. {
  43. //如果可以整除,则表明这个数不是质数
  44. if(i!=2&&i%j==0)
  45. {
  46. continue outer;
  47. }
  48. }
  49. nums.add(i);
  50. }
  51. //使用Toast显示统计出来的所有质数
  52. Toast.makeText(CalPrime.this,nums.toString(),Toast.LENGTH_LONG).show();
  53. }
  54. }
  55. };
  56. Looper.loop();
  57. }
  58.  
  59. }
  60. @Override
  61. protected void onCreate(Bundle savedInstanceState) {
  62. super.onCreate(savedInstanceState);
  63. setContentView(R.layout.activity_cal_prime);
  64. etNum=(EditText)findViewById(R.id.etNum);
  65. calThread=new CalThread();
  66. //启动新线程
  67. calThread.start();
  68. }
  69. //为按钮的点击事件提供事件处理函数
  70. public void cal(View source)
  71. {
  72. //创建消息
  73. Message msg=new Message();
  74. msg.what=0x123;
  75. Bundle bundle=new Bundle();
  76. bundle.putInt(UPPER_NUM, Integer.parseInt(etNum.getText().toString()));
  77. msg.setData(bundle);
  78. //向新线程中的Handler发送消息
  79. calThread.mHandler.sendMessage(msg);
  80.  
  81. }
  82. @Override
  83. public boolean onCreateOptionsMenu(Menu menu) {
  84. // Inflate the menu; this adds items to the action bar if it is present.
  85. getMenuInflater().inflate(R.menu.cal_prime, menu);
  86. return true;
  87. }
  88.  
  89. }

上面的粗体字代码是实例的关键代码,这些粗体字代码在新线程内创建了一个Handler,由于在新线程中创建Handler时必须创建Looper,因此程序先调用Looper()的prpare()方法为当前线程创建了一个Looper实例,并创建配套的MessageQueue,新线程有了Looper对象之后,接下来程序创建了一个Handler对象,该Handler可以处理其他线程发送过来的消息。程序还调用了Looper的loop()方法。

运行该程序,无论用户输入多大的数值,计算该范围的质数将会交给新线程完成,而前台UI线程不会受影响。该线程运行效果如图所示。

Handler消息传递机制——Handler、Loop、MessageQueue的工作原理的更多相关文章

  1. Handler类和Handler,Loop,MessageQueue的工作原理

    原文地址:http://blog.csdn.net/xiyangyang8/article/details/50754771 Handler类的作用主要有两种: 1.在新启动的线程中发送消息. 2.在 ...

  2. Handler消息传递机制——Handler类简洁

    Handler类的主要作用有两个: 在新启动的线程中发送消息. 在主线程中获取.处理消息. 上面的说法很简单,只要分成两步即可:在新启动的线程中发送消息:然后在主线程上获取.并处理消息.但这个过程涉及 ...

  3. 事件处理机制与Handler消息传递机制

    一.基于监听的事件处理机制 基于监听的时间处理机制模型: 事件监听机制中由事件源,事件,事件监听器三类对象组成 处理流程如下: Step 1:为某个事件源(组件)设置一个监听器,用于监听用户操作 St ...

  4. 安卓开发_深入理解Handler消息传递机制

    一.概述 因为子线程的run()方法无法修改UI线程(主线程)的UI界面,所以Android引入了Handler消息传递机制,实现在新创建的线程中操作UI界面 二.消息类(Message) 消息类是存 ...

  5. Android学习笔记-事件处理之Handler消息传递机制

    内容摘要:Android Handler消息传递机制的学习总结.问题记录 Handler消息传递机制的目的: 1.实现线程间通信(如:Android平台只允许主线程(UI线程)修改Activity里的 ...

  6. Handler消息传递机制

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

  7. 解析Android的 消息传递机制Handler

    1. 什么是Handler: Handler 网络释义"机械手.经理"意思,在Android它用于管理多个线程UI操作: 2. 为什么会出现Handler: 在Android里面的 ...

  8. 3.3 线程---Handler消息传递机制浅析

    Handler的执行流程图: 当我们的子线程想修改Activity中的UI组件时,我们可以新建一个Handler对象,通过这个对象向主线程发送信息;而我们发送的信息会先到主线程的MessageQueu ...

  9. Android学习之Handler消息传递机制

    Android只允许UI线程修改Activity里的UI组件.当Android程序第一次启动时,Android会同时启动一条主线程(Main Thread),主线程主要负责处理与UI相关的事件,如用户 ...

随机推荐

  1. hrbustoj 2013 Play Game 2(博弈)

    注释在代码里 /* 1.若输入2 ~ 9 ,因为Stan 是先手,所以Stan 必胜 2.若输入10~18 ,因为Ollie 是后手,不管第一次Stan 乘的是什么,Stan肯定在 2 ~ 9 之间, ...

  2. Unity 之 c# 版的 CharacterMotor

    using System; using System.Collections; using UnityEngine; // This class just convert from Character ...

  3. python之路: 基础篇

    )或>>> name = )    #按照占位符的顺序):]        #下标识从0开始的 wulaoer >>> print name[:]        # ...

  4. 关于在TabBar 中添加按钮,并通过block 或代理在控制器中实现响应

    相信很多朋友会遇到在TabBar中添加按钮,并要求点击按钮能够实现一些功能,但是当我们自定义的时候,怎么才能在控制器中响应?通常我会用代理或者block,block性能更好,建议使用. 自定义TabB ...

  5. [Lua]Mac系统上安装Lua环境

    1.下载 Lua语言的官方网站 http://www.lua.org/ 下载最新版本的Lua环境 2.安装 解压下载包lua-5.3.1.tar.gz 打开终端Terminal 使用cd命令进入该目录 ...

  6. laravel5 html引用问题

    1. Composer 安装 编辑 composer.json 文件, require 节点下增加: "illuminate/html": "~5.0" 然后 ...

  7. 左偏树初步 bzoj2809 & bzoj4003

    看着百度文库学习了一个. 总的来说,左偏树这个可并堆满足 堆的性质 和 左偏 性质. bzoj2809: [Apio2012]dispatching 把每个忍者先放到节点上,然后从下往上合并,假设到了 ...

  8. iOS开发——导入第三方库引起的unknown type name 'NSString'

    今天加入SVProgressHUD的第三方库的时候报了24个错误( too many errors emitted, stopping now),都是 expected identifier or ' ...

  9. javascript--study

    1.函数传参:按值传递 对于数字.字符串等是将它们的值传递给了函数参数,函数参数的改变不会影响函数外部的变量. 对于数组和对象等是将对象(数组)的变量的值传递给了函数参数,这个变量保存的指向对象(数组 ...

  10. jQuery API 中文文档

    Reference: http://www.css88.com/jqapi-1.9/jQuery.proxy/