在非UI线程使用Handler进行线程通信时,一般都需要进行3个步骤:

  • 创建Looper Looper.prepar()
  • 创建Handler
  • 启动消息循环Looper.loop()

通过这3步,基本就建立好了 Android 的多线程消息通信机制:

  • Handler
  • MessageQueue
  • Looper
  • Message

这几者可谓是你中有我,我中有你的存在。通过 Handler 发送 Message 到 Looper 的 MessageQueue 中,待 Looper 的循环执行到 Message 后,就会根据 Message 的 target handler,回调对应 Handler 的 handlerMessage 方法。

例如: Thread-A 拥有一个 Looper,Thread-B 持有一个在 Thread-A 中构造的 Handler,Thread-B 就可以通过这个 Handler 将 Message 发送到 Thread-A 的 Looper 的 MessageQueue 中,然后消息会走到 Thread-A 的 Handler 的 handleMessage 方法。

Looper 原理图

在 Looper 类加载时就会创建一个 ThreadLocal 类型的类变量 sThreadLocal

public final class Looper {
private static final String TAG = "Looper"; // sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

Looper.prepar()

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");
}
// 将构造的 looper 存到类变量 sThreadLocal 中
sThreadLocal.set(new Looper(quitAllowed));
} private Looper(boolean quitAllowed) {
// 构建一个 messageQueue 成员
mQueue = new MessageQueue(quitAllowed);
// 将当前线程存入 mThread 中
mThread = Thread.currentThread();
}

在这里面主要执行了 3 步:

  • 构建一个 looper

    • 构建一个 messageQueue 成员
    • 将当前线程存入 mThread 中
  • 将构造的 looper 存到类变量 sThreadLocal 中

至此,执行 Looper.praper 的当前线程就会拥有一个 looper 成员了,存放在 Looper 的 sThreadLocal 中。

创建Handler

public Handler(Callback callback, boolean async) {
...
// 通过`Looper.myLooper()` 类方法获取 sThreadLocal 中储存的当前线程的 looper,将这个 looper 绑定到 handler 的成员变量 mLooper 中
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
}
// 将 mLooper 中的 messageQueue 绑定到 handler 的成员变量 mQueue 中
mQueue = mLooper.mQueue;
...
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}

