转载请注明出处: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的地方:

  1. 主线程(UI线程)
    UI线程中Looper已经都创建好了,不用我们去创建和循环。
  2. 普通线程
    普通线程中使用Looper需要我们自己去prepare()、loop()。
    看一下普通线程中创建使用Looper的方式,代码如下:
  1. class LooperThread extends Thread {
  2. public Handler mHandler;
  3. public void run() {
  4. Looper.prepare();
  5. mHandler = new Handler() {
  6. public void handleMessage(Message msg) {
  7. // process incoming messages here
  8. }
  9. };
  10. Looper.loop();
  11. }
  12. }

这段代码是Looper源码注释中给的典型列子,主要步骤:

Looper 准备,(Looper实例创建);

  1. 创建发送消息、处理消息的Handler对象;
  2. Looper开始运行。

印象中在UI线程没有出现过Looper相关的东东,这是因为UI线程中会自动创建Looper对象并进行消息循环,我们不再需要调用Looper.prepare()和Looper.loop(),但是在子线程中如果想要创建使用Handelr则需要向如上所示。
我们通过源码看一下Looper实例创建的方法:

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

Looper构造方法是私有的,只能通过prepare()进行创建Looper对象。prepare()会调用私有方法prepare(boolean quitAllowed)。
第6行 sThreadLocal为ThreadLocal类型变量,用来存储线程中的Looper对象。
prepare方法中首先判断sThreadLocal是否存储对象,如果存储了则抛出异常,这是因为在同一个线程中Loop.prepare()方法不能调用两次,也就是同一个线程中最多有一个Looper实例(当然也可以没有,如果子线程不需要创建Handler时)。
该异常应该许多朋友都遇见过,如在UI线程中调用Looper.prepare(),系统会替UI线程创建Looper实例,所以不需要再次调用prepare()。

接着看Looper的构造器:

  1. private Looper(boolean quitAllowed) {
  2. mQueue = new MessageQueue(quitAllowed);
  3. mRun = true;
  4. mThread = Thread.currentThread();
  5. }

在构造器中,创建了一个MessageQueue消息队列;然后获取当前的线程,使Looper实例与线程绑定。
由prepare方法可知一个线程只会有一个Looper实例,所以一个Looper实例也只有一个MessageQueue实例。但这并不代表一个线程只能有一个MessageQueue实例,这是为什么呢?很简单,我们可以自己new 一个MessageQueue实例就可以了,但这个MessageQueue并不是该线程中Handelr对应的消息队列。

接着看Looper的消息循环:

  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. //通过Looper实例获取消息队列
  7. final MessageQueue queue = me.mQueue;
  8.  
  9. // Make sure the identity of this thread is that of the local process,
  10. // and keep track of what that identity token actually is.
  11. Binder.clearCallingIdentity();
  12. final long ident = Binder.clearCallingIdentity();
  13. for (;;) { //消息循环
  14. //从消息队列中取出一条消息,如果没有消息则会阻塞。
  15. Message msg = queue.next(); // might block
  16. if (msg == null) {
  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. //将消息派发给target属性对应的handler,调用其dispatchMessage进行处理。
  28. msg.target.dispatchMessage(msg);
  29.  
  30. if (logging != null) {
  31. logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
  32. }
  33. // Make sure that during the course of dispatching the
  34. // identity of the thread wasn't corrupted.
  35. final long newIdent = Binder.clearCallingIdentity();
  36. if (ident != newIdent) {
  37. //log
  38. }
  39. msg.recycle();
  40. }
  41. }

loop()函数是静态的,所以它只能访问静态数据。
第2行myLooper()函数也是静态的,其代码如下

  1. 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方法安全之处在于清空消息之前会派发所有的非延迟消息。

总结:

  1. UI线程会自动创建Looper实例、并且调用loop()方法,不需要我们再调用prepare()和loop().
  2. Looper与创建它的线程绑定,确保一个线程最多有一个Looper实例,同时一个Looper实例只有一个MessageQueue实例。
  3. loop()函数循环从MessageQueue中获取消息,并将消息交给消息的target的dispatchMessage去处理。如果MessageQueue中没有消息则获取消息可能会阻塞。
  4. 通过调用Looper的quit或quitsafely终止消息循环。

