Handler 的工作流程-源码梳理

总览

Handler 的工作流程

  • 创建,绑定线程特有的 Looper 实例
  • 发消息 Handler#sendMessage 或 Handler#post
  • 入队 MessageQueue#enqueueMessage()
  • 消息循环 Looper.loop()
  • 分发处理 Handler#dispatchMessage

创建,绑定线程特有的 Looper 实例

创建,定义处理逻辑:

// 某个 Activity 中

    // 子线程才需要执行,否则新建 Handler 会报错
    Looper.prepare();

    // 创建的时候,需要实现 handleMessage 函数
    Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            // do something                        
        }
    };
	
    // 子线程才需要执行,否则收不到消息
    Looper.loop();

Handler 的构造函数有多个,但其实终究归类为两种:
「不带 Looper 实例的」和「带 Looper 实例的」。
Handler 的构造函数源码:

// 代码段a0
// .../Android/sdk/sources/android-28/android/os/
// Handler.java

    /**
     * 不带 Looper 实例,那就会自己创建一个 Looper 实例;
     * 通过 Looper.myLooper() 来创建一个 Looper 实例 mLooper。
     * mLooper 是和当前线程绑定的,
     * mLooper 在被创建时,其构造函数,会创建一个 MessageQueue 实例 mQueue,
     * 同时此处的 Handler 实例也会拿到 mQueue 实例的引用。
     * 
     * 所以可以这样说:Handler 实例在创建时,
     * 会绑定一个 Looper 实例 mLooper 和 一个 MessageQueue 实例 mQueue,
     * 并且,Looper 实例 mLooper 又是和当前线程绑定的。
     */
    public Handler(Callback callback, boolean async) {
        .....

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    
    
    /**
     * 带 Looper 实例,则不用再创建一个 Looper 实例
     * 直接赋值就行
     * 
     */
    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

Looper 的构造函数 和 myLooper( ) 函数:

// 代码段a1
// .../Android/sdk/sources/android-28/android/os/
// Looper.java

    private Looper(boolean quitAllowed) {
        // 新建一个队列
        mQueue = new MessageQueue(quitAllowed);
        // 取得当前线程的引用
        mThread = Thread.currentThread();
    }
    
    
    /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        // 返回当前线程的 Looper 实例
        return sThreadLocal.get();
    }

消息发送的两种方式 sendMessage 和 post

发消息的用法示例:

	// 发送一个消息
	Message message = new Message();
	message.obj = "I am a message";
	mHandler.sendMessage(message);

	// post 一个线程
	mHandler.post(new Runnable() {
		@Override
		public void run() {
			 LogUtil.e("当前线程:" + Thread.currentThread().getName());
		}
	});

注意这个动作是可以在别的线程中执行的,因为 mHandler 实例是一个对象,线程共享。


goto sendMessage(message)post(new Runnable(){...})

// 代码段0:
// Handler 类

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

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

到此可知,最终调用了 enqueueMessage 函数进行入队处理。

再看 post 函数源码:

// 代码段0.1:
// Handler 类 

   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 static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool; // 从线程池里取一个 Message 实例
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

post 函数提交的 Runnable 实例,会被放入 Message 实例的成员变量 callback 中,接着调用
sendMessageDelayed 函数继续流程,以 Message 实例的形式继续传递。

到此可知,无论调用 sendMessage 函数还是 post 函数,最终都会调用 sendMessageDelayed 函数,进而调用 enqueueMessage 函数把消息 msg 入队。

消息入队 MessageQueue#enqueueMessage()

// 代码段1:
// Handler 类

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this; //入队前,记录所属的 handler
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
// 代码段2:
// MessageQueue 类

    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) {
            // 如果表头为空 or 延迟时间为0 or 延迟时间比表头时间少
            // 则插入表头

                // 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) { // 按 when 的早晚排序,若when相等,后入队的排后面,例如,2,3,5;入队是 3,那么循环到 p.when == 5 的时候break;
                        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;
    }

可知 queue 中,message 以链表的形式存储,按执行事件的早晚,即 when 由小到大排序,若when相等,后入队的排后面。

消息循环 Looper.loop()

在 new Handler 之前,需要在当前线程通过委托 Looper.prapare() ,new 一个 Looper 实例并存到当前线程的线程本地 map (即 当前线程实例的成员变量 threadLocals)中,从而实现绑定当前线程 。

Looper.prapare() 之后,执行 new Handler(),然后在当前线程执行 Looper.loop() 开启消息循环,Looper 不断从队列里取消息。否则刚刚 new 的 Handler 收不到消息。

