Android的消息机制

时间:2022-10-27 05:35:59

介绍Handler、Looper、MessageQueue、ThreadLocal和主线程ActivityThread等内容

Android的消息机制

版本:2018/2/6-1

消息机制概述

1、Handler是什么?

  1. Android消息机制的上层接口(从开发角度)
  2. 能轻松将任务切换到Handler所在的线程中去执行
  3. 主要目的在于解决在子线程中无法访问UI的矛盾

2、消息机制?

  1. Android的消息机制主要就是指Handler的运行机制
  2. Handler的运行需要底层MessageQueueLooper的支撑

3、MeesageQueue

  1. 消息队列
  2. 内部存储结构并不是真正的队列,而是单链表的数据结构来存储消息列表
  3. 只能存储消息,而不能处理

4、Looper

  1. 消息循环
  2. Looper以无限循环的形式去查找是否有新消息,有就处理消息,没有就一直等待着。

5、ThreadLocal是什么?

  1. Looper中一种特殊的概念
  2. ThreadLocal并不是线程,作用是可以在每个线程中互不干扰的存储数据提供数据
  3. Handler创建时会采用当前线程的Looper来构造消息循环系统,Handler内部就是通过ThreadLocal来获取当前线程的Looper
  4. 线程默认是没有Looper的,如果需要使用Handler就必须为线程创建Looper
  5. UI线程就是ActivityThread,被创建时会初始化Looper,因此UI线程中默认是可以使用Handler

6、ViewRootImpl对UI操作进行验证,禁止在子线程中访问UI:

void checkThread(){
if(mThread != Thread.currentThread()){
throw new CalledFromWrongThreadException("Only th original thread that created a view hierarchy can touch its views");
}
}

7、Handler的要点

  1. Handler创建时会采用当前线程的Looper
  2. 如果当前线程没有Looper就会报错,要么创建Looper,要么在有Looper的线程中创建Handler
  3. Handlerpost方法会将一个Runnable投递到Handler内部的Looper中处理(本质也是通过send方法完成)
  4. Handlersend方法被调用时,会调用MessageQueueenqueueMessage方法将消息放入消息队列, 然后Looper发现有新消息到来时,就会处理这个消息,最终消息中的Runnable或者HandlerhandleMessage就会被调用
  5. 因为Looper是运行在创建Handler所在的线程中的,所以Handler中的业务逻辑就会被切换到创建Handler所在的线程中

ThreadLocal

8、ThreadLocal的作用

  1. ThreadLocal是线程内部的数据存储类,可以在指定线程中存储数据,之后只有在指定线程中才开业读取到存储的数据
  2. 应用场景1:某些数据是以线程为作用域,并且不同线程具有不同的数据副本的时候。ThreadLocal可以轻松实现Looper在线程中的存取。
  3. 应用场景2:在复杂逻辑下的对象传递,通过ThreadLocal可以让对象成为线程内的全局对象,线程内部通过get就可以获取。

9、ThreadLocal的使用

mBooleanThreadLocal.set(true);
Log.d("ThreadLocal", "[Thread#main]" + mBooleanThreadLocal.get());

new Thread("Thread#1"){
@Override
public void run(){
mBooleanThreadLocal.set(true);
Log.d("ThreadLocal", "[Thread#1]" + mBooleanThreadLocal.get());
}
}.start();

new Thread("Thread#2"){
@Override
public void run(){
Log.d("ThreadLocal", "[Thread#2]" + mBooleanThreadLocal.get());
}
}.start();
  1. 最终main中输出true; Thread#1中输出false; Thread#2中输出null
  2. ThreadLocal内部会从各自线程中取出数组,再根据当前ThreadLocal的索引去查找出对应的value值。

10、ThreadLocal的set()源码分析

//ThreadLocal.java
public void set(T value) {
//1. 获取当前线程
Thread t = Thread.currentThread();
//2. 获取当前线程对应的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null)
//3. map存在就进行存储
map.set(this, value);
else
//4. 不存在就创建map并且存储
createMap(t, value);
}
//ThreadLocal.java内部类: ThreadLocalMap
private void set(ThreadLocal<?> key, Object value) {
//1. table为Entry数组
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
//2. 根据当前ThreadLocal获取到Hash key,并以此从table中查询出Entry
for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
//3. 如果Entry的ThreadLocal与当前的ThreadLocal相同,则用新值覆盖e的value
if (k == key) {
e.value = value;
return;
}
//4. Entry没有ThreadLocal则把当前ThreadLocal置入,并存储value
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
//5. 没有查询到Entry,则新建Entry并且存储value
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
//ThreadLocal内部类ThreadLocalMap的静态内部类
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}

11、ThreadLocal的get()源码分析

