oldPAndroid开发Android技术知识

深入理解Handler机制之引路篇

2017-07-08  本文已影响140人  剑舞潇湘

要想深入理解Handler机制,就要理解:

如果去查阅官方Handler文档,会发现官方没有给Handler下过定义,更没有介绍为何要引入Handler机制。官方文档的第一句就说Handler允许你发送和处理与一个线程的消息队列关联的消息和任务。这是对Handler能力和用法的一种描述,并没有讲为什么和是什么,这就造成初学者只知道怎么用Handler,但是不知道其背后的设计思想和原理。

Android为何引入Handler

要知道Android为何引入Handler机制,首先要了解Java线程机制:

这种机制的问题在于,创建线程的开销非常大,即使通过线程池重用线程,线程调度的开销对于数量多、密度大、要求快速响应的UI更新任务来说也是不可接受的,于是Android引入Handler线程解决这个问题。Handler线程就是Handler机制的核心:Handler线程永不退出,在run方法中轮询任务,有任务就处理,没任务就等待。

有些Handler线程是允许退出的。

Android应用的主线程是一个不允许退出、生命周期与进程一样长的线程。

Handler机制工作原理

Handler机制是一整套任务处理机制,需要Handler、Looper、Message和MessageQueue配合。

Message是Android中的一个重要概念,它把Android应用从Java的任务处理系统变成了消息处理系统。

Handler机制工作原理

Looper

要把一个线程变成Handler线程,只需在线程的run方法中调用Looper.prepare()和Looper.loop()。

class LooperThread extends Thread {
    public void run() {
        Looper.prepare();
        Looper.loop();
    }
}

Looper的创建非常巧妙,是在Looper.prepare()中完成的。Looper实例存储在ThreadLocal中,可以通过Looper.myLooper()获取。Looper默认是可以退出的,要创建不可退出的Looper,需要调用Looper.prepare(false),主线程Looper正是这样创建的。要注意的是,一个线程只能与一个Looper关联。

Looper创建后通过Looper.loop()完成工作。正如上文所说,Looper.loop()在Thread.run方法中轮询消息,使线程不退出、消息串行处理。

// Looper.java
public static void loop() {
    for (;;) {
        Message msg = queue.next(); // might block
        msg.target.dispatchMessage(msg);
    }
}

MessageQueue

MessageQueue是在Looper的构造函数内创建的。由此可知,线程、Looper和MessageQueue三者是一一对应的。

MessageQueue的主要工作是处理Message入列和安排Message出列。MessageQueue并不是常规的先进先出队列,而是根据Message期望执行的时间排列的。

这两个方法都有用到native代码,实现比较复杂,就不分析了(因为没看懂😄)。

Message

Message是一个数据类,携带要执行的任务(Runnable)或执行任务所需的数据(what、arg1、arg2、obj、data)以及任务处理者(Handler)和任务执行时间(when)。Message的实现是一个单链表结点,因此可以携带下一条Message。MessageQueue正是基于Message单链表实现的。

由于是数据类,且应用中需要大量Message,因此Message实现了重用缓存池,通过Message.obtain()获取。与MessageQueue一样,Message缓存池也是基于其单链表实现的,不同的是缓存池是栈而非队列。

Handler

官方文档说,Handler与创建它的线程及该线程的MessageQueue关联(When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it)。这说的是不指定Looper的情况下,Handler自动与该线程的Looper关联,上文说,线程、Looper和MessageQueue三者一一对应,因此默认情况下,Handler与创建它的线程及该线程的MessageQueue关联。但在创建Handler时是可以指定Looper的,这样就会与Looper所在的线程和MessageQueue关联。

不管Handler在哪个线程创建,它发出的消息都是在与之关联的Looper所在的线程处理的,这正是Handler用于线程间通信的原理。由于后台线程不允许更新UI,因此后台线程通过一个与主线程Looper关联的Handler(new Handler(Looper.getMainLooper()))来更新UI。

Handler发送消息有两种方式:

但其实就一种,因为Runnable也会转为Message。Handler发送消息还可以设置延时或定时,但最终也是一种,因为延时也会被转为定时任务。因此所有的发送方式最终都调用下列方法处理:

// Handler.java
public boolean sendMessageAtTime(Message msg, long uptimeMillis)

放入MessageQueue中的Message会被逐个取出,串行处理。有3处对象可以处理消息:

  1. Message自身callback(一般是post(Runnable)方式发送的消息)
  2. Handler创建时设置的mCallback
  3. Handler
// Handler.java
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

HandlerThread

Android应用的大部分消息都可以由主线程处理,如果需要在后台线程中处理消息,可以创建一个新的Handler线程。

开发者并不需要自己实现Handler线程,直接使用内置的HandlerThread即可:

HandlerThread handlerThread = new HandlerThread("MyHandlerThread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());

源码篇和实现篇

要想深入理解Handler机制,光凭一篇引路文章是不够的,更重要的是阅读源码、动手实践和总结分享。

是不是很期待源码篇和实践篇?你来写!

看好你哦~

拓展阅读

上一篇下一篇

猜你喜欢

热点阅读