在 Looper.loop() 函数内,不断从消息队列中取消息,
再把消息分发到其 target 的 dispatchMessage(msg) 函数处理。

去看 Looper.loop() 函数源码:

// 代码段3:
// Looper 类

    public static void loop() {
        // 3-0,取出当前线程的 Looper 实例 me
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        // 3-1,通过当前线程的 Looper 实例 me,取出队列实例,
        // 所以可以说,取出当前线程的 MessageQueue 实例 queue
        final MessageQueue queue = me.mQueue;

        ......

        for (;;) {
            // 3-2 处,当前线程的 MessageQueue 实例 queue 取出消息
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            ......

            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
            final long dispatchEnd;
            try {
                // 3-3 分发给所属的 Handler 实例,参考代码段1
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

            ......

            msg.recycleUnchecked();
        }
    }

注释 3-0,3-1 处,可知,消息循环是依赖当前线程的 Looper 实例 和 MessageQueue 实例进行的;

注释 3-2 处,在 for 循环中,不断的从当前线程的 MessageQueue 实例取出消息,并做一些判断,然后在当前线程处理。

这里可以引出线程切换问题:

此处取出的消息,是由本线程创建的 Handler 实例发送进来的,而 Handler 实例是线程共享的;
因此,可以做到线程切换的效果:
由别的线程引用了当前线程创建的 Handler 实例并发送消息过来,
然后在当前线程处理消息。
那其他线程可以引用到本线程的

Q: 其他线程可以拿到当前线程的 MessageQueue 实例吗?

通过 Handler 实例的 getLooper().getQueue() 可以拿到。

Q: 开发者通过其他线程拿到当前线程的 MessageQueue 实例后,可以执行线程循环吗?
不行,开发者即使拿到 MessageQueue 实例,因为 next( ) 函数是默认缺省修饰符,不同包是无法访问的。

取消息 MessageQueue.next()

源码如下:

// .../Android/sdk/sources/android-28/android/os/
// MessageQueue.java

    Message next() {
        .....

        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            // native 层调用
            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; 
                
                /** 如果表头不空,但是 表头消息没有 handler
                 *  则向后遍历
                 */ 
                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());
                    // 直到找到一个 msg 不空,且是异步的
                    // 同步屏障的意义就在于先取异步消息来执行
                }
                if (msg != null) {
                    // 没到执行时间,等过 nextPollTimeoutMillis 这段时间再唤醒
                    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 {
                        // 找到可以执行的 msg
                        // 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;
                }
            ......
        }// end for (;;)
    }

默认缺省的修饰符,只能在同类或同包下调用 next( ) 函数,
而在 “…/Android/sdk/sources/android-28/android/os/” 该包下,
仅有 Looper类的静态函数 looper( ) 中,调用了 next( ) 函数;
换句话说:Looper.looper( ) 是开启线程循环的唯一入口。

再回到 代码段3 的注释 3-3 处,msg.target 得到的是 msg 所属的 Handler 实例, 代码段1的 Handler 类的 enqueueMessage 函数把消息入队之前,赋值自身 this 给 msg.target 。
所以,msg.target.dispatchMessage(msg) 调用的是 Handler 类的 dispatchMessage 函数。

小结

Looper.loop() 函数内用死循环,不断调用消息队列里的 next 函数取消息;

而消息队列里的 next 函数内又有死循环,所以可能会阻塞。

消息队列本质是以链表的形式存储消息的。

取消息的原则

首先取表头消息,如果 target 为空,再往后找,

直到找到 target 不为空的排最早的消息,然后再进入下一个判断;

下一个判断:

根据消息的延迟时间,判断是否可以立刻执行?

若消息还不是立刻执行,就休眠一会;若消息可以执行,就分发给 target 的 dispatchMessage 函数处理。


接着看 dispatchMessage 函数源码...

分发处理 Handler#dispatchMessage

// 代码段4
// Handler 类

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) { 
            // 4-1 表示由 post 函数发送的 Runnable,参考代码段0.1,post 函数的源码可知
            handleCallback(msg);
        } else {
            if (mCallback != null) { 
                if (mCallback.handleMessage(msg)) { // 4-2 如果有回调处理了,直接返回
                    return;
                }
            }
            handleMessage(msg); //4-3 最后才叫给 handleMessage 函数
        }
    }

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

值得注意的是 4-2 处,mCallback 是在构造函数传进来的。

构造函数 和 Callback 的定义:

