阅读 118

从源分析Handler、MessageQueue、Looper

前言

很长的一段时间我一直在使用Handler,主要是在处理异步任务的时候来实现线程切换,不过对其原理和工作流程并没有详细的做过了解,以下我把从分析源码得到一些内容做出了一些总结。

从源分析Handler/MessageQueue/Looper的工作流程

首先来看下如下的示意图,图中描述的对象之间的基本通信。

首先是Handler对象发送了一条Message,然后消息会被存放到一个列表(队列:MessageQueue)中,紧接着有一个叫做Looper的对象会不停的去这个队列中寻找有没有新的消息,有的话将消息分配给Handler对象进行处理(每一个Message对象都会默认持有一个Handler对象的引用,这个Handler对象就是发送这个Message的对象,在Message对象内部被定义为target变量)。其具体的消息会被放在target所在的线程中执行。接下来详细介绍消息的收发和处理过程。

https://imgservice.lost520.cn/InternetImgService/2019-07-24/20190724222357343.pn

Handler的创建过程

首先先来看一下Handler的构造函数,如下图,Handler一共向外提供了4个构造函数(其实在内部一共提供了是7个构造函数,只不过对外是隐藏的)。

Handler的构造函数

//Step1
public Handler() {
    this(null, false);
}
//Step2
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()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
复制代码

可以看到的是我们在调用无参数构造方法时其实的调用的内部的具有两个参数的构造方法,第一个要求传入一个回调实现(后便会具体介绍),第二个则是否异步的标识符。

Step2中可以看到几个比较关键的内容,第一个关键点就是在我们新建的Handler对象内部保存了一个Looper对象的引用,这个Looper.myLooper()函数获取的是当前线程所持有的Looper对象(线程中默认是没有Looper对象的,只有调用Looper.propare()函数之后才会在当前线程中创建一个唯一的Looper对象,所以如果没有则会抛出一个异常,这个异常就是我们最初在子线程中使用Handler提示的异常信息。);第二个关键点则是从Looper对象中拿到了一个消息队列对象mQueue,这个对象是一个MessageQueue,它是在Looper被创建时创建的。

Looper/MessageQueue的创建过程/时机

MessageQueue是跟随Looper的创建而创建的,在一个线程中只会存在一个Looper对象,也就是说在一个线程中MessageQueue也只会存在一个(理论上来说)。下面从源码中来印证如上所说。

1、来看Looper.prepare()函数

这个方法仅仅是提供了一个入口方法,实际上调用的是内部的另一个prepare方法。紧接着内部的这个prepare方法通过new的方式创建了一个Looper对象,也就是在Step3的内容。可以清楚的看到这里为Looper的内部变量mQueue进行了赋值,也就是在这个时候MessageQueue被创建。

在Step2的时候我们发现调用了一个叫做sThreadLocal的变量的set函数,这个ThreadLocal并非是一个线程,它是用来存储线程中数据的,具体可参考我的另一篇文章:ThreadLocal是什么?

//Step1
public static void prepare() {
    prepare(true);
}
//Step2
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));
}
//Step3
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
复制代码

在Looper对象中有一个非常重要的函数,那就是loop了,中文翻译过来就是循环的意思,这个函数会帮助我们不停的从MessageQueue中来获取Message

2、来看MessageQueue

目前来说,MessageQueue的创建并没有什么值得我们关注的,它只是提供了一个先进先出的机制来帮助我们存取消息,但是我们需要知道它所提供的两个非常重要的方法,第一个就是enqueueMessage,这个函数是用于将Message对象添加到队列中的,第二个就是next函数,该函数是从消息队列中取消息的,取出来后会立刻从队列中移除。

Handler的消息发送过程

如下是一个基本的消息发送流程,基本使用这里不在赘述。

Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        //do something...
    }
};
Message message = Message.obtain();
handler.sendMessage(message);
复制代码

1、走进handler.sendMessage(Message msg)函数中来一探究竟。

//Step1
public final boolean sendMessage(Message msg)
{
    return sendMessageDelayed(msg, 0);
}
//Step2
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
//Step3
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}
复制代码

如上可以看到的是在我们调用handler.sendMessage(Message msg)函数时,它会调用内部的sendMessageDelayed函数,这个函数是用于发送定时消息的,因为sendMessage发送的都是需要立即被处理的消息,所以传入的就是0了,紧接着sendMessageDelayed函数又调用了sendMessageAtTime函数。

在这个sendMessageAtTime函数中我们需要关注的是enqueueMessage的调用,这个enqueueMessage函数是帮助我们把消息加入到MessageQueue对象中。在如上Hanlder创建过程的描述中,我们说了:这个消息队列(mQueue对象)是在Handler创建时从Looper对象中获取并保存到局部变量中的。

2、来看Handler中的enqueueMessage函数

 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}
复制代码

这里有两点我们需要特别关注,第一个就是把当前Handler对象的引用给了msgtarget变量,这其实就是为之后的消息处理提供处理者,在加入到消息队列之前会默认把Messagetarget设置为发送MessageHandler对象,即便我们在创建Message对象时设置了target也会在enqueueMessage函数中被重置,由此可以得出,Message对象的发送者即是Message的处理者。

到这一步消息已经被添加到了MessageQueue对象中,至此HandlersendMessage任务就算完成了,也就是说它成功的将消息递交给了MessageQueue对象。

Message的处理过程

在上一段的结尾我们知道了HandlersendMessage函数会把我们的Message对象加入到一个叫做MessageQueue的对象中,也就是说我们只是把消息保存了起来,单纯的消息保存没有任何意义,所以引入了Looper对象来不停的从MessageQueue中拿数据并且交给消息的target对象来进行处理。

