Handler、Looper、MessageQueue消息处理讲解

一、Handler的使用可以查看我这篇文章

Handler的使用和内存泄漏处理

二、源码分析Handler消息处理的流程

我们要玩的东西也不是很难,我们通过源码来分析Handler如何发送消息到MessageQueue,然后Looper如何如何将消息从MessageQueue取出分发
给Handler

(一)、创建主线程的MessageQueue和Looper对象
  1. 首先查看应用的主线程ActivityThread的main方法
public static void main(String[] args) {
     // 创建一个Looper对象,并创建MessageQueue对象
     Looper.prepareMainLooper();

     // 开启消息轮询,轮询消息
     Looper.loop();
}
  1. 查看**prepareMainLooper()**方法
public static void prepareMainLooper() {
        // 最终还是调用了prepare()方法
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
  1. 查看**prepare()**方法
private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        // 创建一个Looper对象并且存入ThreadLocal中
        // 主线程的Looper对象
        sThreadLocal.set(new Looper(quitAllowed));
    }

此处我们可以查看下sThreadLocal的set()方法,其实就是以当前线程为key来保存数据

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
  1. 查看Looper的构造方法new Looper(quitAllowed)
private Looper(boolean quitAllowed) {
        // 在Looper的构造方法中创建了全局唯一的主线程的
        // MessageQueue对象
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

至此,主线程的Looper对象和MessageQueue对象已经创建完成,可以用来处理消息了

(2)、将消息发送到消息队列
  1. 我们就简单的看一下sendMessage方法,其他的传递消息的方法都是一样的流程
public final boolean sendMessage(@NonNull Message msg) {
        // 其实调用的还是发送延迟消息的方法
        return sendMessageDelayed(msg, 0);
    }
  1. 继续查看sendMessageDelayed
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        // 最终是会调用到如下发送消息的方法
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
  1. 查看sendMessageAtTime方法
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
        // 这个mQueue哪里来的?后面讲解到Handler创建的源码的时候
        // 我们就会看到,暂时挖个坑,O(∩_∩)O哈哈~
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        // 调用到enqueueMessage方法,并且将消息队列的对象
        // queue传递到方法中
        return enqueueMessage(queue, msg, uptimeMillis);
    }
  1. 接下来我们查看enqueueMessage方法,最终我们可以看到此时是调用了MessageQueue的enqueueMessage方法将消息存入消息队列
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        // Looper的loop方法中分发消息的Handler对象就是在此处赋值的
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        // 最终调用的是MessageQueue的消息入队方法
        return queue.enqueueMessage(msg, uptimeMillis);
    }
  1. 最终我们来到了MessageQueue的enqueueMessage方法中,将消息存入消息队列
    mMessages = msg
boolean enqueueMessage(Message msg, long when) {
       ......
        synchronized (this) {
            ......
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                // 最终看到此时是将传递进来的消息赋值给了全局的
                // Message对象"mMessages"
                mMessages = msg;
                needWake = mBlocked;
            } else {
               ......
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
(三)、从消息队列取消息
  1. 回到ActivityThread的main方法,我们看到在main方法中中调用了Looper的loop方法来轮询消息

  2. 查看Looper的loop方法

public static void loop() {
    // 获取Looper对象,回头看上面在Looper的prepare方法中,
    // 已经将Looper的对象保存在sThreadLocal
	final Looper me = myLooper();
	// 从Looper中取出消息队列MessageQueue对象,
	// mQueue在Looper的构造方法中被创建
	final MessageQueue queue = me.mQueue;
	.......
	// 一个死循环来取出消息
	for (;;) {
	    // 将消息从消息队列中取出
		Message msg = queue.next();
        ......
        // 最终调用Handler的dispatchMessage方法将消息分发到Handler
        // msg.target是在消息存入消息队列的时候赋的值,可以往前查看发送消息的源码,有标注
        msg.target.dispatchMessage(msg);
        .......
	}
}
// myLooper方法其实就是从sThreadLocal中根据key(当前线程)
// 取出当前的Looper对象
public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
  1. 查看MessageQueue的next方法
Message next() {
    // 此方法获取到消息并且将消息返回
    ....
    // 我们可以看到如下代码,全局的消息对象赋值给当前消息
    // mMessages对象在保存消息的时候赋的值
    Message msg = mMessages;
    ....
    return msg;
}
  1. 最终我们继续回到Looper的loop方法中去查看,此时调用了Handler的
    dispatchMessage方法将消息分发给Handler处理
public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            // Handler的post方法就会走到如下的逻辑
            // 有兴趣可以看下我上一篇讲解Handler的使用,可以看到
            // 这里为什么是post方法的处理逻辑
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                // mCallback就是我们创建Handler的时候传递的CallBack对象
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
(四)、Handler的使用

Handler的使用和内存泄漏处理

我们查看Handler的构造方法

public Handler(@Nullable Callback callback, boolean async) {
        ......
        // 此处获取Looper对象,在Looper的prepare方法的时候已经存入
        // ThreadLocal中
        mLooper = Looper.myLooper();
        // 如果取不到Looper对象就会抛出异常
        // 这里也是为什么不能直接在子线程创建Handler的原因,在子线程
        // 中取不到Looper对象
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        // mQueue对象就是在Looper的构造方法中创建的
        // 消息存入消息队列的MessageQueue对象就是在此处赋值的
        // 这是全局唯一的MessageQueue对象
        mQueue = mLooper.mQueue;
        // 将Callback赋值给全局对象,消息的分发会用到
        mCallback = callback;
        mAsynchronous = async;
    }

如果要在子线程中使用Handler对象,可以去查看HandlerThread的源码,
会教你怎么使用。O(∩_∩)O哈哈~

总的流程图
在这里插入图片描述

三、日常的一些问题

1、为什么主线程Looper使用死循环不会引起ANR异常?

因为Looper.loop()在开启死循环的时候,一旦需要等待时,或者还没有执行到需要执行的代码的时候,会调用NDK中的JNI方法释放当前时间片,
这样就不会引发ANR异常了。

Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();

2、为什么Handler构造方法中的Looper不是直接new?

如果在Handler中直接new一个Looper,无法保证Looper的唯一,只有
Looper.prepare()才能保证Looper的唯一性。

private static void prepare(boolean quitAllowed) {
        // 判断如果此时Looper已经存在就会直接抛出异常,保证Looper的唯一
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        // 直接使用私有的构造方法new一个Looper存入ThreadLocal,
        // 根据当前线程作为key值取存储的
        sThreadLocal.set(new Looper(quitAllowed));
    }
// Looper的私有的构造方法
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

3、MessageQueue为什么要放在Looper的私有构造方法中来初始化?

因为一个线程只能绑定一个Looper,所以在Looper的私有构造方法中初始化就可以保证MessageQueue也是唯一的线程对应一个Looper对应一个MessageQueue。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吃骨头不吐股骨头皮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值