Handler消息机制全解(三)消息的发送

    Android系统提供了 一 个Handler类,用来向 一 个线程的消息队列发送一 个消息并处理消息,Handler类内部有mLooper和mQueue两个成员变量,它们分别指向 一 个Looper对象和 一 个MessageQueue对象。创建的Handler中mLooper和mQueue属于哪个线程,那么,其发送的消息和处理消息也就属于哪个线程的。所以我们可以在一个Thread中同时创建多个Handler,但是Looper实例可以是其他线程的,默认情况下,我们在哪个线程创建Handler,其所关联的Looper对象也就是哪个线程的。比如, 应用程序开发中经常使用如下方式来创建Handler,以及发送一个线程消息。

//创建Handler
Handler mHandler = new Handler() {
    public void handleMessage(Message msg) {
        //处理消息事件
    }
}
//创建一个消息
Message msg=Message.obtain();
msg.object="携带的数据";
//发送消息
mHandler.sendMessage(msg)

下面我们先来看看Handler的构造函数,看看他是如何与Looper对象和 一 个MessageQueue对象关联起来的。

public class Handler {
    ......
        
    public Handler() {
        this(null, false);
    }
    
    ......
    
    public Handler(Callback callback, boolean async) {
        ......

        mLooper = Looper.myLooper();
        ......
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

    ......

    final Looper mLooper;
    final MessageQueue mQueue;
    final Callback mCallback;
    final boolean mAsynchronous;
    IMessenger mMessenger;

    ......
}
    我们 一 般使用 Handler 类的默认构造函数来创建一个 Handler 对象, 这时候它的成员变量 mLooper 和mQueue 就会分别指向与当前线程所关联的 一 个 Looper 对象和 一 个 MessageQueue 对象。另外还可以知道默认的Handler对象发送的消息都是同步的,不会有延迟。此外,我们也可以自己调用带Looper参数的构造函数,来自己制定Handler所关联哪个线程的Looper对象,这样我们就可以向哪个线程发送消息了,比如,我们可以在子线程中构造一个主线程的Handler对象,这样就可以向主线程发送消息。
    当Handler创建完毕,就可以发送消息了,下面我们来我们来看看,消息是如何创建的,这里我们可以直接使用Message类来创建,也可以通过Handler类中的方法帮助创建。
public class Handler {
   
    ......
    
    public final Message obtainMessage()
    {
        return Message.obtain(this);
    }

    public final Message obtainMessage(int what)
    {
        return Message.obtain(this, what);
    }
    
    public final Message obtainMessage(int what, Object obj)
    {
        return Message.obtain(this, what, obj);
    }

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

    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

    private static Message getPostMessage(Runnable r, Object token) {
        Message m = Message.obtain();
        m.obj = token;
        m.callback = r;
        return m;
    }
    ......
}
    可以看到,其内部实质是调用Message 对象的对应obtain方法,并会自动关联当前的handler对象为作为target,使用Handler创建消息的好处是省去了我们手动设置 target参数,因为使用Handler发送消息后,该消息的处理者一般也是这个Handler本身。下面看看Message类的主要实现。
public final class Message implements Parcelable {
   
    ......
    /*package*/ Message next;

    private static Message sPool;
    private static int sPoolSize = 0;

    private static final int MAX_POOL_SIZE = 50;
    ......
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

    ......
        
    public static Message obtain(Handler h, int what,
            int arg1, int arg2, Object obj) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.arg1 = arg1;
        m.arg2 = arg2;
        m.obj = obj;

        return m;
    }

    ......

    /** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
    */
    public Message() {
    }

