Handler消息机制源码解析(二)

上一篇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消息机制源码解析(三)。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值