public T get() {
//1. 获取当前线程对应的ThreadLocalMap
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
//2. 取出map中的对应该ThreadLocal的Entry
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
//3. 获取到entry后返回其中的value
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//4. 没有ThreadLocalMap或者没有获取到ThreadLocal对应的Entry,返回规定数值
return setInitialValue();
}
private T setInitialValue() {
//1. value = null
T value = initialValue();//返回null
Thread t = Thread.currentThread();
//2. 若不存在则新ThreadLocalMap, 在里面以threadlocal为key,value为值,存入entry
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
  1. 当前线程对应了一个ThreadLocalMap
  2. 当前线程的ThreadLocal对应一个Map中的Entry(存在table中)
  3. Entrykey会获取其对应的ThreadLocal, value就是存储的数值

消息队列: MessageQueue

12、MessageQueue的主要操作

  1. enqueueMessage: 往消息队列中插入一条消息
  2. next:取出一条消息,并且从消息队列中移除
  3. 本质采用单链表的数据结构来维护消息队列,而不是采用队列

13、MessageQueue的插入和读取源码分析

//MessageQueue.java:插入数据
boolean enqueueMessage(Message msg, long when) {
//1. 主要就是单链表的插入操作
synchronized (this) {
......
}
return true;
}
/**==========================================
* 功能:读取并且删除数据
* 内部是无限循环,如果消息队列中没有消息就会一直阻塞。
* 一旦有新消息到来,next方法就会返回该消息并且将其从单链表中移除
*===========================================*/

Message next() {
for (;;) {
......
}
}

Looper

14、Looper的构造

private Looper(boolean quitAllowed) {
//1. 会创建消息队列: MessageQueue
mQueue = new MessageQueue(quitAllowed);
//2. 当前线程
mThread = Thread.currentThread();
}

15、为线程创建Looper

//1. 在没有Looper的线程创建Handler会直接异常
new Thread("Thread#2"){
@Override
public void run(){
Handler handler = new Handler();
}
}.start();

异常:
java.lang.RuntimeException: Can’t create handler inside thread that has not called Looper.prepare()

//2. 用prepare为当前线程创建一个Looper
new Thread("Thread#2"){
@Override
public void run(){
Looper.prepare();
Handler handler = new Handler();
//3. 开启消息循环
Looper.loop();
}
}.start();

16、主线程ActivityThread中的Looper

  1. 主线程中使用prepareMainLooper()创建Looper
  2. getMainLooper能够在任何地方获取到主线程的Looper

17、Looper的退出

  1. Looper的退出有两个方法:quitquitSafely
  2. quit会直接退出Looper
  3. quitSafely只会设置退出标记,在已有消息全部处理完毕后才安全退出
  4. Looper退出后,Handler的发行的消息会失败,此时send返回false
  5. 子线程中如果手动创建了Looper,应该在所有事情完成后调用quit方法来终止消息循环

18、Looper的loop()源码分析

//Looper.java
public static void loop() {
//1. 获取Looper
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//2. 获取消息队列
final MessageQueue queue = me.mQueue;
......
for (; ; ) {
//3. 获取消息,如果没有消息则会一直阻塞
Message msg = queue.next();
/**=================================
* 4. 如果消息获得为null,则退出循环
* -Looper退出后,next就会返回null
*=================================*/

if (msg == null) {
return;
}
......
/**==========================================================
* 5. 处理消息
* -msg.target:是发送消息的Handler
* -最终在该Looper中执行了Handler的dispatchMessage()
* -成功将代码逻辑切换到指定的Looper(线程)中执行
*========================================================*/

msg.target.dispatchMessage(msg);
......
}
}

Handler

19、Handler的post/send()源码分析

//Handler.java: post最终是通过send系列方法实现的
//Handler.java
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
//Handler.java
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
//Handler.java
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);
}
//Handler.java
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//1. 最终是向消息队列插入一条消息
return queue.enqueueMessage(msg, uptimeMillis);
}
  1. sendMessage()会将消息插入到消息队列中
  2. MessageQueuenext方法就会返回这条消息交给Looper
  3. 最终Looper会把消息交给HandlerdispatchMessage

20、Handler的消息处理源码

//Handler.java
public void dispatchMessage(Message msg) {
//1. Msg的callback存在时处理消息——Handler的post所传递的Runnable
if (msg.callback != null) {
handleCallback(msg);
} else {
/**===============================================
*2. mCallback不为null时调用handleMessage
* -Handler handle = new Handler(callback)
* -好处在于不需要派生Handler子类并且也不需要重写其handleMessage
*=============================================*/

if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//3. 如果其他条件都不符合,最后会调用Handler的handleMessage进行处理
handleMessage(msg);
}
}
//Handler.java-调用Handler的post所传递的Runnable的run()方法
private static void handleCallback(Message message) {
message.callback.run();
}
//Handler.java-Callback接口用于不需要派生Handler就能完成功能
public interface Callback {
public boolean handleMessage(Message msg);
}

21、Handler的特殊构造方法

  1. Handler handle = new Handler(callback);-不需要派生Handler
  2. 通过特定Looper构造Handler
public Handler(Looper looper) {
this(looper, null, false);
}
  1. 默认构造函数
public Handler(Callback callback, boolean async) {
......
mLooper = Looper.myLooper();
//1. 在没有Looper的线程中创建Handler
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

主线程的消息循环

22、主线程ActivityThread的消息循环

//ActivityThread.java
public static void main(String[] args) {
//1. 创建主线程的Looper和MessageQueue
Looper.prepareMainLooper();
......
//2. 开启消息循环
Looper.loop();
}
/**=============================================
* ActivityThread中需要Handler与消息队列进行交互
* -内部定义一系列消息类型:主要有四大组件等
* //ActivityThread.java
*=============================================*/

private class H extends Handler {
public static final int LAUNCH_ACTIVITY = 100;
public static final int PAUSE_ACTIVITY = 101;
public static final int PAUSE_ACTIVITY_FINISHING= 102;
......
}
  1. ActivityThread通过ApplicationThreadAMS进行IPC通信
  2. AMS完成请求的工作后会回调ApplicationThread中的Binder方法
  3. ApplicationThread会向Handler H发送消息
  4. H接收到消息后会将ApplicationThread的逻辑切换到ActivityThread中去执行