前言

Android中的异步消息处理机制主要有四部分:Message、Handler、MessageQuene、Looper。这一消息处理机制也称为Handler机制。Handler机制是支撑整个Android系统运行的基础,本质上是因为Android系统是由事件驱动的,而处理事件的核心就在于Handler机制。

Messgae

Messgae是在线程间传递的信息,它可以携带少量的信息以便在线程之间传递。

message.what	 // 标识是哪条信息
message.arg1 // 存放整型数据
message.arg2 // 存放整型数据
message.obj // 存放任意对象
  • 生成Message

    Handler h = new Handler();
    ...
    Message m1 = new Message();
    Message m2 = Message.obtain(); //推荐使用,消息池
    Message m3 = h.obtainMessage(); //推荐使用,消息池

使用消息池获取 Message 实例,并不一定直接创建新实例,而是先在消息池中先看是否存在可用实例,存在则直接取出并返回该实例。

Handler

Handler作为线程间消息的处理者,主要用于发送和处理消息。

  • 构造方法

    Handler()
    Handler(Callback)
    Handler(Looper)
    Handler(Looper, Callback)

    其中前两个不传入Looper的构造方法已被废弃(Android 11,R)。官方解释是在Handler的构造中隐式指定Looper可能会导致错误发生。Handler中引用的是哪个线程的Looper,就在哪个线程中处理消息。

  • 使用方法

    • 在主线程中创建一个Handler,然后在子线程中使用它
    • 在子线程中创建一个运行在主线程中的Handler并使用它
    • 新建一个静态内部类,将Activity作为弱引用放到Handler中使用(推荐使用)
  • 常用函数

    • sendMessage(Message):将消息对象发送到消息队列MessageQuene中,将自身(Handler)的引用传递给Message的目标处理器target,调用MessageQueue的enqueueMessage方法
    • handleMessage(Message):根据消息的标识,进行消息的处理。继承Handler类新建Handler时,需要重写此方法

MessageQueue

MessageQueue是一个消息队列,用于存放消息,其内部通过单链表的数据结构来维护消息列表。消息经过Handler发送至这里之后,等待被处理。MessageQueue由Looper对象进行管理,不需要手动创建。每个线程只有一个MessageQueue对象。

MessageQueue在Handler的构造函数中被赋值,赋值对象为Looper,Looper对象有一个字段是 MessageQueue。是可以通过Looper.myQueue()获取当前线程的 MessageQueue。

  • enqueueMessage(Message, long)方法作用

    • 获取队列头
    • 如果消息不需要延时(long参数),或者消息(传入的Message)的执行时间比头部消息早,或者当前队列是空的,就插到队列头部
    • 如果不是以上的情况,需要将当前消息插入到消息队列的中间位置,通过遍历整个队列,当队列中某个消息的延时比当前消息晚时,将当前消息插入到这个消息的前面。由此得知,消息队列是一个依据“执行时间先后”链接起来的单向链表。

Looper

Looper作为MessageQueue的管理者,是消息循环的核心,不断从其MessageQueue对象中获取消息。在Handler的前两个构造函数中,Handler通过Looper.myLooper方法获取到了Looper对象,但也有可能获取失败,所以后两个构造中的Looper参数就显得十分重要。线程和Looper一一对应,每个线程只有一个Looper。除了主线程有默认 Looper以外,其他线程默认没有 Looper 对象。要想让新创建的线程拥有Looper,应先调用 Looper.prepare()、再调用Looper.loop()。可以使用HandlerThread类创建新线程,该类创建的新线程(非主线程)也含有Looper对象。

主线程由ActivityThread类创建,ActivityThread类(中的main方法)也是整个App的入口,主线程Looper也是在此处创建的。主线程Looper可以通过Looper.getMainLooper()获取。当前线程Looper可以通过Looper.myLooper()获取。

Looper分发消息,是通过一个方法为Looper.loop() 的死循环执行的。在该死循环中,Looper不停地从其MessageQueue对象中获取消息。获取到消息后,根据其target(Handler)的dispatchMessage去分发消息。消息先分发给Message的Callback,未定义的情况下会在分发给Handler的CallBack(从Handler的构造中有所体现),此处的回调与直接声明Handler时相同的是,都要重写handleMessage()方法。

也就是说,主线程Looper获取到Message后,根据Message的target字段找到了发送消息的Handler,紧接着调用了Handler的handleMessage方法。在此处,不论Handler在那个线程被创建,主线程Looper只在主线程调用Handler的handleMessage方法,这样也就完成了线程的切换——“子线程返回主线程”。

ANR 与 Looper

在Linux操作系统上执行一些命令,一般来说程序运行完毕后终端返回信息然后程序都会直接退出。Android应用打开后,不会自己退出的原因就是因为程序还在运行,即使程序中“不写代码”。此处程序的运行,具体到代码中,就是Looper.loop()这个死循环方法,这个方法阻塞了主线程,使得程序一直运行不会退出。

整个Android系统中,驱动程序运行的大部分都是事件,这些事件都作为Message被发送到了MessageQueue中,由Looper进行分发,然后再进行处理。

ANR是Application Not Resopnding,也就是程序无响应,通常是由于在主线程做了耗时操作导致的,其本质不是阻塞了主线程,而是阻塞了主线程Looper的loop()方法,导致loop()无法从MessageQueue中获取消息,进而出现了ANR事件。

Handler 导致的内存泄漏问题