// 代码段4.1
// Handler 类

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

    public Handler(Callback callback, boolean async) {
	// 。。。

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

    public interface Callback {
        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        public boolean handleMessage(Message msg);
    }

代码段4中,如果 mCallback.HandleMessage 返回 true,就表示处理了,就不会走 4-3 处的 handleMessage 函数。


Handler 的工作过程的时序图展示

线程A Looper MessageQueue Handler 线程B 0,Looper.prepare() 1,prepare(true) 2,mQueue = new MessageQueue(quitAllowed) mQueue 实例 opt [ new Looper(.) ] 3,mThread = Thread.currentThread() 把新 Looper 实例, 放到线程本地map 4,mHandler = new Handler() Handler 实例 5,Looper.loop() 6,me = myLooper() //线程A 的 Looper 实例 7,queue = me.mQueue // 线程A 的 MessageQueue 实例 8,for( ;;) //死循环 9,msg = queue.next() //阻塞 10,for( ;;) //死循环 11,nativePollOnce(ptr, nextPollTimeoutMillis) 12,prevMsg = null 13,msg = mMessages // 拿到表头 若表头消息没有 target, 进入循环,否则跳过循环 向后遍历,直到找到 一个异步的 且不为空的 msg while (msg != null && !msg.isAsynchronous()) 14,prevMsg = msg 15,msg = msg.next loop [ while 循环 ] 能来到这里,表示 已经找到 msg, 判断是否可立刻执行 没到执行时间,等过 nextPollTimeoutMillis 这段时间再唤醒 16,nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE) 能来到这里,表示 找到的 msg, 可以立刻执行 脱离链表处理, 操作前后指针 17,if (prevMsg != null) - prevMsg.next = msg.next 18,else - mMessages = msg.next 19,msg.next = null return msg opt [ synchronized (this) 同步代码块 ] loop [ 消息循环,拿到消息就返回 ] 20,msg.target.dispatchMessage(msg) if (msg.callback != null) 21,handleCallback(msg) if (mCallback != null) 22,mCallback.handleMessage(msg) else ...... 23,handleMessage(msg) opt [ 在 线程A 中执行 ] 24,msg.recycleUnchecked() // 处理完回收 loop [ 消息循环,拿到消息就分发,在线程A中 ] 25,sendMessage( Message = msg ) 26,sendMessageDelayed(msg, 0) 27,sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis) 28,mQueue.enqueueMessage(msg, uptimeMillis) 29,Message p = mMessages // 拿到表头 进入判断 if (p == null || when == 0 || when < p.when) 如果表头为空 or 延迟时间为0 or 延迟时间比表头时间少 - - - 则插入表头 30,msg.next = p 31,mMessages = msg else - 否则插入链表中间 32,for( ;;) 33,prev = p 34,p = p.next 每往后移动一次,判断一次 if (p == null || when < p.when) break //找到插入位置,跳出循环 细节:when < p.when, 比某个消息早,才能排其前面, 同时间则排后面 排序顺序: 按 when 的早晚排序, 若when相等,后入队的排后面 35,msg.next = p 36,prev.next = msg // 完成插入 loop [ 从表头往后遍历 ] 小结:消息以 链表的形式存储 表头是成员变量 mMessages 37,post( Runnable = r) 38,sendMessageDelayed(getPostMessage(r), 0) 39,sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis) 40,mQueue.enqueueMessage(msg, uptimeMillis) - - -后续操作同上 线程A Looper MessageQueue Handler 线程B

总结

Handler 的工作流程

  • 创建,绑定线程特有的 Looper 实例

    • 构造函数内,mLooper = Looper.myLooper()
      • Looper 类的静态函数会 new 一个 Looper 实例,存到当前线程中
  • 发消息 Handler#sendMessage 或 Handler#post

    • post 的 Runnable,被 getPostMessage 函数包装称 message
      • Runnable 被赋值到 message 的成员变量,m.callback = r;
    • 两种方法,最终都是调用 sendMessageDelayed
      • msg.target = this; //入队前,记录所属的 handler
      • queue.enqueueMessage(msg, uptimeMillis);
  • 入队 MessageQueue#enqueueMessage()

    • 以链表形式存储
    • 如果表头为空 or 延迟时间为0 or 延迟时间比表头时间少,插入表头
    • 否则,插入队列中间或末尾,保证按 when 从小到大排序即可,when 相同的,后来后处理
  • 消息循环 Looper.loop()

    • for : msg = queue.next();
    • msg.target.dispatchMessage(msg);)
  • 分发处理 Handler#dispatchMessage

    • post Runnable – handleCallback(msg); – callback.run
    • mCallback.handleMessage(msg)
    • handleMessage(msg)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值