Looper.loop()

  • 声明一个局部常量final Loop me = myLoop()

    • myLoop()将返回当前线程的looper成员
  • 声明一个局部常量final MessageQueue queue

    • 将me.mQueue赋值给queue
  • 进入无限循环

    //进入无限循环
    for (;;) {
    //取出一条消息
    Message msg = queue.next();
    //没有消息就阻塞
    if (msg == null) {
    return;
    }
    ... //分发消息
    try {
    msg.target.dispatchMessage(msg);
    //msg.target是一个Handler对象
    } finally {
    if (traceTag != 0) {
    Trace.traceEnd(traceTag);
    }
    }
    ... //回收消息
    msg.recycleUnchecked();
  • 通过Message.obtain()获取的消息,需要使用Handler.sendMessage()插入到消息队列。

  • 通过Handler.obtainMessage()获取的消息,可以使用message.sendToTaget()插入到消息队列。

Android 消息队列机制的更多相关文章

  1. ZWave 中的消息队列机制

    文章主题   在我们的日常编程中,对消息队列的需求非常常见,使用一个简洁.高效的消息队列编程模型,对于代码逻辑的清晰性,对于事件处理的高效率来说,是非常重要的.这篇文章就来看看 ZWave 中是通过什 ...

  2. Android消息队列和Looper

    1. 什么是消息队列 消息队列在android中对应MessageQueue这个类,顾名思义,消息队列中存放了大量的消息(Message) 2.什么是消息 消息(Message)代表一个行为(what ...

  3. Android 消息分发机制

    Android 中针对耗时的操作,放在主线程操作,轻者会造成 UI 卡顿,重则会直接无响应,造成 Force Close.同时在 Android 3.0 以后,禁止在主线程进行网络请求. 针对耗时或者 ...

  4. Android开发学习—— 消息队列

    ###主线程不能被阻塞* 在Android中,主线程被阻塞会导致应用不能刷新ui界面,不能响应用户操作,用户体验将非常差* 主线程阻塞时间过长,系统会抛出ANR异常* ANR:Application ...

  5. Android 开发笔记 “Android 的消息队列模型”

    Android是参考Windows的消息循环机制来实现Android自身的消息循环的. Android通过Looper.Handler来实现消息循环机制,Android消息循环是针对线程的(每个线程都 ...

  6. Android 的消息队列模型

    Android 的消息队列模型 Android是参考Windows的消息循环机制来实现Android自身的消息循环的.    Android通过Looper.Handler来实现消息循环机制,Andr ...

  7. Android HandlerThread 消息循环机制之源代码解析

    关于 HandlerThread 这个类.可能有些人眼睛一瞟,手指放在键盘上,然后就是一阵狂敲.立即就能敲出一段段华丽的代码: HandlerThread handlerThread = new Ha ...

  8. Window Azure ServiceBus Messaging消息队列技术系列1-基本概念和架构

    前段时间研究了Window Azure ServiceBus Messaging消息队列技术,搞了很多技术研究和代码验证,最近准备总结一下,分享给大家. 首先,Windows Azure提供了两种类型 ...

  9. 跟我一起学WCF(1)——MSMQ消息队列

    一.引言 Windows Communication Foundation(WCF)是Microsoft为构建面向服务的应用程序而提供的统一编程模型,该服务模型提供了支持松散耦合和版本管理的序列化功能 ...

随机推荐

  1. C#连接Oracle数据库的方法(System.Data.OracleClient、Oracle.DataAccess.Client也叫ODP.net、Oracle.ManagedDataAccess.dll)

    官方下载地址(ODP.net)(中文):http://www.oracle.com/technetwork/cn/topics/dotnet/downloads/index.html 官方下载地址(O ...

  2. eas启动服务器时非法组件

    EAS实例启动报系统中存在非法组件,实例启动失败:   组件检查机制,要求除了 $EAS_HOME eas\server\lib: $EAS_HOME \eas\server\deploy\files ...

  3. npm命令及解释

    npm是Node Package Manager,也就是长说的NPM包管理器. 一般安装node.js就会一起安装. npm install npm install XXX    //表示安装模块, ...

  4. grunt入门 出处:http://artwl.cnblogs.com

    grunt-contrib-uglify uglify是一个文件压缩插件,项目地址:https://github.com/gruntjs/grunt-contrib-uglify 本文将以一个DEMO ...

  5. html_entity_decode()、空格、&nbsp; 乱码问题

    普通 ASCII 码空格为 32,但是浏览器会对普通空格进行自动归并,也就是如果你输入10个 0x20 的空格在HTML页面里面,可能会被合并成一个空格. 如果想要一致的呈现多个空格,就要用到 编码标 ...

  6. android debug签名文件

    现象 可以运行程序,但不能启动安装成功的软件 并且run application的时候也不弹出界面. 路径: C:\Users\sunfb\.android 下替换debug.keystore 就OK

  7. (10)Spring Boot修改端口号【从零开始学Spring Boot】

    Spring boot 默认端口是8080,如果想要进行更改的话,只需要修改applicatoin.properties文件,在配置文件中加入: server.port=9090 常用配置: #### ...

  8. Spring Cloud-hystrix(六)

    作用 防止 多个服务相互交互时某个服务运行缓慢导致调用方线程挂起,高并发情况下 导致挂起线太多 引起调用方的服务不可用 能够在服务发生故障或者通过断路器监控向调用方返回一个错误 而不是长时间的等待 S ...

  9. Spring Cloud-Ribbon实现客户端的服务均衡(三)

    客户端负载均衡与服务端负载均衡 服务端负载均衡 通过服务端负载均衡设备维护的服务清单根据算法(轮训 权重负载 流量负载)取出服务地址 进行转发 客户端负载 将指定服务的服务清单订单(注册中心)下来 在 ...

  10. 配置db账号和密码时一定注意空格问题、空行问题否则连接报错

    #postgresql dbpg.datasource.type=com.alibaba.druid.pool.DruidDataSourcepg.datasource.driverClassName ...