0%

1. Andorid的消息机制概述

主要是指Handler的运行机制,Handler的运行需要底层的MessageQueue和Looper的支撑。

Handler的主要作用是将一个任务切换到某个指定的线程中去执行,为了解决在子线程中无法切换到UI的矛盾,Andorid规定访问UI必须在主线程中执行,ViewRootImpl对UI线程操作做了验证。

系统为什么不允许在子线程中访问UI呢?

  • Andorid的UI控件不是线程安全的,如果在多线程中并发访问会导致其处于不可预期的状态。
  • 为什么不对UI控件加锁访问?因为加锁会使得访问UI的逻辑变复杂,同时锁机制会降低UI访问的效率。
  • 所以最简单高效的方法就是采用单线程模型来处理UI操作。

2. Android的消息机制分析

2.1. ThreadLocal的工作原理

参考:Java之基础知识汇总#ThreadLocal

2.2. 消息队列的工作原理

  1. MessageQueue主要包括两个操作:插入和读取。
  2. 读取操作本身会伴随着删除操作。
  3. 插入和读取对应的方法分别为enqueueMessage和next。
  4. 它的内部实现并不是队列,而是一个单链表的数据结构来维护消息列表,单链表在插入和删除上比较有优势。

2.3. Looper的工作原理

  1. Looper扮演着消息循环的角色。
  2. Looper除了prepare方法外,还提供了prepareMainLooper方法,这个方法主要是给主线程(即ActivityThread)创建Looper使用的。
  3. Looper提供了quit和quitSafely来退出一个Looper。
  4. Looper的loop方法是一个死循环,唯一跳出循环的方式是MessageQueue的next方法返回null。
  5. Looper处理消息的流程
    • msg.target.dispatchMessage(msg)
    • 这里的dispatch是在Looper中执行的,这样就成功将代码逻辑切换到了指定线程

2.4. Handler的工作原理

  1. Handler创建时会采用当前线程的Looper构建内部消息循环系统,如果当前没有Looper则报错。
  2. Handler通过post或send将msg投递到MessageQueue中。
  3. Looper轮询消息队列并且运行在创建Handler的线程中,这样一来Handler中的业务逻辑就被切换到创建Handler所在的线程中执行了。

问题:有两个Handler同时被创建,Looper是怎样保证将Msg分发到正确的Handler中执行的?

下图是Handler机制原理图,包括了线程的数量关系:

2019年复习归档-Handler机制原理图

3. 主线程的消息循环

Android的主线程就是ActivityThread,主线程的入口是main,在main方法中系统会通过Looper.prepareMainLooper()来创建主线程的Looper和一个MessageQueue,并通过Looper.loop()来开启主线程的消息循环。

ActivityThread通过ApplicationThread和AMS进行进程间通信,AMS以进程间通信的方式完成ActivityThread的请求后回调ApplicationThread中的Binder方法,然后ApplicationThread会向H发送消息。

H收到消息后会将ApplicationThread中的逻辑切换到ActivityThread中去执行,即切换到主线程中去执行。