Android中的Handler, Looper, MessageQueue和Thread

前几天,和同事探讨了一下Android中的消息机制,探究了消息的发送和接收过程以及与线程之间的关系。虽然我们经常使用这些基础的东西,但对于其内部原理的了解,能使我们更加容易、合理地架构系统,并避免一些低级错误。

 

对于这部分的内容,将分成4小节来描述:

 

1.职责与关系

 

2.消息循环

 

3.线程与更新

 

4.几点小结

 

--------------------------------------------------------------------------------------------------

 

1) 接下来,我们开始这部分的内容,首先了解一下各自的职责及相互之间的关系。

 

职责

 

Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。

 

Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。

 

MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。

 

Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。

 

Thread:线程,负责调度整个消息循环,即消息循环的执行场所。

 

关系

 

 

 

Handler,Looper和MessageQueue就是简单的三角关系。Looper和MessageQueue一一对应,创建一个 Looper的同时,会创建一个MessageQueue。而Handler与它们的关系,只是简单的聚集关系,即Handler里会引用当前线程里的特定Looper和MessageQueue。

 

这样说来,多个Handler都可以共享同一Looper和MessageQueue了。当然,这些Handler也就运行在同一个线程里。

 

2) 接下来,我们简单地看一下消息的循环过程:

 

生成

 

       Message msg = mHandler.obtainMessage();

 

       msg.what = what;

 

       msg.sendToTarget();

 

 

 

发送

 

       MessageQueue queue = mQueue;

 

        if (queue != null) {

 

            msg.target = this;

 

            sent = queue.enqueueMessage(msg, uptimeMillis);

 

        }

 

在Handler.java 的sendMessageAtTime(Message msg, long uptimeMillis)方法中,我们看到,它找到它所引用的MessageQueue,然后将Message的target设定成自己(目的是为了在处理消息环节,Message能找到正确的Handler),再将这个Message纳入到消息队列中。

 

抽取

 

        Looper me = myLooper();

 

        MessageQueue queue = me.mQueue;

 

        while (true) {

 

            Message msg = queue.next(); // might block

 

            if (msg != null) {

 

                if (msg.target == null) {

 

                    // No target is a magic identifier for the quit message.

 

                    return;

 

                }

 

                msg.target.dispatchMessage(msg);

 

                msg.recycle();

 

            }

 

        }

 

在Looper.java 的loop()函数里,我们看到,这里有一个死循环,不断地从MessageQueue中获取下一个(next方法)Message,然后通过Message中携带的target信息,交由正确的Handler处理(dispatchMessage方法)。

 

 

 

处理

 

        if (msg.callback != null) {

 

            handleCallback(msg);

 

        } else {

 

            if (mCallback != null) {

 

                if (mCallback.handleMessage(msg)) {

 

                    return;

 

                }

 

            }

 

            handleMessage(msg);

 

        }

 

在Handler.java的dispatchMessage(Message msg)方法里,其中的一个分支就是调用handleMessage方法来处理这条Message,而这也正是我们在职责处描述使用Handler时需要实现handleMessage(Message msg)的原因。

 

至于dispatchMessage方法中的另外一个分支,我将会在后面的内容中说明。

 

至此,我们看到,一个Message经由Handler的发送,MessageQueue的入队,Looper的抽取,又再一次地回到Handler的怀抱。而绕的这一圈,也正好帮助我们将同步操作变成了异步操作。

 

3)剩下的部分,我们将讨论一下Handler所处的线程及更新UI的方式。

 

在主线程(UI线程)里,如果创建Handler时不传入Looper对象,那么将直接使用主线程(UI线程)的Looper对象(系统已经帮我们创建了);在其它线程里,如果创建Handler时不传入Looper对象,那么,这个Handler将不能接收处理消息。在这种情况下,通用的作法是:

 

                class LooperThread extends Thread {

 

                               public Handler mHandler;

 

                               public void run() {

 

                                               Looper.prepare();

 

                                               mHandler = new Handler() {

 

                                                               public void handleMessage(Message msg) {

 

                                                                              // process incoming messages here

 

                                                               }

 

                                               };

 

                                               Looper.loop();

 

                               }

 

                }

 

