Android最简单易懂的消息机制(Message MessageQueue Handler Looper)(1)

我笃定,只要大家耐着性子看完,那么就能将Android消息机制玩弄于鼓掌之间了。吼吼~


首先用一段很直白的话简单总结下我所理解的Android 消息机制:

在每一个线程中都会有一个唯一的Looper(子线程中需要自己手动去创建),此Looper会创建一个唯一的MessageQueue,然后该Looper会无限循环不断从该MessageQueue中读取消息Message(若消息队列为空时,线程则会阻塞等待),这些Message则都是由Handler发送到MessageQueue当中的,且持有发送它的Handler的引用,所以Message被取出后,就交由了发送它的Handler来处理,而Handler则在一个线程中可以创建很多个,在Handler的构造方法中,会将当前线程的Looper及MessageQueue与之绑定,则Handler实例就与Looper及MessageQueue关联上了,就能把不同的Message发送到消息队列MessageQueue当中去了。

有几点大家也许会踩坑,这里先提前说下,待会会详细分析:

  • Message被取出后,交由相应Handler中回调的相关处理函数来处理,只有当这个Message被处理完后,Looper才会继续循环,取出新的Message来处理。
  • 在子线程中,需要先手动调用Looper.prepare()方法(有且只能有一次),然后在绑定该线程Looper的Handler中执行发送消息等各种操作后,再调用Looper.loop()方法,就会进行轮循处理了。
  • 子线程中Looper.loop()方法是无限循环,所以其后面的代码是无法执行的,要停止则需要在handler的相关处理函数中调用Looper.quit()或quitSafely ()方法。
  • 调用了Looper.quit()或quitSafely ()方法后,该线程中handler就无法再发送消息到消息队列中进行轮循处理了。

Android 消息机制说白了主要涉及的就是4个类:

  • Looper
  • MessageQueue
  • Handler
  • Message

Looper

官方文档中对Looper的介绍:Class used to run a message loop for a thread.用于在一个线程中轮循消息的类。

Looper的主要属性如下:

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

private static Looper sMainLooper;//主线程中的looper

final MessageQueue mQueue;//与之对应的消息队列
    
final Thread mThread;//所在的线程

主要方法如下:

static LoopergetMainLooper()

Returns the application's main looper, which lives in the main thread of the application.

MessageQueuegetQueue()

Gets this looper's message queue.

ThreadgetThread()

Gets the Thread associated with this Looper.

booleanisCurrentThread()

Returns true if the current thread is this looper's thread.

static voidloop()

Run the message queue in this thread.

static LoopermyLooper()

Return the Looper object associated with the current thread.

static MessageQueuemyQueue()

Return the MessageQueue object associated with the current thread.

static voidprepare()

Initialize the current thread as a looper.

static voidprepareMainLooper()

Initialize the current thread as a looper, marking it as an application's main looper.

voidquit()

Quits the looper.

voidquitSafely()

Quits the looper safely.

其中,子线程中默认是没有Looper的,所以我们需要调用Looper.prepare()来为当前线程创建一个Looper,来看一看这个方法:

    public static void prepare() {
        prepare(true);
    }

    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,如果有就会抛出个异常,没有则会创建,并添加到sThreadLocal中,而sThreadLocal是一个ThreadLocal对象,用来在线程中储存变量。这也就是告诉大家Looper.prepare()方法不能被调用两次。来看一下Looper的构造方法:

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

主要就是给该Looper创建并绑定了一个唯一的MessageQueue。在Looper.prepare()后,我们就可以通过Handler执行各类操作,然后调用Looper.loop()来循环处理消息了。来看一下loop()方法,截取了主要部分:

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {    //当前线程必须创建了Looper,即调用了Looper.prepare()
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;

    //IPC相关 
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) {  
        Message msg = queue.next(); //从消息队列中读取消息
        if (msg == null) {  //当调用了quit()或quitSafely()方法,即当MessageQueue已退出时
            return;
        }

        //...
        try {
            msg.target.dispatchMessage(msg);    //调用消息关联的handler来处理消息
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        //...
        msg.recycleUnchecked();    //当这个消息被处理时,会将其Flag标志为FLAG_IN_USE,防止该消息再入队,并将其添加到了回收消息的链中
    }
}

说白了loop()方法就是不断的从与该Looper绑定的MessageQueue当中取出Message,然后调用该Message的target的dispatchMessage()方法来处理,即交给发送该消息的Handler来处理。

需要注意的是,由于loop()方法是无线循环。

在主线程中为什么不会ANR这里不着重讨论,简单引用之前在某篇文章中看到的一段话(忘了在哪了= =):Android 是由事件驱动的,looper.loop() 不断地接收事件、处理事件,每一个点击触摸或者说Activity的生命周期都是运行在 Looper.loop() 的控制之下,如果它停止了,应用也就停止了。它在不停地调度分发消息,因此四大组件的调度、我们的输入事件、绘制请求才能得到处理。只能是某一个消息或者说对消息的处理阻塞了 Looper.loop(),而不是 Looper.loop() 阻塞它。也就说我们的代码其实就是在这个循环里面去执行的,当然不会阻塞了。

在子线程中,由于loop()是无限循环,所以loop()方法之后的代码就不会执行,这时你就需要在handler中调用Looper的 quit() 或 quitSafely ()方法,来结束循环,来看一下这两个方法:

    public void quit() {
        mQueue.quit(false);
    }

   
    public void quitSafely() {
        mQueue.quit(true);
    }