Handler

源码路径:frameworks/base/core/java/android/os/Handler.java
Handler主要职责:

  1. 发送消息给MessageQueue(消息队列);
  2. 处理Looper派送过来的消息;
    我们使用Handler一般都要初始化一个Handler实例。看下Handler的构造函数:
  1. public Handler() {
  2. this(null, false);
  3. }
  4.  
  5. public Handler(Callback callback, boolean async) {
  6. //。。。。
  7.  
  8. mLooper = Looper.myLooper();
  9. if (mLooper == null) {
  10. throw new RuntimeException(
  11. "Can't create handler inside thread that has not called Looper.prepare()");
  12. }
  13. mQueue = mLooper.mQueue;
  14. mCallback = callback;
  15. mAsynchronous = async;
  16. }

第8行 Looper.myLooper();获取当前线程保存的Looper实例,如果当前线程没有Looper实例则会抛出异常。这也就是说在线程中应该先创建Looper实例(通过Looper.prepare()),然后才可以创建Handler实例。
第13行 获取Looper实例所保存的MessageQueue。之后使用Handler sendMesage、post都会将消息发送到该消息队列中。保证handler实例与该线程中唯一的Looper对象、及该Looper对象中的MessageQueue对象联系到一块。

sendMessage

接着看一下平常使用Handler发送消息,先看sendMessage的流程:

  1. public final boolean sendMessage(Message msg)
  2. {
  3. return sendMessageDelayed(msg, 0);
  4. }
  5. public final boolean sendMessageDelayed(Message msg, long delayMillis)
  6. {
  7. if (delayMillis < 0) {
  8. delayMillis = 0;
  9. }
  10. return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
  11. }
  12. public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
  13. MessageQueue queue = mQueue;
  14. if (queue == null) {
  15. RuntimeException e = new RuntimeException(
  16. this + " sendMessageAtTime() called with no mQueue");
  17. Log.w("Looper", e.getMessage(), e);
  18. return false;
  19. }
  20. return enqueueMessage(queue, msg, uptimeMillis);
  21. }

sendMessage最终调用到enqueueMessage函数,接着看下enqueueMessage。

  1. private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
  2. msg.target = this;
  3. if (mAsynchronous) {
  4. msg.setAsynchronous(true);
  5. }
  6. return queue.enqueueMessage(msg, uptimeMillis);
  7. }

在enqueueMessage中,首先设置msg的target属性,值为this。之前在Looper的loop方法中,从消息队列中取出的msg,然后调用msg.target.dispatchMessage(msg);其实也就是调用当前handler的dispatchMessage函数。
然后调用queue的dispatchMessage方法,将Handler发出的消息,保存到消息队列中。

post

看一下post方法

  1. public final boolean post(Runnable r)
  2. {
  3. return sendMessageDelayed(getPostMessage(r), 0);
  4. }
  5.  
  6. private static Message getPostMessage(Runnable r) {
  7. Message m = Message.obtain();
  8. m.callback = r;
  9. return m;
  10. }

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源码

  1. public void dispatchMessage(Message msg) {
  2. if (msg.callback != null) {
  3. handleCallback(msg);
  4. } else {
  5. if (mCallback != null) {
  6. if (mCallback.handleMessage(msg)) {
  7. return;
  8. }
  9. }
  10. handleMessage(msg);
  11. }
  12. }