在创建Handler之前,为该线程准备好一个Looper(Looper.prepare),然后让这个Looper跑起来(Looper.loop),抽取Message,这样,Handler才能正常工作。

 

因此,Handler处理消息总是在创建Handler的线程里运行。而我们的消息处理中,不乏更新UI的操作,不正确的线程直接更新UI将引发异常。因此,需要时刻关心Handler在哪个线程里创建的。

 

如何更新UI才能不出异常呢?SDK告诉我们,有以下4种方式可以从其它线程访问UI线程:

 

·      Activity.runOnUiThread(Runnable)

 

·      View.post(Runnable)

 

·      View.postDelayed(Runnable, long)

 

·      Handler

 

其中,重点说一下的是View.post(Runnable)方法。在post(Runnable action)方法里,View获得当前线程(即UI线程)的Handler,然后将action对象post到Handler里。在Handler里,它将传递过来的action对象包装成一个Message(Message的callback为action),然后将其投入UI线程的消息循环中。在 Handler再次处理该Message时,有一条分支(未解释的那条)就是为它所设,直接调用runnable的run方法。而此时,已经路由到UI线程里,因此,我们可以毫无顾虑的来更新UI。

 

4) 几点小结

 

·      Handler的处理过程运行在创建Handler的线程里

 

·      一个Looper对应一个MessageQueue

 

·      一个线程对应一个Looper

 

·      一个Looper可以对应多个Handler

 

·      不确定当前线程时,更新UI时尽量调用post方法

 

转自:http://www.open-open.com/lib/view/open1327558863765.html

转载于:https://www.cnblogs.com/Mr-Hannibal/archive/2012/02/04/2337845.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/422579.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java平面_java知识点整理

一、http和tcp/ip的区别?http是应用层协议,主要解决如何封装数据。tcp/ip则是传输层协议,主要解决数据如何在网络中传递。web应用使用http协议作为应用层协议来封装成http文本协议,然后使用tcp/ip做传输层协议将信息发布到网络上。…

设计模式—适配器模式

一、概念 将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。——Gang of Four Adapter模式的宗旨:保留现有类所提供的服务,向客户提供接口,以满足客户的期望。 对象适配…

android UI进阶之实现listview的分页加载

上篇博文和大家分享了下拉刷新,这是一个用户体验非常好的操作方式。新浪微薄就是使用这种方式的典型。 还有个问题,当用户从网络上读取微薄的时候,如果一下子全部加载用户未读的微薄这将耗费比较长的时间,造成不好的用户体验&…

java stringutils_Java 笔记----- StringUtils 拼接字符

