上一篇Handler消息机制(一)为大家讲述了Handler是如何创建的这篇Handler消息机制(二)将为大家介绍Handler是如何发送消息的。
一般使用的Handler方法
Handler.post(Runnable)
Handler.sendEmptyMessage()
Handler.sendMessage()
Handler.sendMessageDelayed()
这四种方法是大家在App中经常使用的那么我们就首先看一下后面三种sendMessage()的方法
第一,Hander.sendEmptyMessage()
看一下源码
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
那么sendEmptyMessageDelayed(what, 0)
的源码是什么呢?接着往下看
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
那么调用的是sendMessageDelayed(msg, delayMillis)
这个方法,OK,第二种常调用的方法和第四种其实是一样的只不过进行了一个封装。
那么我们再来第三种调用的方法的背后
Handler.sendMessage()方法的源码
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
OK,大家其实已经明白后面三种发送消息的方法最后调用的都是sendMessageDelayed(msg, 0).这个方法。那让我们来看看这个方法到底是何方神圣,请看一下这个方法的源码。
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
最后调用的是sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
这个方法。这个方法有两个参数,一个是需要发送的消息。另一个是发送消息的时间,这个时间是系统当前的时间加上设置的延迟时间。明白了,那么我们来看看sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)的源码吧。
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);
}
在这个方法中首先涉及了一个类MessageQueue这个类是做什么的,看类的名字就知道这是一个消息队列。大家这个时候发现了一个对象mQueue,这个时候很多人就会好奇了这个mQueue对象是做什么的?
从哪里来,这个时候我们就要回到Handler消息机制(一)里面实例化对象的时候实例化每个Handler必须要有Loop,那这个Loop到底起到了什么作用?
我们重新看一下Handler()实例化对象时的构造方法
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
大家这个时候有没有发现 mQueue = mLooper.mQueue,这个时候眼前一亮,mQueue这个参数是用Loop获得的,那么我们就来看看mLooper.mQueue到底是个什么鬼?
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
大家这个时候突然发现mQueue是在创建Looper对象时就会创建一个与Looper相关联的消息队列,那问题就来了Looper对象又是在什么时候创建的。这个时候我们不得不联系到上一篇博客Hander消息机制(一),在Hander实例化对象时,让我们来回顾一下当时的源代码,实例化对象的时候如果不是在主线程中必须使用Loop.prepare()
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));
}
一切清楚了,在Loop.prepare()这个方法的时候就实例化了一个Looper对象,然后在实例化Looper对象的时候又实例化了一个与这个Looper相关联的MessageQueue(消息队列),来理一下这个逻辑,意思就是每一个Looper都有一个与之相关的消息队列,而Looper又是与什么相关呢。在前面的ThreadLocal中介绍的就明白了,每一个线程对应一个与之相关的Looper。我们已经弄清楚了这个mQueue,现在重新回到发送消息时的方法
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);
}
这个方法最终调用的是enqueueMessage(queue, msg, uptimeMillis)
这个方法那我们来看看这个方法的源代码是什么?
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
这个方法里唯一要注意的就是 msg.target = this,这个表明其实msg.target其实就是当前Handler的实例化对象,当然这里也许用不到,到下面怎么接收这个消息将用到,大家发现最后调用的是queue.enqueueMessage(msg, uptimeMillis)这个方法,来看看这个方法的源代码
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.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
这里大家需要理解清楚一个逻辑,就是消息进入消息队列是怎样进入的,大家可以仔细注意一下这段代码, 大家从20行代码往下看。这边的逻辑首先是msg.when = when;Message p = mMessages这两句
,when自然就是传进来的参数,那么mMessages是啥,大家其实这就是个消息的全局变量,连实例化这个对象也没,那么如果是第一次必然是p == null ,走入 if (p == null || when == 0 || when < p.when)代码中,那这个时候会做什么动作,msg.next = p;mMessages = msg;会把下一个消息变成p,mMessages可以理解为当前传进来的消息。这个时候如果再进来一个消息,是进行怎样的判断 if (p == null || when == 0 || when < p.when)这个里面还有两个参数,第一是when ==0, 第二是when< p.when ,when其实就是消息当前消息的when,但是这个时候的p已经变成上一次传进来的消息,Message p = mMessages, 其实这个判断就是说如果穿进来的消息的when为0,或者when小于新进来的消息的when.就把这个上一个消息放到下一个消息。OK,理一下这个逻辑,如果msg.when越小就越在消息队列的前面,when就是传进来的系统时间和设置的延迟时间之和。
OK,我们开始讲述最后一个方法Handler.post(Runnable),这里我们首先要将一个预备知识点。很多安卓程序有这个误区,就是实现一个Runnable接口到底是不是一个线程,那我们来做一个试验,请看试验代码。
public class Test {
public static void main(String[] args) {
Test03 test03 = new Test03();
test03.run();
Thread thread = Thread.currentThread();
System.out.println("TestName =" + thread.getId() );
}
}
public class Test03 implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
Thread thread = Thread.currentThread();
System.out.println("Test03Id =" +thread.getId());
}
}
打印结果:
Test03Id =1
TestName =1
这个打印结果的线程Id是一样的,那么这就证明一个问题,一个类实现一个Runable接口并不是说这个类变成单独的一个线程,实际上只是实现了一个接口。那么接下来接着来看Handler.post(Runnable)这个的源码
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
这个最终调用的是sendMessageDelayed()方法,然后sendMessageDelayed()方法最终又是调用的sendMessageAtTime(),这是说通过
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
这段代码其实就是告诉我们把Runnable接口变成一个消息塞了进去,然后给这个消息一个callback。OK,所有发消息的基本已经讲述完毕。至于这三种方法怎么回调,请看下一篇博客Handler消息机制源码解析(三)。