Android Handler运行机制Java层源码分析

1. Handler的使用

    mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };
    Message msg = mHandler.obtainMessage(what);
    mHandler.sendMessage(msg);

从上述代码可知,我们使用Handler的流程:

    创建Handler -> 获取消息对象 -> 发送消息 -> 处理消息

2. Handler的创建

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

构建函数中比较重要的是looper(Looper)对象,mQueue(MessageQueue)对象是与looper相关联的,mCallbackmAsynchronous后续会讲解。

3. Looper的来历

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

构建函数中设置当前线程对象到mThread变量中,并创建了一个MessageQueue对象,其创建过程我们稍后分析。心细的读者可能已经发现这是个私有构造函数,那么肯定是在类内部进行调用的,搜索发现其调用函数为:

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

    // 目前quitAllowed应该仅在主线程中被设置成false,自定义线程中均为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#prepare方法会创建Looper对象,并且某一线程仅能创建一个Looper对象,然后将创建的对象设置为线程安全变量。那么这个函数在哪里调用咧?既然跟线程相关,那么大胆猜测是在线程中调用的,而Android中最主要的线程是主线程,我们一开始的“Handler的基本使用”中并没有创建Looper,却可以使用Handler,那么Android主线程中必然在生成的时候就新建了一个Looper,查看Looper源码时会发现的确含有一个特殊的Looper(即:sMainLooper),这个特殊Looper对象通过调用Looper#prepareMainLooper()创建的,我们可以去ActivityThread中去看是如何实现的:

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

上述代码已去掉无关代码,至此我们就了解了Looper的来历,但之后调用的Looper#loop()是个什么鬼?这个主要是进入一个不断从MessageQueue中获取消息进行处理的无限循环,在分析这个循环之前我们要先来分析下MessageQueue是个什么鬼。

4. MessageQueue是什么鬼

    mQueue = new MessageQueue(quitAllowed);

上面创建Looper的时新建了一个MessageQueue对象,看下它的构造函数:

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

    private native static long nativeInit();

WFK,你就给我看这个!!!C++学渣表示强烈抗议。对于Native感兴趣的小伙伴可以自行查看源码,我跟着老罗的文章:Android应用程序消息处理机制(Looper、Handler)分析稍微感受了下源码的逻辑,nativeInit就是在native层创建了一个NativeMessageQueue及一个Looper对象,而native层的Looper对象利用管道机制来监控文件,从而可以利用epoll机制实现MessageQueue的等待和唤醒,这个在MessageQueue#next()会进一步分析。至此,MessageQueue对象就被创建出来了(由于MessageQueue的构建函数仅有包访问权限,因此正常情况下我们无需关心MessageQueue的创建),然后我们来看下Looper#loop()到底在做什么。

5. Looper#loop()到底正在做什么

    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
        //···省略无关代码
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            //···省略无关代码
            msg.target.dispatchMessage(msg);
            //···省略无关代码
            msg.recycleUnchecked();
        }
    }

Looper#loop()方法只有当Looper被创建出来之后方可调用,主要包括一个无限循环:从MessageQueue中获取消息进行处理,然后回收处理完的消息。我们先来看MessageQueue#next():

    Message next() {
        // 在Looper#loop()中我们知道返回空消息会退出loop()中的无限循环
        // 当调用MessageQueue#quit(boolean)时会调用nativeDestory()销毁MessageQueue,将ptr置为0
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // 仅在第一次调用时为-1
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            /**
            * 这个调用跟上面提到native层中Looper的epoll机制相关,用于等待可处理的消息
            * nextPollTimeoutMillis < 0 : 进入无限空闲等待,直到有新消息唤醒
            * nextPollTimeoutMillis = 0 : 不等待
            * nextPollTimeoutMillis > 0 : 进入空闲等待,直到有新消息唤醒或者nextPollTimeoutMillis超时
            **/
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                // 注:MessageQueue管理的消息是一个消息链表,后续Message中会详细分析
                if (msg != null && msg.target == null) {
                    /**
                    * msg.target为空是一类特殊消息(栅栏消息),用于阻塞所有同步消息,但是对异步消息没有影响,
                    * 后续会详细分析。在这个前提下,当头部是特殊消息时需要往后找是否有异步消息
                    */
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    // 找到消息
                    if (now < msg.when) {
                        // 消息的触发时间在当前时间之后,于是计算出需要等待的时间,准备进入有限空闲等待
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // 找到可处理的消息,更新消息链表数据,返回可处理消息
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (false) Log.v("MessageQueue", "Returning message: " + msg);
                        return msg;
                    }
                } else {
                    // 没找到消息,准备进入无限空闲等待
                    nextPollTimeoutMillis = -1;
                }

                // 没有可处理的消息,并且消息队列已经退出,则返回空消息让loop退出
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // 当mMessages为空或者mMessages的处理时间在当前时间之后(注意栅栏消息的特殊情况)时,
                // 并且pendingIdleHandlerCount没有在此处初始化过,
                // 则设置pendingIdleHandlerCount为IdleHandler的数量,IdleHandler后续详细说明。
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // 无空闲处理器,阻塞队列,进入空闲等待
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // 调用空闲处理器逻辑,此处代码仅调用一次
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf("MessageQueue", "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // 设置为0保证空闲处理器代码仅调用一次
            pendingIdleHandlerCount = 0;

            // 在处理空闲处理器的时候可能已经有可处理的消息,因此无需等待
            nextPollTimeoutMillis = 0;
        }
    }

