Android源码---Handler

关于Handler的源码流程,我也看过很多遍,却没有认真的写过一篇博客,每次想看的时候都要上网去找一些博客。这次我想自己认真的写一篇Handler的博客,好供自己还有需要的人学习。

说到Handler相信大家都不陌生,谁的项目中还没用过Handler呢,是吧。这篇博客,我打算从源码中来撸Handler的流程,对于Handler的时候,我就不再这里废话了。

一、源码

提到Handler就一定要说的四个类:Message,MessageQueue,Looper,Handler

我在网上看到一篇博客,对于它们的关系,描述的非常的好,推荐大家看看:点击这里。我就不废话说它们四个的源码和关系了。我更喜欢从流程上来探索。

我们首先先从App的入口ActivityThread类入手,我们在main方法中看到如下的代码:

    public static void main(String[] args) {
        ....
        Looper.prepareMainLooper();
        ....
        Looper.loop();
    }

我们先进入preparemainLooper看一下

    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");
        }
        //保存主线程的Looper对象
        sThreadLocal.set(new Looper(quitAllowed));
    }


    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            //赋值为刚刚保存的主线程Looper对象
            sMainLooper = myLooper();
        }
    }

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

先要了解ThreadLocal的点击这里,其实上述代码很好理解,主要就是创建了一个主线程特有的Looper对象。

我们再看一下Looper.loop方法,这个方法很长,我贴一下关键的代码:

    public static void loop() {
        //获取主线程的Looper对象
        final Looper me = myLooper();
        ...
        //拿到Looper对象中的消息队列
        final MessageQueue queue = me.mQueue;
        ...
        for (;;) {
            //从消息队列中拿到要发送的消息
            Message msg = queue.next(); // might block
            ...
            try {
                //发送给目标类
                msg.target.dispatchMessage(msg);
                ...
            } finally {
                ...
            }
            ...
            //Message对象进行recycle
            msg.recycleUnchecked();
        }
    }

前两句代码没什么说的,看到for方法的时候,有的小伙伴可能会有疑问了:这里不是一个死循环吗?难道不会ANR吗?我当时在这里也存了这个疑问。然后想出了一个合理的解释(没有确认过,但是我认为应该差不多是这样):首先整个App的运行正是依赖于主线程,而一个线程想要持续运行不停止,内部肯定要有一个死循环,不让线程退出。而Looper的死循环的设计保证了App持续运行不退出。

我们继续向下看,我们查看queue.next()方法,进入MessageQueue中,查看next方法

Message next() {
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            ...

            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }
                ...
            }
            ...
        }
    }

我们可以看见,这里又是一个死循环,那为什么这里也没有ANR呢?这篇文章有提过。我们首先看见了nativePollOnce方法,这个方法是一个native方法,我没有深入研究过,从其他博客中可以得知其左右:若果nextPollTimeoutMillis等于0时,立即向下执行;如果大于0,则阻塞相应的毫秒时间;如果小于0,一直阻塞直到被nativeWake方法唤醒。

mMessage表示将要执行的消息,如果此方法是首次执行,那么mMessage肯定等于空,所以nextPollTimeoutMillis被赋值为-1,导致nativePollOnce方法一直被阻塞。那什么时候被nativeWake方法唤醒呢?

我们思路切换一下,切换到Handler这里,使用Handler发送一条Message(注意这里的Handler一定要是主线程的Handler)

Handler中有多个发送Message的API,但是最终都会到sendMessageAtTime这个方法中,我们看一下源码:

   final MessageQueue mQueue;

   public boolean sendMessageAtTime(@NonNull 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方法中

    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

Message的target会被赋值。queue是MessageQueue,所以我们进入MessageQueue中查看enqueueMessage方法:

    boolean enqueueMessage(Message msg, long when) {    
        synchronized (this) {
            ....
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                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;
            }

            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

这里有一个if-else,表示两种情况。

if的情况:p被mMessage赋值,p=null,表示在此之前没有要被执行的消息;when等于0表示立即执行,此时新消息会被放入的消息队列的头,被第一个执行;when<p.when表示新消息延迟时间比消息队列第一个消息的延迟时间小,所以新消息会被放入到一个位置执行。

else的情况就:新消息不是第一个执行,延迟时间when和消息队列中消息的延迟时间作比较,放在相应的位置。

if-else之后,就会根据needWake值来执行nativeWake方法,进行唤醒nativePollOnce。如果新消息不是第一个执行,needWake肯定为false,也就是说不会进行唤醒操作;如果新消息时第一个执行,needWake被mBlocked赋值,mBlocked在next方法中有被赋值,如果pendingIdleHandlerCount小于等于0时,会被赋值为true,而此时的pendingIdleHandlerCount等于0。也就是说,此时肯定会执行唤醒操作,所以我们再次回到next方法中,主要执行同步代码块synchronized中的以下代码:

    synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                //把无效的消息移出消息队列
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }
                ...
            }

先把消息队列中的第一个Message取出来。如果target为空,表示此消息无效,从消息队列中移除。然后判断一下这个消息的延迟时间,如果比当前时间小,则继续执行nativePollOnce进行阻塞,阻塞时间为刚刚比较的时间差;如果当前的时间比延迟时间大,或者相等,那么此消息要被执行了,先从队列中移除,再把这个消息返回给Looper。

Message msg = queue.next(); 

拿到这个消息后,执行了如下代码:

msg.target.dispatchMessage(msg);

我们从Message源码中可以执行,target就是Handler。我们查看Handler中的dispatchMessage方法:

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

我们可以看到,如果没有对Message中的Callback和Handler中的Callback赋值,那么执行handleMessage方法,这个方法大家应该都很熟悉吧,就是使用Handler时覆写的方法。到这里,源码的流程就结束了。

二、问题

Handler是面试中的常客,我找了一篇比较全面的面试博客,推荐给大家,点击这里

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值