这些关于 Handler 的知识点你都知道吗?

在安卓面试中,关于 Handler 的问题是必备的,但是这些关于 Handler 的知识点你都知道吗?

一、题目层次

  1. Handler 的基本原理
  2. 子线程中怎么使用 Handler
  3. MessageQueue 获取消息是怎么等待
  4. 为什么不用 wait 而用 epoll 呢?
  5. 线程和 Handler Looper MessageQueue 的关系
  6. 多个线程给 MessageQueue 发消息,如何保证线程安全
  7. Handler 消息延迟是怎么处理的
  8. View.post 和 Handler.post 的区别
  9. Handler 导致的内存泄漏
  10. 非 UI 线程真的不能操作 View 吗

二、题目详解

代码分析基于 Android SDK 28

大家可以先看上面的问题思考一下,如果都清楚的话,下面的文章也没必要看了~

1. Handler 的基本原理

关于 Handler 的原理,相比不用多说了,大家都应该知道,一张图就可以说明(图片来自网络)。

handler

2. 子线程中怎么使用 Handler

除了上面 Handler 的基本原理,子线程中如何使用 Handler 也是一个常见的问题。
子线程中使用 Handler 需要先执行两个操作:Looper.prepare 和 Looper.loop。
为什么需要这样做呢?Looper.prepare 和 Looper.loop 都做了什么事情呢?
我们知道如果在子线程中直接创建一个 Handler 的话,会报如下的错误:

"Can't create handler inside thread xxx that has not called Looper.prepare()

我们可以看一下 Handler 的构造函数,里面会对 Looper 进行判断,如果通过 ThreadLocal 获取的 Looper 为空,则报上面的错误。

    public Handler(Callback callback, boolean async) {
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
    }

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

那么 Looper.prepare 里做了什么事情呢?

    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));
    }

可以看到,Looper.prepare 就是创建了 Looper 并设置给 ThreadLocal,这里的一个细节是每个 Thread 只能有一个 Looper,否则也会抛出异常。
而 Looper.loop 就是开始读取 MessageQueue 中的消息,进行执行了。

这里一般会引申一个问题,就是主线程中为什么不用手动调用这两个方法呢?相信大家也都明白,就是 ActivityThread.main 中已经进行了调用。
通过这个问题,又可以引申到 ActivityThread 相关的知识,这里就不细说了。

3. MessageQueue 如何等待消息

上面说到 Looper.loop 其实就是开始读取 MessageQueue 中的消息了,那 MessageQueue 中没有消息的时候,Looper 在做什么呢?我们知道是在等待消息,那是怎么等待的呢?

通过 Looper.loop 方法,我们知道是 MessageQueue.next() 来获取消息的,如果没有消息,那就会阻塞在这里,MessageQueue.next 是怎么等待的呢?

    public static void loop() {
        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;
            }
        }
    }
    Message next() {
        for (;;) {
            nativePollOnce(ptr, nextPollTimeoutMillis);
            // ...
        }
    }

在 MessageQueue.next 里调用了 native 方法 nativePollOnce。

// android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
        jlong ptr, jint timeoutMillis) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}

void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
    // ...
    mLooper->pollOnce(timeoutMillis);
    // ...
}

// Looper.cpp
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    // ...
    result = pollInner(timeoutMillis);
    // ...
}

int Looper::pollInner(int timeoutMillis) {
    // ...
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
}

从上面代码中我们可以看到,在 native 侧,最终是使用了 epoll_wait 来进行等待的。
这里的 epoll_wait 是 Linux 中 epoll 机制中的一环,关于 epoll 机制这里就不进行过多介绍了,大家有兴趣可以参考 segme

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值