import org.apache.commons.lang3.StringUtils;StringUtils 提供了 join 方法用来 按照自定义的拼接符拼接join实现源码:感兴趣的可以看看public static String join(Iterator> iterator, String separator) {// handle null, zero and one elements before buil…

EJB分布式对象实现方式描述

在Java RMI中要求所有的参数传递和返回类型是JAVA的基本类型或实现Serilizable的对象。串行化对象通过值传递(又名拷贝传递),而不是引用传递。这意味着在某一层中串行化对象的更新并不自动影响到其它的对象。 对于EJB对象而言,它由…

iis5.0+php5.0+mysql5.0配置完全手册_IIS5.0+PHP5.0+MySQL5.0配置完全手册

IIS5.0PHP5.0MySQL5.0配置完全手册环境:windows Xp sp2 IIS5.0 MySQL 5.0 PHP 5.0.4 使用PHPWind3.0论坛程序使用PHPmyadmin2.6.3适合初学者的配置1.首先是安装IIS5.0xp默认下是没有安装IIS的"控制面板"> "添加删除程序"> "添加\删除windows组件…

蓝桥杯java提交格式_2019第十届蓝桥杯JAVA省赛B组

B 不同子串题目描述一个字符串的非空子串是指字符串中长度至少为 1 的连续的一段字符组成 的串。例如,字符串aaab 有非空子串a, b, aa, ab, aaa, aab, aaab,一共 7 个。 注意在计算时,只算本质不同的串的个数。请问,字符串0100110…

浅析Java与C#的事件处理机制(转)

Java与C#的事件处理都是实现了事件源-事件响应者机制,但又不完全相同。Java实现的是一种事件源与事件响应者两级实体对象方式,这里的事件响应者也是事件监听者,而C#实现的是一种事件源-代理-事件响应者三级实体对象方式。下面就这两种方式来具…

java xml接口实例化_Spring简介及xml配置

Java Web发展史第一阶段:JavaBeanServletJsp逐步发展第二阶段:面对EJB重量级框架带来的种种麻烦第三阶段:SpringMVC/StrutsSpringHibernate/myBatis第四阶段:享受SpringBoot"约定大于配置"的种种乐趣,很多繁琐的配置都变成了约定第五阶段:以Dubbo为代表的…

员工薪酬管理设计方案

待遇管理模块目前我遇到的要分两种方案,第一种是集团版的(有子公司、区域公司);第二种是标准版的(就是一个标准的公司,不存在什么子公司),下面就谈谈标准版个人是如何处理的。 特别感谢:江琴童鞋、罗静童鞋两位财务人员…

文件上传简介1---上传到指定的目录

preparation 本节摘要:本节主要介绍上传文件到指定目录。 引入: 文件上传是开发中常用的功能,本节主要介绍用commons-fileupload-1.1.jar包实现基本的文件上传功能,即上传文件到指定的目录中,同时介绍上传过程中使用到…

null和undefined相等

<script language"javascript"><!-- var v0 1; // 定义一个值为1的变量 var v1 null; // 定义一个变量&#xff0c;并指定值为null var v2; // 定义一个变量…

java 文件inputstream_java – 如何在InputStream中接收多个文件并相应地处理它?

我想收到从客户端上传的多个文件.我上传了多个文件并使用JAX-RS(Jersey)请求我的服务器端(Java).我有以下代码,POSTConsumes(MediaType.MULTIPART_FORM_DATA)public void upload(Context UriInfo uriInfo,FormDataParam("file") final InputStream is,FormDataParam(…

java驱动pl sql优点_用PL/SQL和Java开发Oracle8i应用程序

用PL/SQL和Java开发Oracle8 i应用程序随着Oracle8i的发布&#xff0c;Oracle 在数据库里支持了二种主要的编程语言??PL/SQL和Java。今天&#xff0c;Oracle的许多客户既使用PL/SQL建立数据库应用程序&#xff0c;也使用Java建立数据库应用程序。既然存在二种数据库编程语言&a…

java客户姓名添加和查找_java类与对象 演练 客户姓名添加与查看

java类与对象 演练 客户姓名添加与查看java类与对象 演练 客户姓名添加与查看https://www.ixigua.com/6870483106223096327?logTag_YUmIb2rn9-6JpoNipj9r属性由于需要保存很多名字因此来一个字符串的数组保存字符串的名字即可考虑&#xff0c;定义多个长度的数组&#xff0c;例…

Winform登录窗体登录仿asp.net验证成功后进入主界面

一、登录界面验证成功后进入主界面&#xff0c;否则则退出应用程序。 假设登录窗体frmLogin.cs和主界面frmMain.cs在程序的Main入口这样来实现&#xff1a; frmLogin login new frmLogin(); login.ShowDialog(); if (frmLogin.blCanLogin true) { Application.Run(new frm…

应用程序调试技术(更新程度:完毕)送源码及PPT

课程简介 调试技术实务分为调试基本概念和设置&#xff0c;Windows下常用到调试工具VS和Windbg的使用&#xff0c;自动化调试技术&#xff0c;以及实际调试场景示例等。不仅仅是介绍枯燥的程序调试技术&#xff0c;而是深入的从操作系统实现以及.NET虚拟机实现的本质出发&#…

mysql数据库的变量有哪些_[数据库]MySQL变量

[数据库]MySQL变量0 2021-02-08 04:01:23原文链 id"mysql变量">MySQL变量MySQL变量分为系统变量和自定义变量系统变量系统变量有全局变量和会话变量查看系统变量#查看全局系统变量show global variables;#根据条件查询全局系统变量show global variables like %%;…

安装ie浏览器的js脚本调试工具

在ie中&#xff0c;如果不依赖外部工具进行js调试&#xff0c;而使用原始的alert的话&#xff0c;会大大增加js调试的难度。而微软的office、visual studio里边都带了可以支持ie调试的工具。 在安装office(2003、2007带此工具&#xff0c;2010不带此工具)时&#xff0c;选择自定…