Handler 原理

Handler 的原理看这张图就可以知道

handler
Handler sendMessage(msg) 会调用 sendMessageAtTime(msg, uptimeMillis), 在这里会调 enqueueMessage(queue, msg, uptimeMillis), 其中 queue 是 handler 的全局变量 mQueue, 在构造函数里,有这两行代码

mLooper = Looper.myLooper();
mQueue = mLooper.mQueue;

最终会调用 queue.enqueueMessage(msg, uptimeMiilis), 在这个方法里会设置 msg.target = this, 然后把该 msg 插入 queue 中。
Looper.loop() 方法会循环取 mQueue 中的值,取到消息就分发执行。

思考:

1. 为什么子线程中直接 new Handler() 会报异常呢?
mLooper = Looper.myLooper();
if (mLooper == null) {
    throw new RuntimeException(
        "Can't create handler inside thread that has not called Looper.prepare()");
}

上面一段代码是 Handler 的构造方法中的一段代码,创建 Handler 时会取 Looper.myLooper(), 返回的是当前线程的 Looper 对象,由于子线程中还没有 Looper 对象,所以会报异常。主线程是在 ActivityThread 的 main 方法中创建的 Looper 对象。
注:子线程先调用 Looper.prepare() 之后也可以创建 Handler, 但是更新 UI 时不建议这样做,因为这样还是在子线程中更新的 UI, 还是线程不安全的。

2. 一个线程只能有一个 Looper 对象?

创建 Looper 对象是调用 Looper.prepare, 其源码如下:

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

这里使用 ThreadLocal 保证了一个线程只有一个 Looper 对象。

3. 主线程的 Looper.loop 无限循环为什么不会造成 ANR?
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
        }

之所以不会造成 ANR 就是因为消息的阻塞和唤醒机制,重点在 queue.next() 获取新的消息时,

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

            nativePollOnce(ptr, nextPollTimeoutMillis); // 重点在这里,
        }
    }

nativePollOnce 会引起阻塞消息队列,在唤醒时间到的时候再唤醒。
在此处会唤醒,还有在 enqueueMessage 时,如果有在消息队列头部插入的话,就会调用唤醒 nativeWake(mPtr).
推荐这篇文章https://my.oschina.net/youranhongcha/blog/492591,讲的很清楚。

4. Handler发送延迟消息的原理

发送延迟消息,最终一样都是调用 sendMessageAtTime,给设置一个唤醒的时间,在时间到了就唤醒。

5. 是否可以停止消息循环

Looper 提供了一个 quit() 方法,那么主线程的 Looper 能否停止循环呢,试图调 Looper.getMainLooper().quit() 会抛出异常 “Main thread not allowed to quit.”,原因:

// MessageQueue
void quit(boolean safe) {
    /* mQuitAllowed 是在创建 MessageQueue 时指定的,主线程创建 Looper 是调用 Looper.prepareMainLoop(), 传进来的是 false, 所以停止主线程的 Looper 会抛异常 */
    if (!mQuitAllowed) { 
        throw new IllegalStateException("Main thread not allowed to quit.");
    }
    // ...
}

子线程的 Looper 创建是调 Looper.prepare, 传的 mQuitAllowed 是 true, 所以子线程是可以停止的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值