由于这个函数是消息获取的关键,因此是无删减版。而相关说明直接注释在代码中,主要目的在于建议小伙伴们看源码。我相信读完源码应该对获取消息的机制有了比较完整的了解,当loopMessageQueue中获取到消息便可以进行消息处理并在处理后回收该消息,具体等我们分析完消息的发送之后再来看。

6. Handler#sendMessage中的消息怎么获取到

    public final Message obtainMessage(){
        return Message.obtain(this);
    }

直接调用Message#obtain(Handler):

    public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;
        return m;
    }

直接调用Message#obtain() - -!:

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

这里会先从回收池中取消息,如果没有就新创建一条消息;回收池是一个消息链表,sPoolSync是同步符号,sPool是链表头,sPoolSize是回收池中可用消息数,最大限制为MAX_POOL_SIZE(默认为50)。这些都没有太大可分析性,我们来看看Message的结构:

    public int what;    // 消息码,带Handler命名空间,因此不同Handler中相同消息码不冲突
    public int arg1;    // 整数数据
    public int arg2;    // 整数数据
    public Object obj;  // 任意对象,当利用Messenger进行跨进程传递时需要继承自Parcelable

    public Messenger replyTo;   // Messenger对象实现跨进程消息传递
    public int sendingUid = -1; // 跨进程是标记消息来源的Uid

    /**
    * flags可设置消息是否在使用以及是否异步
    * FLAG_IN_USE = 1 << 0,该标记只有在创建或obtain时才会清除,此时方可修改消息的相关数据及进行发送
    * FLAG_ASYNCHRONOUS = 1 << 1,标记该消息为异步消息,不受栅栏消息的影响
    **/
        /*package*/ int flags;

        /*package*/ long when;  // 消息执行时间,采用SystemClock#uptimeMillis()时间base
        /*package*/ Bundle data;    // 消息的数据
        /*package*/ Handler target; // 消息对应的Handler
        /*package*/ Runnable callback; // 消息对应的回调,具体参看下文中消息处理一节

        /*package*/ Message next; // 形成消息链表,以在MessageQueue以及消息回收池中使用

至此,消息的来源以及消息的结构分析完毕,其中flagsMessag自己管理,datagetDatapeekData以及setData进行管理,targetcallbackHandler中相关获取或发送消息的接口管理。获取到消息之后,便可以调用Handler的消息发送接口进行发送,那是如何进入MessageQueue的咧?

7. Handler#sendMessage中的消息怎么放入消息队列

Handler中的消息发送接口除了Handler#sendMessageAtFrontOfQueue(Message)均会调用Handler#sendMessageAtTime(Message),而这两个接口最终调用了Handler#enqueueMessage(MessageQueue, Message, long):

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

