本篇主要介绍Android中的消息机制,即Looper、Handler是如何协同工作的;

Looper:主要用来管理当前线程的消息队列,每个线程只能有一个Looper

Handler:用来将消息(Message)插入到当前线程的消息队列,并负责分发Looper中的消息,将消息发送到当前线程执行

具体关系图如下所示:

接下来我们来分析一下Looper和Handler的源码,了解一下其中的奥妙。

首先我们从一个程序运行的入口来分析,源码如下:

public static void main(String[] args){

    ......
Looper.prepareMainLooper();//初始化Looper ......
if(smainThreadHandler==null){
smainThreadHandler=thread.getHandler();//初始化Handler
} ...... Looper.loop();//消息循环执行
}

可以看出,程序在运行的时候首先会创建主线程的Looper对象,并通过Looper开启消息循环,不停的取出消息并执行;

接下来我们来研究Looper的源码;

第一部分:Looper源码

初始化

  private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
} 初始化Looper对象(该过程包含初始化消息队列和当前线程对象)
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在初始化的时候,首先会创建消息队列,并通过sThreadLocal保存在当前的线程本地变量中;

再来看一下程序入口Looper.prepareMainLooper();//初始化Looper究竟执行了什么

 //初始化主线程的Looper对象
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}

这里有两行关键的代码:prepare(false);和sMainLooper = myLooper();

首先我们来看prepare(false);即上面讲到的 初始化Looper,可以看看上面的源码;

我们来看sMainLooper = myLooper();

  public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}

非常简单,我们上面提到在初始化Looper的时候会把Looper保存到当前线程的本地变量中,而这行代码的意思

就是从线程本地变量中将looper取出来

有了Looper,程序怎样才能运行?答案就在Looper.loop();//消息循环执行

  public static void loop() {
final Looper me = myLooper();//得到当前线程的Looper对象
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//得到消息队列 ...... //执行消息循环
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
msg.target.dispatchMessage(msg);
msg.recycleUnchecked();
}
}

显而易见,loop方法就是获取到当前线程的Looper对象,并从中循环取出消息,并执行,程序就这样跑起来了,具体是如何分发消息的

我们将会在下面讲解;

至此我们至少应该明白,当主线程在执行的时候

1、初始化Looper,并将Looper保存的线程变量中

2、Looper在初始化的时候会创建消息队列,并管理消息队列

2、取出Looper,并从消息队列中取出消息,循环执行

第二部分:Handler源码

我们知道Handler有两种使用方式,一种是使用handler.post(Runnable r);另一种是复写handleMessage(Message msg)方法

复写handleMessage(Message msg)方法非常简单,Handler在消息分发的时候,直接回调该方法即可,我们主要来研究第一种

 public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}

看到这个我们首先得明白getPostMessage(r)干了什么

 private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}

可以看出是将r封装成了一个消息,r作为该消息的回调;

我们接着看:

 public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

关键代码在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);
}

这段代码主要就是获取到消息队列,有了消息队列我们接着看enqueueMessage(queue, msg, uptimeMillis);

 //handler和msg建立关联
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
 msg.target = this;这里将msg的target指向自己,msg的target只能是this
最后相信大家也看出来了,接下来就是真正将msg插入到消息队列了
 //负责将msg插入到消息队列
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
} synchronized (this) {
//将msg插入到消息队列 msg.when = when;
Message p = mMessages;
boolean needWake;
//消息队列为链式存储 如果消息队列中的消息为0,将msg插入到第一个,并新建一个message对象,将
//msg对象的next指向新建的message 等待新msg插入
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//将msg插入到队尾
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
} }
return true;
}

绕了这么大一圈,最后是通过Handler中的消息队列,将消息成功插入队尾,至此handler在post的时候实际上

是将r封装成了一个msg并插入到消息队列;

另外这里再提一下Handler第二种方式即复写handleMessage(Message msg)方法使用

handler.sendMessage(msg);

其源代码其实就是执行以上的

 public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

重复以上一系列过程,将msg插入到消息队列;

最后我们来看一下比较关键的消息分发,消息分发是在以上Looper源码的loop方法中核心方法是:

msg.target.dispatchMessage(msg);

