huanzhiyazi / articles

我的原创文章,包括且不限于技术 blog,历史,文学写作,日常心得等
103 stars 32 forks source link

技术 / android / 线程间通信模型——Handler #4

Open huanzhiyazi opened 4 years ago

huanzhiyazi commented 4 years ago

目录



1 Handler 简介[Top]

在 android 开发中,Handler 通常用于 UI 线程与工作线程之间的通信。由于 UI 线程的高实时性要求,我们必须将一些 IO 或者高计算量的任务分派给一个独立的工作线程去运行,这样在 UI 线程与工作线程之间就需要某种方式来进行通信,比如告知 UI 线程任务的完成情况。Handler 就是为此目的而实现。

在 android 系统的早期版本,Handler 的使用频率相当高,即便是当初用的非常普遍的 AsyncTask,其内部实现也需要 Handler 充当通信桥梁。此外,像 IntentService 也是基于此实现。

如果不谈繁琐的源代码细节,总体来说,Handler 实现线程间通信是基于生产者-消费者模型。



2 简单的生产者-消费者模型[Top]

一个简单的生产者-消费者模型如下图所示:

Simple Producer-Consumer

在该模型中,有 4 个主要的角色:



3 Handler 中的生产者-消费者模型[Top]

Handler 继承了生产者-消费者模型的思想,如下图所示:

Handler Producer-Consumer

其要点如下:



4 一些思考[Top]

以上基本上就是 Handler 的实现思想。剩下还有两个点需要注意一下:

  1. Handler 循环访问消息队列这个操作,必须是独占的。也就是说,必须只有消费者线程自己能从消息队列中取消息,试想一下,如果其它线程也能从消息队列中取消息,那么本来应该发送给消费者线程的消息就会丢失,造成安全问题。在实现中该操作委托给了一个 Looper 类,它通过 ThreadLocal ,用线程本地化操作的方式,将自己绑定到当前的消费者线程。
public final class Looper {
    // ...
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    // ...

    public static void prepare() {
        prepare(true);
    }

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

        // 初次实例化,保证一个 thread 只有一个 looper
        sThreadLocal.set(new Looper(quitAllowed));
    }

    // ...

    public static @Nullable Looper myLooper() {
        // 每次都返回的 Looper 都已经被某个消费者线程绑定了,这样 Looper 的所有操作都限定在这个线程里
        return sThreadLocal.get();
    }

    // ...
}
  1. 我们看到,Handler 是和消费者线程绑定在一起的,那么为什么不和生产者线程绑定呢?其实上述第一点就已经同时解释了这个问题。Handler 绑定到消费者线程的本质就是将 Looper 绑定到消费者线程。那么如果将 Looper 绑定到生产者线程,然后由生产者轮询取得消息后通知消费者线程可以么?如果消费者线程和生产者线程都不是 UI 线程,理论上也是可行的。但是大部分情况下,消费者线程都是 UI 线程,我们知道,UI 线程中的对象都是 UI 线程独占的,不允许其它线程访问,如果生产者来通知 UI 线程,就必须持有 UI 线程的对象,这违反了 UI 线程框架设计的原则。关于 UI 线程框架设计原则的论述,大家可以参考 Java Concurrency In Practice 这本书的 Part II Structuring Concurrent Applications / GUI Appliacations 这一章。