Android消息循环机制

1)Handler、Looper、MessageQueue

Handler成员变量有mLooper、mQueue,它们分别是Looper和MessageQueue的引用。在实例化Handler时,Handler通过Lopper.myLooper()来获取本线程的Looper实例,再从Looper中获取消息队列,mQueue = mLooper.mQueue。没错,消息队列是在Looper中存储的。

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

可以看到,Looper实例其实是从线程本地存储类ThreadLocal中获取到的。
这里只看到Looper实例从线程中获取,但是,是什么时候添加进线程中去的呢?
我们看下Android应用程序的主入口ActivityThread.main

public static void main(String[] args) {
    ...
    Looper.prepareMainLooper();
    ...
    Looper.loop();

查看Looper.prepareMainLooper的调用层次,最后调用到Looper.perpare方法,在此方法中就会新建一个Looper的实例,并且传入线程本地中

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

另外Handler有send和post两种,send方法一般有一个Message类型的参数,封装了要发送的信息。post方法一般有一个Runnable类型的参数,并都会通过handler中的getPostMessage(Runnable r)封装成一个Message。这两个方法最后都会调用到sendMessageAtTime,在此方法中就会将发送的消息进入消息队列。

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    ...
    return enqueueMessage(queue, msg, uptimeMillis);
}

最后,通过Looper.loop方法,开启消息循环。追踪一下消息是如何处理的。

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;
    //无限循环取消息
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        ...
        msg.target.dispatchMessage(msg);
        ...
    }
}

msg.target.dispatchMessage(msg);target是Handler类型的,也就是说,一般一个Message也会持有一个发送它的Handler的引用。通过这个引用,调用Handler的dispatchMessage来分发消息

public void dispatchMessage(Message msg) {
    //使用post发送时,msg封装了callback,在此处处理
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        //创建Handler时,如果注入了CallBack实例,在此处处理
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //这是我们一般会重写的方法
        handleMessage(msg);
    }
}

//下面是Callback接口的形式
public Handler(Callback callback) {
    this(callback, false);
}
public interface Callback {
    public boolean handleMessage(Message msg);
}

推荐阅读:带着这篇去通关所有Handler的提问Handler带来的内存泄漏

2)在子线程中创建Handler为什么会抛出异常

回忆一下,在创建Handler实例时,要通过Looper.myLooper获取本线程的Looper,但是一开始,线程本地是没有Looper的。主线程可以直接创建Handler,是因为在程序入口处,调用Looper.prepareMainLooper在主线程加入了Looper,回顾一下,此方法最终调用Looper.prepare在线程本地添加Looper,所以要在子线程中创建Handler,可以在创建前,调用Looper.prepare,并且在调用后开启消息循环,Looper.loop。

3)ThreadLocal类

上面讲到Looper.prepare方法会在线程本地存入Looper对象,其实就是存到ThreadLocal对象里面了。
Looper相关:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

下面是ThreadLocal的set方法:

public void set(T value) {
    Thread currentThread = Thread.currentThread();
    Values values = values(currentThread);
    if (values == null) {
        values = initializeValues(currentThread);
    }
    values.put(this, value);
}

可见ThreadLocal存储的键是当前线程对象,所以不同的线程就有不同的ThreadLocal,且不会发生同步等问题,这种设计让人焕然一新。

另外,在Looper类中,会一直持有UI线程的Looper对象,可以通过getMainLooper获得,用于与UI线程通信。

推荐阅读:
Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系、
Android Handler 异步消息处理机制的妙用 创建强大的图片加载类
图解消息循环机制

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值