Android 的异步消息处理机制

前言

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 掉,这样也能消除持有链
posted @ 2022-03-05 20:36  wx2020  阅读(473)  评论(0编辑  收藏  举报