可以看到,这两个方法都是调用MessageQueue的quit()方法,只是传入的参数不同,待会我们在MessageQueue当中,再来着重看一下该方法,现在知道的就是当我们调用了Looper的quit() 或 quitSafely ()方法后,循环就结束了,这时你若再想使用Handler发送消息到消息队列中来进行轮循处理的话,就无法实现了。

MessageQueue

官方文档中对 MessageQueue 的介绍:Low-level class holding the list of messages to be dispatched by a Looper. Messages are not added directly to a MessageQueue, but rather through Handler objects associated with the Looper.即MessageQueue包含着由Looper分派的消息列表,消息并不是直接添加到消息队列中,而是通过与Looper相关联的Handler来添加的。

MessageQueue实质持有的就是一个消息链表的节点,一般我们使用Looper.myQueue()来获取当前线程的MessageQueue。

从上面Looper的分析中我们已知,在Looper的构造函数中会对MessageQueue进行初始化,来看一下MessageQueue的构造方法:

    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }

会传入一个是否允许退出的flag,一般都是true,然后调用了一个Native的方法,Native层我就不太明白了= =,不过这里也无需深究。而我们通过Handler发送消息到消息队列中,调用的都是enqueueMessage()此方法,我们来看一看:

    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {  //该msg的target不能为空,即我们通常消息都是通过handler来发送的
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {  //判断该msg是否被处理或移出了队列,当一个消息在被处理或移出队列时会将其flag标记为FLAG_IN_USE,即判断此msg的flag,防止有此标记的msg再次入队
            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的flag为FLAG_IN_USE
            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.
                //之前looper是阻塞状态,消息队列为空,新添加后会唤醒
                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;
    }

其实就是判断一下当前消息和消息队列的情况,做出相应的入队处理。

而消息出队的方法是Message next() { },源码有点长就不列出来了,主要就是有一个循环,会在消息列表中遍历去找下一个在当前时间可以处理且target不为空的消息返回,若没有的话就会阻塞。如果阻塞了,则在下一个msg进入队列时就会进行如上enqueueMessage()的一系列判断,看是否唤醒线程。

其中有一个自认为比较有用的接口:

    /**
     * Callback interface for discovering when a thread is going to block
     * waiting for more messages.
     */
    public static interface IdleHandler {
        /**
         * Called when the message queue has run out of messages and will now
         * wait for more.  Return true to keep your idle handler active, false
         * to have it removed.  This may be called if there are still messages
         * pending in the queue, but they are all scheduled to be dispatched
         * after the current time.
         */
        boolean queueIdle();
    }

根据注释可知,当线程阻塞时会回调这个接口,而MessageQueue提供了注册和移除此回调的方法 addIdleHandler(@NonNull IdleHandler handler) 与removeIdleHandler(@NonNull IdleHandler handler),那么我们就可以在当消息队列阻塞时回调此接口,在queueIdle()中做一些自己想要的操作了。

Handler

Handler在消息机制中毋庸置疑是非常重要的角色。

每一个Handler都会与该线程中唯一的Looper和MessageQueue绑定,在一个线程中,可以有多个Handler实例,同时在handler的构造方法中,你可以选择相关联的Looper对象,是在主线程中还是当前子线程中。

需要注意的是,在主线程中可以直接创建Handler对象,而在子线程中需要先调用Looper.prepare()才能创建Handler对象。

Handler的构造方法很简单:

    public Handler() {
        this(null, false);
    }

    public Handler(Callback callback) {
        this(callback, false);
    }

    public Handler(Looper looper) {
        this(looper, null, false);
    }

    public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }

    public Handler(boolean async) {
        this(null, async);
    }

    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;
    }

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

即你可以传入callback,也可以在handler的子类中重写handleMessage 方法。需要注意的是如果是创建了匿名内部类则会持有外部类的引用,注意内存泄漏。

通常我们所认为的发消息就是通过handler.sendMessage(Message msg),实质上handler.post(Runnabel r)方法其实也就是将runnable转成Message来发送,我们看下源码:

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

    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

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

    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

辗转反则最后调用的都是sendMessageAtTime()此方法,我们来看一下此方法:

    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,然后调用了enqueueMessage方法。

从前面的分析可知,Looper从队列中循环取出消息后,调用的是msg.target.dispatchMessage(msg)来处理消息,我们来看一下这个方法:

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

    private static void handleCallback(Message message) {
        message.callback.run();
    }

    public interface Callback {
        public boolean handleMessage(Message msg);
    }

    public void handleMessage(Message msg) {
    }

可以看到有三种情况:

  1. msg.callback不为空,即通过handler.post(runnable r)方法传入的,就会执行所传入的runnable中的run()方法;
  2. msg.callback为空,mCallback不为空,即在handler的构造方法中将callback当做参数传入,就会执行所传入的callback的handleMessage(msg)方法;
  3. msg.callback为空,mCallback为空,即我们是创建了handler的子类并重写了handleMessage 方法,那么就会执行Handler.handleMessage(msg)方法。

同时Handler还有很多方法,比如:

obtainMessage()

Returns a new Message from the global message pool.

hasMessages(int what)

Check if there are any pending posts of messages with code 'what' in the message queue.

removeCallbacks(Runnable r)

Remove any pending posts of Runnable r that are in the message queue.

removeMessages(int what)

Remove any pending posts of messages with code 'what' that are in the message queue.

removeMessages(int what, Object object)

Remove any pending posts of messages with code 'what' and whose obj is 'object' that are in the message queue.

都很通俗易懂,这里也就不一一列举了。


 

写得有点长了。。。Message就下一篇再聊聊了= =。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值