我们知道msg的target只能是Handler本身,因此消息分发是在Handler中来完成的;

   /**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

第一:if (msg.callback != null)  如果你传入了callback即Runnable,那么就执行 handleCallback(msg);

即调用r的run方法,通常是handler.post(r);类型的

第二:if (mCallback != null)这种方法允许让activity等来实现Handler.Callback接口,避免了自己编写handler重写handleMessage方法。

第三:handleMessage(msg); 即handler.sendMessage(msg);时调用的。直接回调Handler的handleMessage(msg);方法

至此,Android中的消息机制Looper和Handler相信你已经有了一定的了解;

最后我们再来总结一下

1、Looper 一个线程中只能有一个Looper,用来管理消息队列

2、Looper从消息队列里取出msg,交给Handler来进行分发,分发到Handler所在的线程执行,即创建Handler时的线程;

3、可以在当前线程中创建消息对象或直接复写Runnable的run方法,同过Handler将msg和r封装后的msg插入到消息队列

Android消息机制源码分析的更多相关文章

  1. [Android]简略的Android消息机制源码分析

    相关源码 framework/base/core/java/andorid/os/Handler.java framework/base/core/java/andorid/os/Looper.jav ...

  2. 史上最详细的Android消息机制源码解析

    本人只是Android菜鸡一个,写技术文章只是为了总结自己最近学习到的知识,从来不敢为人师,如果里面有不正确的地方请大家尽情指出,谢谢! 606页Android最新面试题含答案,有兴趣可以点击获取. ...

  3. Android -- 消息处理机制源码分析(Looper,Handler,Message)

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

  4. Android事件分发机制源码分析

    Android事件分发机制源码分析 Android事件分发机制源码分析 Part1事件来源以及传递顺序 Activity分发事件源码 PhoneWindow分发事件源码 小结 Part2ViewGro ...

  5. Android Handler消息机制源码解析

    好记性不如烂笔头,今天来分析一下Handler的源码实现 Handler机制是Android系统的基础,是多线程之间切换的基础.下面我们分析一下Handler的源码实现. Handler消息机制有4个 ...

  6. Springboot学习04-默认错误页面加载机制源码分析

    Springboot学习04-默认错误页面加载机制源码分析 前沿 希望通过本文的学习,对错误页面的加载机制有这更神的理解 正文 1-Springboot错误页面展示 2-Springboot默认错误处 ...

  7. ApplicationEvent事件机制源码分析

    <spring扩展点之三:Spring 的监听事件 ApplicationListener 和 ApplicationEvent 用法,在spring启动后做些事情> <服务网关zu ...

  8. Android异步消息传递机制源码分析

    1.Android异步消息传递机制有以下两个方式:(异步消息传递来解决线程通信问题) handler 和 AsyncTask 2.handler官方解释的用途: 1).定时任务:通过handler.p ...

  9. Android线程间异步通信机制源码分析

    本文首先从整体架构分析了Android整个线程间消息传递机制,然后从源码角度介绍了各个组件的作用和完成的任务.文中并未对基础概念进行介绍,关于threadLacal和垃圾回收等等机制请自行研究. 基础 ...

随机推荐

  1. Guid算法与标识列(自动增长字段)在表中的应用

    <<1>>int(bigint)+标识列(自动增长字段) 用标识列实现字段自增可以避免并发等问题.不需开发人员自己控制自增,用标识列的字段在Insert的时候不用指定主键的值. ...

  2. 浅谈Dictionary用法

    一.基础篇 1.Dictionary泛型类提供了从一组键到一组值的映射,即键和值的集合类. 2.Dictionary通过键来检索值的速度是非常快的,这是因为 Dictionary 类是作为一个哈希表来 ...

  3. C# ~ 从 IEnumerable / IEnumerator 到 IEnumerable<T> / IEnumerator<T> 到 yield

    IEnumerable / IEnumerator 首先,IEnumerable / IEnumerator 接口定义如下: public interface IEnumerable /// 可枚举接 ...

  4. windbg学习进阶之——windbg环境变量配置

    接触性能调优以来一直想学下windbg分析dump,每次看老师几个命令就能找到很底层的问题原因那简直就是羡慕加崇拜啊~但是这接近一年了,愣是没啥进展呢,主要就是在今天整理的这部分卡住了...这理由找的 ...

  5. eclipse的快捷操作(转)

    快捷键命令作用 快捷键序列 保存 Ctrl+S 刷新 F5 关闭 Ctrl+W 属性 Alt+Enter Format Ctrl+Shift+F 删除行 Ctrl+D 在当前行上面插入行 Ctrl+S ...

  6. 通过python将图片生成字符画

    基础知识: 1.python基础知识   快速学习链接:https://www.shiyanlou.com/courses/214 2.linux命令行操作   快速学习链接:https://www. ...

  7. mongoDB简介

    概述 MongoDB 是一款跨平台.面向文档的数据库.用它创建的数据库可以实现高性能.高可用性,并且能够轻松扩展.MongoDB 的运行方式主要基于两个概念:集合(collection)与文档(doc ...

  8. C#如何定制Excel界面并实现与数据库交互

    Excel是微软办公套装软件的一个重要的组成部分,它可以进行各种数据的处理.统计分析和辅助决策操作,广泛地应用于管理.统计财经.金融等众多领域.(另外,Excel还是伦敦一所会展中心的名称)..NET ...

  9. sDashboard:简单的,轻量级的 jQuery 仪表板插件

    sDashboard 是一个轻量的仪表板 jQuery 插件,转换一个对象数组到仪表板.数组中的每个对象将被呈现为一个仪表板组件,可以通过左右拖 ​动重新排列. sDashboards 内置渲染 Da ...

  10. 小数5.2500四舍五入保留1位小数的java算法之一

    BigDecimal bd = new BigDecimal(5.2500); BigDecimal a = bd.setScale(1, BigDecimal.ROUND_HALF_UP); dou ...