Handler消息机制



认识Handler类

首先看一下Handler源码的官方介绍:

A Handler allows you to send and process {@link Message} and Runnable objects associated with a thread’s {@link MessageQueue}. Each Handler instance is associated with a single thread and that thread’s message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it – from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
There are two main uses for a Handler: (1) to schedule messages and runnables to be executed at some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.

简单翻译一下:

Handler允许你发送和处理与线程的 MessageQueue相关的 Message/Runnable对象,每个Handler实例都与一个线程及该线程的 MessageQueue关联。当你创建一个新的 Handler,它就会与创建它所在的线程及该线程的 MessageQueue关联起来。之后,Handler就可以发送 Message/RunnableMessageQueue,并可以执行从 MessageQueue出来的 Message/Runnable
Handler主要有两种用途:1、规划在将来的某个时刻执行 Message/Runnable;2、在与自己不同的线程上执行操作。

从文档的介绍可以知道,与Handler的功能紧密相连的几个关键类有:

  • MessageQueue 消息队列
  • Message 消息对象
  • Runnable 可运行对象

下面我们会一一熟悉这些类及其作用。

Handler的初始化
public Handler() {    
        this(null, false);
}

public Handler(@Nullable Callback callback) {    
        this(callback, false);
}

public Handler(@NonNull Looper looper) {    
        this(looper, null, false);
}

public Handler(@NonNull Looper looper, @Nullable Callback callback) {    
        this(looper, callback, false);
}

以上是Handler提供的初始化方法中的一部分,可以看到最终都会进入到

Handler(@Nullable Callback callback, boolean async)
Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async)

这两个方法里面去,那么我们来看下这两个方法的区别:

public Handler(@Nullable 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()");   
        }    
        mQueue = mLooper.mQueue;    
        ··· ···
}

public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {    
        mLooper = looper;    
        mQueue = looper.mQueue;    
        ··· ···
}

以上的省略号表示省略了一些代码,这里我们只关注重点。

可以看到,Handler中有两个重要的参数需要在构造的时候初始化,一个是 mLooperLooper,一个是 mQueueMessageQueue对象,如果没有 LooperHandler初始化的时候会直接抛出异常,而 MessageQueue则直接从 Looper获取。

既然 Looper如此重要,我们不如先看一下 Looper是什么。



Looper

看一下 Looper源码的介绍:

Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call {@link #prepare} in the thread that is to run the loop, and then {@link #loop} to have it process messages until the loop is stopped.

简单翻译下:

Looper用来为线程运行一个消息循环。线程默认没有关联的消息循环,要创建一个,需在该线程调用 Looper.prepare()方法,然后调用 Looper.loop()让它来处理消息直到循环被停止。

Looper的初始化

如文档里所介绍的,要创建一个 Looper,需调用 Looper.prepare()方法,那么我们来看下这个方法到底做了什么。

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");    
        }    
        sThreadLocal.set(new Looper(quitAllowed));
}

private Looper(boolean quitAllowed) {    
        mQueue = new MessageQueue(quitAllowed);    
        mThread = Thread.currentThread();
}

可以看到Looper初始化的时候创建了一个 MessageQueue,而前面提到过的Handler初始化的时候需要的MessageQueue,就是在这里创建的。同时,Looper初始化的时候还获取了当前线程。

注意:从 prepare()方法可知,一个线程只能创建一个 Looper

我们回到 Looper源码介绍部分,一个线程里默认不存在 Looper,要创建一个,需要调用 Looper.prepare()方法,这个方法不但新建了一个 Looper,同时还与当前线程关联起来,并且还新建了一个与之关联的 MessageQueue

Handler源码介绍部分有这么一句话: 当你创建一个新的 Handler,它就会与创建它所在的线程及该线程的 MessageQueue关联起来。到这里我们应该已经看明白了它们是如何相互关联的。

Looper如何处理消息

文档介绍提到 Looper通过 Looper.loop()方法来处理消息,我们来看一下它在这里做了什么:

public static void loop() {   
        //获取当前线程的Looper    
        final Looper me = myLooper();   
        //如果没有则抛异常    
        if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");   
        }    
        //获取Looper里的MessageQueue    
        final MessageQueue queue = me.mQueue;    
        ··· ···    
        //开启一个无限循环   
        for (;;) {        
                //从MessageQueue拿出Message        
                Message msg = queue.next(); // might block        
                ··· ···        
                //派发Message
                msg.target.dispatchMessage(msg);        
                ··· ···    
        }
}

