乐趣区

从源分析HandlerMessageQueueLooper

前言

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

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

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

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

Handler 的创建过程

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

//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对象。如下我绘制了一张图帮助更好记忆和理解。

第三步:从 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 强大的消息机制,以上只是简述了其中的部分内容,还有很多的知识点等待日后进行挖掘。

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

退出移动版