Android 开发 深入理解Handler、Looper、Messagequeue 转载
转载请注明出处:http://blog.csdn.net/vnanyesheshou/article/details/73484527
本文已授权微信公众号 fanfan程序媛 独家发布 扫一扫文章底部的二维码或在微信搜索 fanfan程序媛 即可关注
上一篇总结了一下Handler的基本用法,但是对于其原理并不太清楚,这篇主要分析下其内部的原理。看一下其源码是怎么回事,从源码的角度理解Handler机制,Handler、Looper、MessageQueue之间的关系。
简介
先对这几个类做一下简单介绍。
Handler:
线程间通信的方式,主要用来发送消息及处理消息。
Looper:
为线程运行消息循环的类,循环取出MessageQueue中的Message;消息派发,将取出的Message交付给相应的Handler。
MessageQueue:
存放通过Handler发过来的消息,遵循先进先出原则。
Message:消息,线程间通信通讯携带的数据。
例如后台线程在处理数据完毕后需要更新UI,则可发送一条包含更新信息的Message给UI线程
Looper
源码路径:frameworks/base/core/java/android/os/Looper.java
Looper主要工作:
- 自身实例的创建,创建消息队列,保证一个线程中最多有一个Looper实例。
- 消息循环,从消息队列中取出消息,进行派发。
Looper用于为线程运行消息循环的类,默认线程没有与它们相关联的消息循环;如果要想在子线程中进行消息循环,则需要在线程中调用prepare(),创建Looper对象。然后通过loop()方法来循环读取消息进行派发,直到循环结束。
程序中使用Looper的地方:
- 主线程(UI线程)
UI线程中Looper已经都创建好了,不用我们去创建和循环。 - 普通线程
普通线程中使用Looper需要我们自己去prepare()、loop()。
看一下普通线程中创建使用Looper的方式,代码如下:
- 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();
- }
- }
这段代码是Looper源码注释中给的典型列子,主要步骤:
Looper 准备,(Looper实例创建);
- 创建发送消息、处理消息的Handler对象;
- Looper开始运行。
印象中在UI线程没有出现过Looper相关的东东,这是因为UI线程中会自动创建Looper对象并进行消息循环,我们不再需要调用Looper.prepare()和Looper.loop(),但是在子线程中如果想要创建使用Handelr则需要向如上所示。
我们通过源码看一下Looper实例创建的方法:
- public static void prepare() {
- prepare(true);
- }
- private static void prepare(boolean quitAllowed) {
- if (sThreadLocal.get() != null) {
- throw new RuntimeException("Only one Looper may be created per thread");
- }
- sThreadLocal.set(new Looper(quitAllowed));
- }
Looper构造方法是私有的,只能通过prepare()进行创建Looper对象。prepare()会调用私有方法prepare(boolean quitAllowed)。
第6行 sThreadLocal为ThreadLocal类型变量,用来存储线程中的Looper对象。
prepare方法中首先判断sThreadLocal是否存储对象,如果存储了则抛出异常,这是因为在同一个线程中Loop.prepare()方法不能调用两次,也就是同一个线程中最多有一个Looper实例(当然也可以没有,如果子线程不需要创建Handler时)。
该异常应该许多朋友都遇见过,如在UI线程中调用Looper.prepare(),系统会替UI线程创建Looper实例,所以不需要再次调用prepare()。
接着看Looper的构造器:
- private Looper(boolean quitAllowed) {
- mQueue = new MessageQueue(quitAllowed);
- mRun = true;
- mThread = Thread.currentThread();
- }
在构造器中,创建了一个MessageQueue消息队列;然后获取当前的线程,使Looper实例与线程绑定。
由prepare方法可知一个线程只会有一个Looper实例,所以一个Looper实例也只有一个MessageQueue实例。但这并不代表一个线程只能有一个MessageQueue实例,这是为什么呢?很简单,我们可以自己new 一个MessageQueue实例就可以了,但这个MessageQueue并不是该线程中Handelr对应的消息队列。
接着看Looper的消息循环:
- public static void loop() {
- final Looper me = myLooper();
- if (me == null) {
- throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
- }
- //通过Looper实例获取消息队列
- final MessageQueue queue = me.mQueue;
- // Make sure the identity of this thread is that of the local process,
- // and keep track of what that identity token actually is.
- Binder.clearCallingIdentity();
- final long ident = Binder.clearCallingIdentity();
- for (;;) { //消息循环
- //从消息队列中取出一条消息,如果没有消息则会阻塞。
- Message msg = queue.next(); // might block
- if (msg == null) {
- // No message indicates that the message queue is quitting.
- return;
- }
- // This must be in a local variable, in case a UI event sets the logger
- Printer logging = me.mLogging;
- if (logging != null) {
- logging.println(">>>>> Dispatching to " + msg.target + " " +
- msg.callback + ": " + msg.what);
- }
- //将消息派发给target属性对应的handler,调用其dispatchMessage进行处理。
- msg.target.dispatchMessage(msg);
- if (logging != null) {
- logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
- }
- // Make sure that during the course of dispatching the
- // identity of the thread wasn't corrupted.
- final long newIdent = Binder.clearCallingIdentity();
- if (ident != newIdent) {
- //log
- }
- msg.recycle();
- }
- }
loop()函数是静态的,所以它只能访问静态数据。
第2行myLooper()函数也是静态的,其代码如下
- return sThreadLocal.get()
获取sThreadLocal存储的Looper实例,如果为空则抛出异常,这也说明loop()方法必须在prepare()方法之后才能调用。
第7行 通过Looper对象获取消息队列。
然后进行消息循环,从队列中获取消息,把消息交给msg的target的dispatchMessage方法进行处理,也就是交给handler进行处理,这个稍后说Handler时再细说。
然后调用msg.recycle(); 释放msg。
Looper.loop()是给死循环,那如何终止消息循环呢?我们可以调用Looper的quit方法或quitSafely方法。
quit方法作用是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息还是非延迟消息。
quitSafely只会清空MessageQueue消息队列中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理,quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息。
总结:
- UI线程会自动创建Looper实例、并且调用loop()方法,不需要我们再调用prepare()和loop().
- Looper与创建它的线程绑定,确保一个线程最多有一个Looper实例,同时一个Looper实例只有一个MessageQueue实例。
- loop()函数循环从MessageQueue中获取消息,并将消息交给消息的target的dispatchMessage去处理。如果MessageQueue中没有消息则获取消息可能会阻塞。
- 通过调用Looper的quit或quitsafely终止消息循环。
Handler
源码路径:frameworks/base/core/java/android/os/Handler.java
Handler主要职责:
- 发送消息给MessageQueue(消息队列);
- 处理Looper派送过来的消息;
我们使用Handler一般都要初始化一个Handler实例。看下Handler的构造函数:
- public Handler() {
- this(null, false);
- }
- public Handler(Callback callback, boolean async) {
- //。。。。
- mLooper = Looper.myLooper();
- if (mLooper == null) {
- throw new RuntimeException(
- "Can't create handler inside thread that has not called Looper.prepare()");
- }
- mQueue = mLooper.mQueue;
- mCallback = callback;
- mAsynchronous = async;
- }
第8行 Looper.myLooper();获取当前线程保存的Looper实例,如果当前线程没有Looper实例则会抛出异常。这也就是说在线程中应该先创建Looper实例(通过Looper.prepare()),然后才可以创建Handler实例。
第13行 获取Looper实例所保存的MessageQueue。之后使用Handler sendMesage、post都会将消息发送到该消息队列中。保证handler实例与该线程中唯一的Looper对象、及该Looper对象中的MessageQueue对象联系到一块。
sendMessage
接着看一下平常使用Handler发送消息,先看sendMessage的流程:
- public final boolean sendMessage(Message msg)
- {
- return sendMessageDelayed(msg, 0);
- }
- public final boolean sendMessageDelayed(Message msg, long delayMillis)
- {
- if (delayMillis < 0) {
- delayMillis = 0;
- }
- return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
- }
- public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
- MessageQueue queue = mQueue;
- if (queue == null) {
- RuntimeException e = new RuntimeException(
- this + " sendMessageAtTime() called with no mQueue");
- Log.w("Looper", e.getMessage(), e);
- return false;
- }
- return enqueueMessage(queue, msg, uptimeMillis);
- }
sendMessage最终调用到enqueueMessage函数,接着看下enqueueMessage。
- private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
- msg.target = this;
- if (mAsynchronous) {
- msg.setAsynchronous(true);
- }
- return queue.enqueueMessage(msg, uptimeMillis);
- }
在enqueueMessage中,首先设置msg的target属性,值为this。之前在Looper的loop方法中,从消息队列中取出的msg,然后调用msg.target.dispatchMessage(msg);其实也就是调用当前handler的dispatchMessage函数。
然后调用queue的dispatchMessage方法,将Handler发出的消息,保存到消息队列中。
post
看一下post方法
- public final boolean post(Runnable r)
- {
- return sendMessageDelayed(getPostMessage(r), 0);
- }
- private static Message getPostMessage(Runnable r) {
- Message m = Message.obtain();
- m.callback = r;
- return m;
- }
post方法中调用getPostMessage方法,创建一个Message对象,设置此Message对象的callback属性为创建Runnable对象。
然后调用sendMessageDelayed,最终和sendMessage一样,都是调用到sendMessageAtTime。调用enqueueMessage方法,将此msg添加到MessageQueue中。
也就是post(Runnable r) 并没有创建线程。其run方法是在Handler对应的线程中运行的。
dispatchMessage
这里主要说下handler是如何处理消息的。在Looper.loop方法中通过获取到的msg,然后调用msg.target.dispatchMessage(msg);也就是调用handler的dispatchMessage方法,看下Handler中dispatchMessage源码
- public void dispatchMessage(Message msg) {
- if (msg.callback != null) {
- handleCallback(msg);
- } else {
- if (mCallback != null) {
- if (mCallback.handleMessage(msg)) {
- return;
- }
- }
- handleMessage(msg);
- }
- }
在dispatchMessage方法中首先判断msg的callback属性,如果不为空则调用handleCallback函数,
handleCallback函数如下:
- private static void handleCallback(Message message) {
- message.callback.run();
- }
handleCallback函数中messag.callback也就是我们传的Runnable对象,也就是调用Runnable对象的run方法。
如果msg.callback属性为空,判断Handler属性mCallback是否为空, 不为空则让mCallback处理该msg。
mCallback为空则调用Handler的handleMessage,这就是我们创建Handler对象时一般都实现其handleMessage方法的原因。
MessageQueue
源码路径:frameworks/base/core/java/android/os/MessageQueue.java
MessageQueue 消息队列:
- enqueueMessage将消息加入队列
- next从队列取出消息
- removeMessage移除消息
MessageQueue内部是如何管理这些消息队列的就先不说了,之后又空再好好分析一下。
总结
本文分析了下Handler、Looper、MessageQueue之间的联系,及handler进程间通信的原理。了解到Handler不仅仅可以更新UI,也可以在一个子线程中去创建一个Handler,然后使用这个handler实例在任何其他线程中发送消息,最终处理这些消息的代码都会在你创建Handler实例的线程中运行。
Android 开发 深入理解Handler、Looper、Messagequeue 转载的更多相关文章
- Android开发 之 理解Handler、Looper、MessageQueue、Thread关系
本文转自博客:http://blog.csdn.net/he90227/article/details/43567073 一. 图解与概述 首先Android中 的每一个线程都会对应一个Message ...
- 讲讲Handler+Looper+MessageQueue 关系
Handler+Looper+MessageQueue这三者的关系其实就是Android的消息机制.这块内容相比开发人员都不陌生,在面试中,或者日常开发中都会碰到,今天就来讲这三者的关系. 概述: H ...
- Handler+Looper+MessageQueue深入详解
概述:Android中的异步处理机制由四部分组成:Handler+Looper+MessageQueue+message,用于实现线程间的通信. 用到的概念: Handler: 主要作用是发送消息和处 ...
- Handler Looper MessageQueue 之间的关系
Handler Looper MessageQueue 之间的关系 handler在安卓开发中常用于更新界面ui,以及其他在主线程中的操作.内部结构大概图为: 1.handler持有一个Looper对 ...
- Handler,Looper,MessageQueue流程梳理
目的:handle的出现主要是为了解决线程间通讯. 举个例子,android是不允许在主线程中访问网络,因为这样会阻塞主线程,影响性能,所以访问网络都是放在子线程中执行,对于网络返回的结果则需要显示在 ...
- Android之消息机制Handler,Looper,Message解析
PS:由于感冒原因,本篇写的有点没有主干,大家凑合看吧.. 学习内容: 1.MessageQueue,Looper,MessageQueue的作用. 2.子线程向主线程中发送消息 3.主线程向子线程中 ...
- Android开发资源获取国内代理(转载)
Android Dev Tools官网地址:www.androiddevtools.cn 收集整理Android开发所需的Android SDK.开发中用到的工具.Android开发教程.Androi ...
- android开发架构理解
1. android 开发和普通的PC程序开发的,我觉得还是不要过度设计,因为手机开发,项目相对传统软件开发就小很多,而且手机的性能有限,过度设计代码mapping需要消耗的能相对就高,而且手机开发的 ...
- Android开发手记(28) Handler和Looper
Android的消息处理有三个核心类:Looper,Handler和Message.其实还有一个Message Queue(消息队列),但是MQ被封装到Looper里面了,我们不会直接与MQ打交道.平 ...
随机推荐
- HttpsessionListener 实现在线人数统计
最近在学servlet jsp,用的林信良先生的 jsp&servlet 这本书,在第五章有道在线人数统计的课后题完成,做一次记录. 实际效果: 一:用户类: package cc.openh ...
- Jmeter压力测试生成聚合报告
Jmeter压力测试: 压力测试的场景设置分为三种: 单场景:一个请求(如:单个接口的测试) 混合场景:多个请求(如:购物流程的测试) 压测时间,一般场景都是运行10-15分钟,如果是做疲劳测试的话, ...
- L332 NBA: Dwyane Wade and Dirk Nowitzki Say Emotional Goodbyes
Two games in the NBA ended amid emotional scenes on Tuesday as legends at separate teams marked thei ...
- 结构体变量的 extern 使用方法,转--
要求如下,在.h文件中这样定义: typedef struct typFNT_GB16 // 汉字字模数据结构 { signed ]; // 汉字内码索引 ]; ...
- dom4j操作xml的demo
package com.cn.shop.util; import java.io.File;import java.io.FileOutputStream;import java.io.OutputS ...
- 非常好的开源C项目tinyhttpd(500行代码)
编译命令 gcc -W -Wall -lpthread -o httpd httpd.c 源码 #include <stdio.h> #include <sys/socket.h&g ...
- Windows10 64位安装TensorFlow-GPU
TensorFlow有GPU版和CPU版. GPU版需要CUDA和cuDNN支持,到链接:https://developer.nvidia.com/cuda-gpus 确认自己的显卡是否支持CUDA. ...
- day05 集合
今日进度(数据类型) 集合 内存相关 深浅拷贝 1.集合表示 1.无序 2.不重复 3.hash查找 #问题:v={}表示? set: v1=set()#空集合 v1={1,2,3,4,5} dict ...
- Linux下修改Jenkins默认端口
我是自动安装的Jenkins,默认目录为 jenkins安装目录:/var/lib/jenkins jenkins日志目录:/var/log/jenkins/jenkins.logjenkins默认配 ...
- hadoop 单机模式 伪分布式 完全分布式区别
1.单机(非分布式)模式 这种模式在一台单机上运行,没有分布式文件系统,而是直接读写本地操作系统的文件系统,一般仅用于本地MR程序的调试 2.伪分布式运行模式 这种模式也是在一台单机上运行,但用不同的 ...