上面的代码缩略了很多细节,我们关注重点,调用 LooperLoop()方法之后,Looper开启了一个无限循环,从 MessageQueueMessage,拿到之后交给 Messagetarget来处理。
那么 Message里面的 target是什么呢?Message又是如何被放入 MessageQueue中的呢?

我们接下来需要了解一下 MessageQueueMessage



MessageQueue

仍然是看官方文档介绍:

Low-level class holding the list of messages to be dispatched by a {@link Looper}. Messages are not added directly to a MessageQueue, but rather through {@link Handler} objects associated with the Looper.

翻译下:

MessageQueue持有等待 Looper派发的 Message列表,Message无法被直接添加到 MessageQueue,需要通过与 Looper关联的 Handler

通过介绍我们知道了一些答案,即 Message需要通过 Handler才能被添加到 MessageQueue中。那么我们找一下 MessageQueue中接收 Message的方法。

boolean enqueueMessage(Message msg, long when) {   
        //如果该消息没有target,抛出异常    
        if (msg.target == null) {        
                throw new IllegalArgumentException("Message must have a target.");    
        }    
        //如果该消息正在处理中,抛出异常    
        if (msg.isInUse()) {        
                throw new IllegalStateException(msg + " This message is already in use.");    
        }    
       
        synchronized (this) {        
                 //标记该消息正在处理中        
                msg.markInUse();        
                //消息的处理时间        
                msg.when = when;        
                Message p = mMessages;        
                //如果当前MessageQueue里没有Message,或者新加入的Message没有设置处理时间,        
                // 或者处理时间小于上一个Message的处理时间,那么就把消息放到表头        
                if (p == null || when == 0 || when < p.when{        
                        msg.next = p;            
                        mMessages = msg;        
                } else {            
                        //如果当前MessageQueue中已有Message,且新加入的Message设置了处理时间,
                        //且处理时间大于上一个Message的处理时间,那么按照时间顺序把Message插入Message链表中,
                        //处理时间最早的Message放在表头,处理时间最晚的Message放在表尾            
                        Message prev;            
                        for (;;) {                
                                prev = p;                
                                p = p.next;                
                                if (p == null || when < p.when) {                    
                                        break;               
                                }            
                        }            
                        msg.next = p;             
                        prev.next = msg;        
                }    
        }    
        return true;
}

当然,上面的代码省略了很多,主要是事件队列唤醒机制相关的代码,这里不是我们关注的重点,就不详述了。

通过上面的代码我们可以知道,Message必须要有 target才能被加入到 MessageQueue之中,并且,同一个 Message不能被多次添加到MessageQueueMessage有一个参数 next,它也是一个 Message,可以证明 Message是以链表结构来存储的。并且, Message的顺序按照处理时间来排列,处理时间最晚的 Message排在链表的尾部。

到目前为止,我们仍不知道 target代表什么意思,那么我们需要来了解一下 Message 类。



Message

先看文档介绍:

Defines a message containing a description and arbitrary data object that can be sent to a {@link Handler}. This object contains two extra int fields and an extra object field that allow you to not do allocations in many cases. While the constructor of Message is public, the best way to get one of these is to call {@link #obtain Message.obtain()} or one of the {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull them from a pool of recycled objects.

翻译下:

定义一条可以被发送到 Handler,包含描述和任意数据对象的 MessageMessage包含两个额外的 Int字段和一个额外的 Object字段,以适应多种情况需求。虽然其构造方法是 public的,但构造 Message最佳的途径是通过 Message.obtain()方法或者 Handler.obtainMessage()系列方法,它们将从回收对象池中获取Message

简单来说 Message主要是一个数据对象类,看一下 Message中我们关心的几个字段:

public long when;
Handler target;
Message next;

when是该消息需要被派发的时间,target是一个 Handlernext是下一个 Message的引用,表明 Message是以链表方式存储的。

这里的 target居然就是一个 Handler。通过 MessageQueue的介绍文档我们知道:Message无法被直接添加到 MessageQueue,需要通过与 Looper相关连的 Handler来添加。那么我们来看一下 Handler是如何添加 MessageMessageQueue中的,自然就能知道 taget中的 Handler是从何而来。



Handler发送消息

通过前面的 Handler介绍,我们知道 Handler可以发送 MessageRunnable。那么我们去源码中找下它发送消息的方法:

public final boolean sendMessage(@NonNull Message msg) { 
        return sendMessageDelayed(msg, 0);
}

public final boolean post(@NonNull Runnable r) {   
        return  sendMessageDelayed(getPostMessage(r), 0);
}

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {   
        if (delayMillis < 0) {        
                delayMillis = 0;    
        }    
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {    
        MessageQueue queue = mQueue;    
        if (queue == null) {        
                RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");
                return false;    
        }    
        return enqueueMessage(queue, msg, uptimeMillis);
}

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) {  
        //target就是当前的Handler
        msg.target = this;    
        ··· ···
        return queue.enqueueMessage(msg, uptimeMillis);
}

发送 Message的主要方法为 sendMessage(Message msg),发送 Runnable的主要方法为 post(Runnable r),但最终都进入到了 enqueueMessage 方法,并调用了 MessageQueue.enqueueMessage()方法。

这里面值得注意的地方有两个:1、Runnable最终被转换成了一个 Message;2、Messagetarget即是当前发送消息的 Handler

所以我们有了答案,Looperloop()方法里处理消息的 Handler就是把消息加入到 MessageQueueHandler

//派发Message
public static void loop() {  
        ··· ···
         msg.target.dispatchMessage(msg);  
        ··· ···
}

所以 Handler的文档介绍说:Handler允许你发送和处理与线程的 MessageQueue相关的 Message/Runnable对象

发送消息的是它,最终处理消息的也是它。只不过,Handler与创建它所在的线程相关联,最终处理消息所在的线程即它所关联的线程,但它可以被其它线程用来发送消息,所以可以实现消息的线程切换处理。



Handler处理消息

前面介绍 HandlerMessage的过程中其实忽略了处理消息相关的一些内容,这里作一个补充。

首先Handler有一个接收消息的方法:

/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(@NonNull Message msg) { }

继承了 Handler类的子类需要实现该方法来接收消息。

另外Handler在初始化的时候可以传入一个 Callback对象,该对象也是用于接收和处理消息的,如下:

/** Callback interface you can use when instantiating a 
*Handler to avoid having to implement your own subclass of Handler. 
*/
public interface Callback {    
        //return True if no further handling is desired  
        boolean handleMessage(@NonNull Message msg);
}

接口说明已经很清楚了,使用 Callback接口可以避免必须实现自己的 Handler子类。

最后Message对象中有一个 Runnable字段:

Runnable callback;

它是在 Runnable 转换成 Message的时候被包装进去的,转换方法如下:

private static Message getPostMessage(Runnable r) {    
        Message m = Message.obtain();    
        m.callback = r;    
        return m;
}

现在,我们来看一下 Handler的消息处理方法 dispatchMessage(@NonNull Message msg)的详细过程:

public void dispatchMessage(@NonNull Message msg) {
        //如果Message的callback即Runnable不为空,就调用Runnable的run()方法来处理
        if (msg.callback != null) {        
                handleCallback(msg);    
        } else {
                //如果初始化Handler时传入了Callback,则调用Callback.handleMessage方法来处理
                if (mCallback != null) {           
                        if (mCallback.handleMessage(msg)) {                
                                return;            
                        }        
                }
                //调用Handler自身的handleMessage方法来处理
                handleMessage(msg);    
        }
}

private static void handleCallback(Message message) {    
        message.callback.run();
}


总结

  1. Handler 在创建的时候就需要一个 Looper,如果创建时没有手动提供 Looper,那么 Handler会去拿当前线程的 Looper,如果当前线程没有 Looper,那么创建的时候就会抛出异常。
  2. Looper可以使用 Looper.prepare()方法来创建,Looper创建的同时会初始化并持有一个 MessageQueue,并且与当前创建它的线程关联。一个线程里最多只能有一个 Looper
  3. MessageQueue 主要用来存取 Message,并且按照待派发时间先后顺序给 Message排好序。
  4. 只有 Handler可以往 MessageQueue中添加 Message
  5. Looper在调用 Looper.loop()方法之后,会开启一个无限轮询去 MessageQueue中提取需要派发的 Message,提取到之后会调用 HandlerdispatchMessage方法来处理该 Message
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值