根据HandlermAsynchronous属性设置消息的异步属性,最后调用MessageQueue#enqueueMessage(Message, long):

    // 插入成功返回true,否则返回false
    boolean enqueueMessage(Message msg, long when) {
        // Handler中不允许发送target为空的消息,空消息为特殊消息(栅栏消息)
        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("MessageQueue", 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) {
                // 消息触发时间最早,直接插在链表头部,如果当前队列阻塞则唤醒消息队列的等待,见MessageQueue#next
                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;
                prev.next = msg;
            }

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

建议查看源码,相关说明已注释在代码中。把新消息放入队列,并在必要的时候唤醒消息队列进行处理,从而就回到上述MessageQueue#next的逻辑中,然后在有可处理消息的时候将消息发送到Looper#loop中进行处理及回收。

8. Message的处理及回收

当消息被返回到loop中时,调用:

    msg.target.dispatchMessage(msg);
    msg.recycleUnchecked();

target就是Handler,于是调用:

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

上述代码表明:当消息本身回调不为空时则由消息本身回调处理该消息;当HandlermCallback不为空时则由HandlermCallback处理消息;否则则由Handler中的钩子handleMessage进行处理。消息处理完了之后需要将消息回收:

    void recycleUnchecked() {
        // 标记为使用中,清除所有数据
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        // 放入消息回收池
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

至此,整个Handler的运行机制(创建Handler -> 获取消息对象 -> 发送消息 -> 处理消息)就分析完了。

9. 一些其他补充

9.1 栅栏消息

栅栏消息target为空的特殊消息,用于延迟MessageQueue中所有指定时间之后的同步消息,异步消息则仍可执行。发送和移除栅栏消息必须成对出现,否则可能导致MessageQueue被挂起。
其发送移除接口在Looper中:

    public int postSyncBarrier() {
        return mQueue.enqueueSyncBarrier(SystemClock.uptimeMillis());
    }

    public void removeSyncBarrier(int token) {
        mQueue.removeSyncBarrier(token);
    }

调用了MessageQueue#enqueueSyncBarrier(long):

    int enqueueSyncBarrier(long when) {
        // 创建一个target为空的特殊消息,并根据when插入MessageQueue中合适的位置
        // 无需唤醒因为栅栏消息的目的在于阻塞消息的执行
        synchronized (this) {
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;

            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            if (prev != null) {
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }

必须成对出现的MessageQueue#removeSyncBarrier(token),其中tokenenqueueSyncBarrier返回:

    void removeSyncBarrier(int token) {
        // 移除token对应的栅栏消息,并在必要的时候进行唤醒
        synchronized (this) {
            Message prev = null;
            Message p = mMessages;
            while (p != null && (p.target != null || p.arg1 != token)) {
                prev = p;
                p = p.next;
            }
            if (p == null) {
                throw new IllegalStateException("The specified message queue synchronization "
                        + " barrier token has not been posted or has already been removed.");
            }
            final boolean needWake;
            if (prev != null) {
                prev.next = p.next;
                needWake = false;
            } else {
                mMessages = p.next;
                needWake = mMessages == null || mMessages.target != null;
            }
            p.recycleUnchecked();

            if (needWake && !mQuitting) {
                nativeWake(mPtr);
            }
        }
    }

有没有觉得,so easy - -!

9.2 空闲处理器(IdleHandler)

IdleHandler定义在MessageQueue中:

    public static interface IdleHandler {
        // 返回true表示保持在MessageQueue的mIdleHandlers中
        boolean queueIdle();
    }

具体调用时机见MessageQueue#next中的分析。

9.3 Handler的相关接口介绍

获取消息(带不同参数):

    final Message obtainMessage()
    final Message obtainMessage(int what)
    final Message obtainMessage(int what, int arg1, int arg2)
    final Message obtainMessage(int what, Object obj)
    final Message obtainMessage(int what, int arg1, int arg2, Object obj)

发送消息:

    final boolean sendEmptyMessage(int what)
    final boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
    final boolean sendEmptyMessageDelayed(int what, long delayMillis)
    final boolean sendMessage(Message msg)
    final boolean sendMessageAtFrontOfQueue(Message msg)
    final boolean sendMessageDelayed(Message msg, long delayMillis)
    boolean sendMessageAtTime(Message msg, long uptimeMillis)

发送带Callback的消息:

    post(Runnable r)
    final boolean postAtFrontOfQueue(Runnable r)
    final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
    final boolean postAtTime(Runnable r, long uptimeMillis)
    final boolean postDelayed(Runnable r, long delayMillis)

移除消息:

    final void removeCallbacks(Runnable r)
    final void removeCallbacks(Runnable r, Object token)
    final void removeCallbacksAndMessages(Object token)
    final void removeMessages(int what)
    final void removeMessages(int what, Object object)

9.4 HandlerThread类

HandlerThread类是Handler的一个辅助类,当调用HandlerThread#start()之后,会创建一个带LooperThread

    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

这样,我们使用非主线程Handler的时候便比较简单了:

    HandlerThread t = new HandlerThread(TAG);
    t.start();
    mHandler = new Handler(t.getLooper(), new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
             // TODO: 处理消息
        }
    }); 

10. 参考文献

  1. Android应用程序消息处理机制(Looper、Handler)分析 - - 罗升阳
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容