    ......
}
    Message类是一个链表数据结构,它的obtain静态方法有多种重载,区别就是设置的参数不同,这些参数都是可以单独设置的,所以可以根据需求来使用不同参数的方法。尽管Message的构造器是公开的,但是获取Message对象的最好方法是调用Message的一系列obtain静态方法、或者Handler的一系列obtainMessage实例方法。 因为一个Message被处理完后,消息循环会调用这个消息的recycleUnchecked方法来回收这个对象,也就是将这个对象放在全局sPool对象池中,最多可以回收50个Message对象,这样下次可以直接拿来使用,不用再重新new对象。
    Handler有了, Message也创建好了,怎样使用Handler来发送这个消息呢?其实,使用Handler发送消息有两种方式:post方式和sendMessage方式。其中post方式其内部也是通过sendMessage方式,只是它给Message设置了一个callback对象,这个callback就是runnable对象。下面是一些发送消息的重载方法
public class Handler { 
    ......
    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    
    public final boolean postAtTime(Runnable r, long uptimeMillis)
    {
        return sendMessageAtTime(getPostMessage(r), uptimeMillis);
    }
    
    public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
    {
        return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
    }
    
    public final boolean postDelayed(Runnable r, long delayMillis)
    {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }
    
    public final boolean postAtFrontOfQueue(Runnable r)
    {
        return sendMessageAtFrontOfQueue(getPostMessage(r));
    }

    ......

    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

    public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }

    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }

  
    public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
    }

    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return 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);
    }

    public final boolean sendMessageAtFrontOfQueue(Message msg) {
        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, 0);
    }

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

    ......
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
    ......
}
    以上Handler中提供了很多个发送消息的方法,其中除了sendMessageAtFrontOfQueue()方法之外,其它的发送消息方法最终都会调用到sendMessageAtTime()方法中,在该方法中又会调用enqueueMessage()方法,该方法最终调用的是MessageQueue的enqueueMessage方法将消息入队,等待执行。下面来看看 MessageQueue是如何将这个消息入队的。
public final class MessageQueue {
    
    ......
    
    boolean enqueueMessage(Message msg, long when) {
        (1)消息的有效性检查
        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) {
            ......

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            (2)消息被插入队列头部
            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 {
                (3)消息被插入队列中间
                // 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;
            }

            (4)判断当前消息是否要唤醒线程
            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
    ......
}
     (1)消息的有效性检查:从上面可以看出,Message的target参数必须要设置,也就是处理这个消息的Handler对象必须要设置,否则这个消息将不能入队,消息发送失败。
     (2)消息被插入队列头部: 由于 一 个消息队列中的消息是按照它们的处理时间 when 从小到大的顺序来排列的, 因此, 当我们将 一个消息发送到 一 个消息队列时, 需要先根据这个消息的处理时间找到它在目标消息队列的合适位 置, 然后再将它插人到目标消息队列中。
     由于保存在目标消息队列头部的消息发生了变化, 因此, 当前线程就 需要将目标线程唤醒, 以便它可以对保存在目标消息队列头部的新消息进行处理。 但是, 如果这时 候目标线程不是正处于睡眠等待状态, 那么当前线程就不需要对它执行唤醒操作。 当前正在处理的 MessageQueue对象的成员变量mBlocked记录了目标线程是否正处于睡眠等待状态。 如果它的值等于
true, 那么就表示目标线程正处于睡眠等待状态, 这时候当前线程就需要将它唤醒。 第26行代码将这 个成员变量的值保存在变量needWake中, 以便当前线程接下来可以决定是否需要将目标线程唤醒。
     (3)消息被插入队列中间:  由于当前消息是插入在队列中间,那么保存在目标消息队列头部的消息没有发生变化, 因此, 当前线程 都不需要对目标线程执行唤醒操作, 这时候就会将变量 needWake 的值设置为 false 。但是此时,在一种特殊情况下也需要唤醒线程,那就是当前发送的消息是异步消息,而队列头消息的target为null,也就是说当前线程正在睡眠等待处理的这个消息无效了,此时,该消息前面的消息除头消息之外的所有消息都是同步的,那么就应该唤醒线程来处理后面的消息。
     (4)判断当前消息是否要唤醒线程将要发送的消息插入到目标消息队列中之后, 第50行的语句就检查变量needWake的值是否等于 true。 如果等于, 那么接下来第51行就会调用MessageQueue类的成员函数nativeWake将目标线程唤醒,nativeWake方法是一个Native方法。








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值