在dispatchMessage方法中首先判断msg的callback属性,如果不为空则调用handleCallback函数,
handleCallback函数如下:

  1. private static void handleCallback(Message message) {
  2. message.callback.run();
  3. }

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 转载的更多相关文章

  1. Android开发 之 理解Handler、Looper、MessageQueue、Thread关系

    本文转自博客:http://blog.csdn.net/he90227/article/details/43567073 一. 图解与概述 首先Android中 的每一个线程都会对应一个Message ...

  2. 讲讲Handler+Looper+MessageQueue 关系

    Handler+Looper+MessageQueue这三者的关系其实就是Android的消息机制.这块内容相比开发人员都不陌生,在面试中,或者日常开发中都会碰到,今天就来讲这三者的关系. 概述: H ...

  3. Handler+Looper+MessageQueue深入详解

    概述:Android中的异步处理机制由四部分组成:Handler+Looper+MessageQueue+message,用于实现线程间的通信. 用到的概念: Handler: 主要作用是发送消息和处 ...

  4. Handler Looper MessageQueue 之间的关系

    Handler Looper MessageQueue 之间的关系 handler在安卓开发中常用于更新界面ui,以及其他在主线程中的操作.内部结构大概图为: 1.handler持有一个Looper对 ...

  5. Handler,Looper,MessageQueue流程梳理

    目的:handle的出现主要是为了解决线程间通讯. 举个例子,android是不允许在主线程中访问网络,因为这样会阻塞主线程,影响性能,所以访问网络都是放在子线程中执行,对于网络返回的结果则需要显示在 ...

  6. Android之消息机制Handler,Looper,Message解析

    PS:由于感冒原因,本篇写的有点没有主干,大家凑合看吧.. 学习内容: 1.MessageQueue,Looper,MessageQueue的作用. 2.子线程向主线程中发送消息 3.主线程向子线程中 ...

  7. Android开发资源获取国内代理(转载)

    Android Dev Tools官网地址:www.androiddevtools.cn 收集整理Android开发所需的Android SDK.开发中用到的工具.Android开发教程.Androi ...

  8. android开发架构理解

    1. android 开发和普通的PC程序开发的,我觉得还是不要过度设计,因为手机开发,项目相对传统软件开发就小很多,而且手机的性能有限,过度设计代码mapping需要消耗的能相对就高,而且手机开发的 ...

  9. Android开发手记(28) Handler和Looper

    Android的消息处理有三个核心类:Looper,Handler和Message.其实还有一个Message Queue(消息队列),但是MQ被封装到Looper里面了,我们不会直接与MQ打交道.平 ...

随机推荐

  1. HttpsessionListener 实现在线人数统计

    最近在学servlet jsp,用的林信良先生的 jsp&servlet 这本书,在第五章有道在线人数统计的课后题完成,做一次记录. 实际效果: 一:用户类: package cc.openh ...

  2. Jmeter压力测试生成聚合报告

    Jmeter压力测试: 压力测试的场景设置分为三种: 单场景:一个请求(如:单个接口的测试) 混合场景:多个请求(如:购物流程的测试) 压测时间,一般场景都是运行10-15分钟,如果是做疲劳测试的话, ...

  3. 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 ...

  4. 结构体变量的 extern 使用方法,转--

    要求如下,在.h文件中这样定义: typedef struct typFNT_GB16     // 汉字字模数据结构 {     signed ];        // 汉字内码索引     ]; ...

  5. dom4j操作xml的demo

    package com.cn.shop.util; import java.io.File;import java.io.FileOutputStream;import java.io.OutputS ...

  6. 非常好的开源C项目tinyhttpd(500行代码)

    编译命令 gcc -W -Wall -lpthread -o httpd httpd.c 源码 #include <stdio.h> #include <sys/socket.h&g ...

  7. Windows10 64位安装TensorFlow-GPU

    TensorFlow有GPU版和CPU版. GPU版需要CUDA和cuDNN支持,到链接:https://developer.nvidia.com/cuda-gpus 确认自己的显卡是否支持CUDA. ...

  8. day05 集合

    今日进度(数据类型) 集合 内存相关 深浅拷贝 1.集合表示 1.无序 2.不重复 3.hash查找 #问题:v={}表示? set: v1=set()#空集合 v1={1,2,3,4,5} dict ...

  9. Linux下修改Jenkins默认端口

    我是自动安装的Jenkins,默认目录为 jenkins安装目录:/var/lib/jenkins jenkins日志目录:/var/log/jenkins/jenkins.logjenkins默认配 ...

  10. hadoop 单机模式 伪分布式 完全分布式区别

    1.单机(非分布式)模式 这种模式在一台单机上运行,没有分布式文件系统,而是直接读写本地操作系统的文件系统,一般仅用于本地MR程序的调试 2.伪分布式运行模式 这种模式也是在一台单机上运行,但用不同的 ...