1、来看Looperloop函数

public static void loop() {
    final Looper me = myLooper();//核心1
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;//核心2

    //.......此处省略无关代码

    boolean slowDeliveryDetected = false;

    for (;;) {
        //核心3
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        //.......此处省略无关代码
        try {
            msg.target.dispatchMessage(msg);//核心4(重点!!!!!!)
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        //.......此处省略无关代码
    }
}
//核心1调用的myLooper函数
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
复制代码

以上就是Looperloop函数了,为了便于观看,这里我删减掉了无关的代码,并标注了4条较为重要的代码。

第一步:调用Looper对象内部的myLooper()函数,这个函数是从ThreadLocal对象中取出当前所在线程的Looper对象,它是我们在创建Looper时保存的Looper对象,也就是我们在上边介绍Looper创建时看到的sThreadLocal.set(new Looper(quitAllowed));

第二步:拿到我们当前线程中持有的MessageQueue对象,在上边我们说了MessageQueue是随着Looper的创建而被创建的。也就是说我们拿到的LooperMessageQueue都是当前线程中的。至此你应该要知道LooperMessageQueue在每一个线程中都是可以存在的,但是更要知道的是:在每一个线程中有且只有一个LooperMessageQueue对象。如下我绘制了一张图帮助更好记忆和理解。

https://user-gold-cdn.xitu.io/2019/7/25/16c2902e2ad7575b?w=966&h=663&f=png&s=22741

第三步:从MessageQueue中取出我们使用Handler.sendMessage存放进取的消息。

第四部:这一步其实是最核心的一部了,它通过调用Message对象中的target变量的dispatchMessage函数,将消息交给target对象进行处理,在上边我们说了target对象就是发送Message对象的Handler。所以最终的消息处理会被放在该对象中被处理。

2、来看HandlerdispatchMessage函数

如下就是我们在上一步看到的loop函数中调用的dispatchMessage函数。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}
 /**
 * Subclasses must implement this to receive messages.
 * 译:子类必须实现这个函数来接收消息
 */
public void handleMessage(Message msg) {
}
复制代码

首先它会判断Messagecallback变量是否为NULL(在Message.obtain()函数中可以传入callback),如果存在callback那么会优先处理Messagecallback,否则会继续判断当前Handler对象的callback是否为NULL(这个callback是在构造Handler对象时是可选传入的),如果还不行那么就调用Handler中的handleMessage函数,这也是我们常见的消息处理方式,也就是我们在上边重写的handleMessage函数。

这里我们需要知道的就是消息处理的优先级:

1、由Message对象的callback处理(这个callback是一个Runnable对象)

2、由Handler对象的mCallback处理(这个callbackHandler对象中的一个接口提供了一个用于消息处理的回调public boolean handleMessage(Message msg);

3、由HandlerhandleMessage处理(这个就是Handler对象中的一个方法了,只有默认实现没有任何代码,通常需要重写)

另外需要知道的是:dispatchMessage函数中所有的消息都是在Handler对象所处的线程中被执行的。

消息的发送和处理总结

1、调用Looper.prepare函数

帮助我们创建Looper对象和MessageQueue对象。

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对象并保存当ThreadLocal中
}
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
复制代码

2、创建Handler对象

在这一步主要是拿到当前线程的Looper对象以及Looper对象中的MessageQueue对象并保存其引用。

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()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
复制代码

3、调用HandlersendMessage函数

该函数最终会走到Handler对象中的enqueueMessage中,将消息保存到当前线程的MessageQueue对象中。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}
复制代码

3、Looper对象的loop函数

当前线程的Looper对象不断的从它内部的MessageQueue对象中取消息,然后交给Messagetarget来做处理。

public static void loop() {
    final Looper me = myLooper();//核心1
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;//核心2

    //.......此处省略无关代码

    boolean slowDeliveryDetected = false;

    for (;;) {
        //核心3
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        //.......此处省略无关代码
        try {
            msg.target.dispatchMessage(msg);//核心4(重点!!!!!!)
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        //.......此处省略无关代码
    }
}
//核心1调用的myLooper函数
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
复制代码

4、Handler处理消息

到这里会根据优先级来处理消息,且消息的执行是在当前Handler所在的线程中。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}
复制代码

至此核心的处理流程及已经完成了。

主线程中不用手动创建Looper的原因

Android主线程即ActivityThread,在主线程的入口方法main方法中调用了Looper的prepareMainLooper函数,该函数是专门为主线程提供创建Looper使用的。

public static void main(String[] args) {
    //......省略无用代码
    Looper.prepareMainLooper();

    // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
    // It will be in the format "seq=114"
    long startSeq = 0;
    if (args != null) {
        for (int i = args.length - 1; i >= 0; --i) {
            if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                startSeq = Long.parseLong(
                        args[i].substring(PROC_START_SEQ_IDENT.length()));
            }
        }
    }
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }

    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}
复制代码

总结

持有关系:

1、一个线程中只有一个Looper对象和一个MessageQueue 2、一个线程中可以有多个Handler对象 3、MessageQueue是包含在Looper中的

注意点:

1、Handler必须在持有Looper的线程中才能创建。 2、Handler的回调优先级(1、Message.callback2、Handler.callback、3、Handler.handleMessage)。 3、在使用Handler发送Message时,Message的target会被默认设置为Message的发送者。

最后

HandlerMessageMessageQueueLooper组成了Android强大的消息机制,以上只是简述了其中的部分内容,还有很多的知识点等待日后进行挖掘。

原创文章,转载请标明来源。

关注下面的标签,发现更多相似文章
评论