【Handler系列三】 Handler全解析

694 阅读6分钟

前言

前面我们分析了Android消息机制中的消息Meesage和存储消息的队列MessageQueue,相信大家对这两个的实现已经很了解了吧!!!今天,咱们终于迎来了整个消息机制中的主角Handler,他的主要作用就是用来发送消息和处理消息的。

平时我们都是继承Handler,然后重写handlerMessage()方法来处理消息,在需要发送消息的时候调用sendMessage()或者postMessage()来进行发送消息,然后内部什么的基本不用考虑,但是作为一个有责任有担当的高级技术开发者,怎么能忽略内部实现逻辑呢?

上篇讲述消息队列的时候,遗留两个问题没有进行讲解,今天就跟着笔者一起来 fuck the source code

  1. msg.target又是什么时候赋值的呢?
  2. 为什么msg.target == null的时候就是异步消息呢?

先从构造方法说起

image-20210611104126461.png

从内部方法中可以看出,分为两种情况,参数带Looper和不带Looper,咱们就单独看下带Looper和不带Looper的构造方法分别做了什么

  • 带Looper的构造方法

    最终都调用了三个参数的构造方法,可以看出只是给成员变量进行赋值

    public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    
  • 不带Looper的构造方法

    public Handler(@Nullable Callback callback, boolean async) {
        // 先拿到Looper对象,进行赋值
        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;
        mCallback = callback;
        mAsynchronous = async;
    }
    

    从这里可以看出,要想创建Handler,必须得有Looper对象,否则直接抛出异常,这也是为什么我们在子线程创建Handler对象的时候,先获取Looper对象的原因;但是为什么平时我们在主线程创建Handler的时候不需要调用Looper.prepare()方法来获取Looper对象呢???那是因为在程序ActivityThread的main函数中,已经默认帮我们创建了Looper对象了(详情可以去看看ActivityThread的源码)。

    构造方法我们就讲这么多,实际上也就这么些内容,关于Looper对象的获取,我们会在下篇文章里详细介绍。

    接下来我们就来看看Handler关于获取消息和发送消息的相关操作

获取消息

在发送消息之前,我们首先得有Message对象,而Handler对象获取消息的操作,是通过obtainMessage()方法来获取,看到这里是不是很熟悉呢。没错,在我们之前讲解的Message里也有这个方法,那么Handler类里的这个方法做了什么操作呢,来咱们一起看下

@NonNull
public final Message obtainMessage(int what, int arg1, int arg2)
{
    return Message.obtain(this, what, arg1, arg2);
}

咦,这不是直接调用了Message.obtainMessage()方法吗。没错,确实是这样的。详见【Handler系列一】Message对象的获取机制

既然消息已经获取到了,那么接下来就开始发送消息了。

发送消息

开发中我们经常用到的发送消息就是send...或者post...等系列方法来发送消息,通过源码查看,最终都调用的是enqueueMessage()方法

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
        long uptimeMillis) {
    // 将自身赋值给msg.target
    msg.target = this;
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

这源码还是比较清晰的,首先将自身赋值给msg.target,其次调用MessageQueue的enqueueMessage()方法来将消息插入到消息队列中。关于消息队列如何处理消息,详见【Handler系列二】 MessageQueue

这里就解开了我们开头所说的问题1,msg.target就是在发送消息的将自身handler赋值给该消息的target变量的

发送消息还是比较简单的,那既然发送完消息了,是不是就到处理消息了呢?

处理消息

handler的处理消息是通过dispatchMessage()这个方法来实现的,那我们就来扒一扒到底是怎么实现的

/**
 * Handle system messages here.
 * 该方法注释就说明了 这里是处理系统消息的方法
 */
public void dispatchMessage(@NonNull Message msg) {
		// 情况一:如果msg的callback变量不为null,则直接调用handlerCallback()方法
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
    		// 情况二:构造方法里进行赋值的Callback
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        
        // 情况三:也是我们经常用到的
        handleMessage(msg);
    }
}

从源码可以看出三种情况,不同的处理消息方式,接下来笔者带着大家从这三种情况分别来分析分析

情况一:msg的callback变量不为null

private static void handleCallback(Message message) {
    message.callback.run();
}
  1. Message的callback是通过Message.obtain()方法里赋值或者直接通过Message里setCallback里进行赋值的,然后直接调用进行处理。
  2. 这种处理消息的方式正常开发中基本没用到过

情况二:mCallback对象不为null

public interface Callback {
    /**
     * @param msg A {@link android.os.Message Message} object
     * @return True if no further handling is desired
     */
    boolean handleMessage(@NonNull Message msg);
}
  1. mCallback对象也是在handler的构造方法里传进来的,如果初始化的时候传入的话,直接通过这个接口的重写方法handleMessage进行处理消息
  2. 日常开发中用的也少

情况三:msg.target和mCallback都为null

/**
 * Subclasses must implement this to receive messages.
 */
public void handleMessage(@NonNull Message msg) {
}
  1. 这里的handleMessage为空方法
  2. 这就是我们日常开发中经常在创建Handler对象的时候进行覆写的方法,经常用到

移除消息

Handler中所有的removeMessage()和removeCallback()最终都是调用mQueue.removeMessage或者mQueue.removeCallback()来实现的,这里在上篇文章已经详细介绍了,详见【Handler系列二】 MessageQueue

msg.target什么情况下为null呢?

//MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
    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.");    
    }
}

在MessageQueue插入消息的时候,首先判断msg.target是否为null,为null直接抛出异常;那为什么msg.target还有可能为null呢???这里就继续分析,msg.target什么时候没有被赋值呢?

//开启同步屏障
private int postSyncBarrier(long when) {  
    // Enqueue a new sync barrier token.    
    // We don't need to wake the queue because the purpose of a barrier is to stall it.    
    synchronized (this) {        
        final int token = mNextBarrierToken++;               
        //新创建一个Message对象        
        final Message msg = Message.obtain();       
        msg.markInUse();        
        msg.when = when;       
        msg.arg1 = token;       
        Message prev = null;      
        Message p = mMessages;      
        if (when != 0) {           
            while (p != null && p.when <= when) {              
                prev = p;               
                p = p.next;           
            } 
        }       
        if (prev != null) {
            // invariant:
            p == prev.next           
            msg.next = p;           
            prev.next = msg;      
        } else {           
            msg.next = p;         
            mMessages = msg;       
        }        
        return token;   
    }
}

这里可以看出,在开启同步屏障的时候,创建了一个Message对象,这里没有给msg.target进行赋值,所以这时候msg.target为null,然后将该消息也插入到消息队列里,所有消息队列里就会有一个msg.target为null的消息

总结

  1. Handler主要工作就是发送消息、处理消息和移除消息的一个过程
  2. msg.target是在sendMessage发送消息的时候将当前handler进行赋值
  3. 在开启同步屏障的时候创建的Message对象里的msg.target对象为null
  4. 注意开启同步屏障后,记得在适当时机关闭屏障