内存泄漏就是说该回收的对象没有回收。倘若直接在Activity中声明一个匿名Handler,Android Studio 就会这样提示:

This Handler class should be static or leaks might occur (anonymous android.os.Handler)

发生这件事的原因是,Java中非静态内部类会引用外部类对象。当Activity在1s内退出时,由于Handler会被Message持有,而Handler作为内部类的实例又会持有Activity,导致Activity退出时无法被回收,产生内存泄漏。

解决办法:

  • 新建一个静态内部类继承Handler,将Activity作为弱引用放到Handler中使用
  • 页面退出的时候,在 onDestroy 中,调用 HandlerremoveMessages 方法,将所有的消息 remove 掉,这样也能消除持有链

Android 的异步消息处理机制的更多相关文章

  1. Android多线程----异步消息处理机制之Handler详解

    ​[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/ ...

  2. Android Handler 异步消息处理机制的妙用 创建强大的图片加载类(转)

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38476887 ,本文出自[张鸿洋的博客] 最近创建了一个群,方便大家交流,群号: ...

  3. Android Handler 异步消息处理机制的妙用 创建强大的图片载入类

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38476887 ,本文出自[张鸿洋的博客] 近期创建了一个群.方便大家交流,群号: ...

  4. android学习-异步消息处理机制

    消息处理机制主要对象:Looper,Handler,Message(还有MessageQueue和Runnable) Looper不断从MessageQueue消息队列中取出一个Message,然后传 ...

  5. 【转】Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 很多人面试肯定都被问到过,请问Andr ...

  6. Android 异步消息处理机制 让你在深入了解 Looper、Handler、Message之间的关系

    转载请注明出处:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 非常多人面试肯定都被问到过,请问And ...

  7. Android线程之异步消息处理机制(三)——AsyncTask

    Android的异步消息处理机制能够很完美的解决了在子线程中进行UI操作的问题,但是为了更加方便我们在子线程中对UI进行操作,Android还提供了另一个很好用的工具,AsyncTask就是其中之一. ...

  8. Android学习之异步消息处理机制

    •前言 我们在开发 APP 的过程中,经常需要更新 UI: 但是 Android 的 UI 线程是不安全的: 如果想更新 UI 线程,必须在进程的主线程中: 这里我们引用了异步消息处理机制来解决之一问 ...

  9. Android异步消息处理机制

    安卓子线程无法直接更改UI,所以需要异步消息处理机制来解决 <?xml version="1.0" encoding="utf-8"?><Li ...

  10. Android 异步消息处理机制解析

    Android 中的异步消息处理主要由四个部分组成,Message.Handler.MessageQueue.Looper.下面将会对这四个部分进行一下简要的介绍. 1. Message: Messa ...

随机推荐

  1. python: 判断是否是某一类型

    函数isinstance 语法 isinstance(object, classinfo) 参数 object -- 实例对象. classinfo -- 可以是直接或间接类名.基本类型或者由它们组成 ...

  2. Yunfly 一款高效、性能优异的 node.js web 框架

    介绍 Yunfly 一款高性能 Node.js WEB 框架, 使用 Typescript 构建我们的应用. 使用 Koa2 做为 HTTP 底层框架, 使用 routing-controllers ...

  3. 达梦数据库: SQL查询报错《不是 GROUP BY 表达式解决方法》

    报错信息: ****: 第4 行附近出现错误: 不是 GROUP BY 表达式 修改办法: 达梦可以配置兼容参数,COMPATIBLE_MODE=4,静态参数,需要重启数据库后生效! sp_set_p ...

  4. python中的注释noqa: F401

    在Python中,"noqa: F401" 是一个特殊的注释指示.它主要用于在静态代码检查工具(例如Flake8)运行时,告知工具忽略特定的 "F401" 错误 ...

  5. 手写raft(二) 实现日志复制

    1. Raft日志复制介绍 在上一篇博客中MyRaft实现了leader选举,为接下来实现日志复制功能打下了基础: 手写raft(一) 实现leader选举 日志复制是raft最核心也是最复杂的功能, ...

  6. Java安全之Webshell免杀

    Java安全之Webshell免杀 当遇到文件上传时,如果网站存在查杀软件,我们上传的一句话木马会被直接秒杀,这时候就需要做一下免杀,绕过查杀软件的检测. 思路 我的想法是先拆分,然后分别检验那些语句 ...

  7. Java日志系列:日志门面JCL、SLF4J

    目录 一.日志门面说明 二.JCL 使用 JCL 入门 JCL 原理 三.SLF4J 使用 配合自身简单日志实现(slf4j-simple) 配置logback日志实现 配置Log4J日志实现(需适配 ...

  8. 《小白WEB安全入门》01. 扫盲篇

    @ 目录 基础知识 什么是WEB 什么是前端 什么是后端 什么是数据库 什么是协议 什么是WEB安全 什么是服务器 什么是IP地址.端口 什么是局域网.广域网.内网.外网 什么是URL 什么是MAC地 ...

  9. ELK环境部署-基础环境安装(一)

    ELK简介 ElasticSearch工作原理以及专用名词 ELK是Elasticsearch(ES) , Logstash, Kibana的结合,是一个开源日志收集软件. Elasticsearch ...

  10. CodeForces 1343E Weights Distributing

    题意 多组样例 给定\(n,m,a,b,c\),给定一个长度为\(m\)的数组\(p[]\),给定\(m\)条边,构成一个\(n\)个点\(m\)条边的无向图,\(Mike\)想要从\(a\)走到\( ...