美文网首页
源码分析->Handler

源码分析->Handler

作者: 杨0612 | 来源:发表于2020-07-22 15:09 被阅读0次
源码分析基于Android-28
1.Handler存在意义是什么?

(1)解决线程间通讯的问题;
(2)简化wait、notify的使用;
(3)Android驱动机制,期望所有对ui 的操作都转到主线程,并且保证任务是有序执行的。app 的设计就是单线程处理ui的。

2.Handler、MessageQueue、Looper是什么关系?

(1)有个很重要的关键词:唯一性;
(2)每个线程都只有一个Looper,ThreadLocal保证了线程间单例;
(3)一个Looper对应一个MessageQueue;
(4)多个Handler可以对应同一个Looper。

3.Handler、MessageQueue、Looper的作用分别是什么?

(1)Handler 负责发送、处理消息,执行者;
(2)Looper 负责从MessageQueue中取消息,分发给相应的Handler来处理,分发者;
(3)MessageQueue负责存放消息,保存者,它是一个单链表不是我们通常用的集合;


对应关系.png
4.源码分析
4.1Handler构造函数

如果使用无参的构造函数,最终会调用

Handler(Callback callback, boolean async)

主要工作:
(1)Looper.myLooper(),获取当前线程的Looper赋值给成员变量mLooper ;
Tips:如果在子线程构造Handler,没有调用Looper.prepare的情况下,将会抛出
new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()")
异常;
如果是在主线程构造则不会,因为主线程的Looper在ActivityThread.main方法中就已经创建了;
Looper的构造方法是private,必须通过Looper.prepare静态方法来创建,加上ThreadLocal就保证了Looper线程唯一;
(2)mLooper.mQueue,获取Looper的消息队列赋值给成员mQueue ;
(3)mCallback ,是Handler全局的callBack,默认情况下为空;
(4)mAsynchronous ,默认是false,也就是对应普通消息;
(5)经过(1)(2)两个步骤,Handler就和当前线程的Looper、MessageQueue关联到一起了。

  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.prepare()

主要工作:
(1)通过sThreadLocal判断当前线程是否已经创建了Looper,是则抛出异常,否则创建Looper并保存到sThreadLocal中,Looper的创建伴随着MessageQueue创建,这里通过ThreadLocal保证了Looper是线程唯一;
(2)注意:主线程的Looper是不能退出的,所以传入的参数quitAllowed为false,而子线程的是允许退出,一旦Looper退出,也就是for循环结束,那么线程也就结束了;

   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));
    }
看下发送消息的逻辑,基本上Handler所有跟发送消息相关的方法,最终都调到
4.2Handler.enqueueMessage()

主要工作:
(1) msg.target = this,给msg的target赋值,当该消息需要被处理时,将由target来处理;
(2)mAsynchronous,在Handle的构造函数提到,该变量默认情况为false,消息为普通消息,这有别于异步消息;
(3)queue.enqueueMessage,将消息放入队列;

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

主要工作:
(1)判断msg是否有target,没有则抛出异常;
(2)判断msg是否已经被使用,被使用则抛出异常;
(3)存在多个子线程往主线程队列发消息的情况,synchronized 关键字保证了后续的处理是线程安全的;
(4)判断MessageQueue是否已经退出了,已经退出了则返回;
(5)Message p = mMessages,使得p都指向单链表头部消息;
(6) if (p == null || when == 0 || when < p.when) ,p == null,表示MessageQueue为空,when == 0,表示要插入的msg插入查到队列的最前面,when < p.when,表示msg执行时间小于头部消息执行时间,满足以上三个条件中的一个,则将消息插入链表头部,并让mMessages 指向该msg,如果线程原本是block的,后续则唤醒;
(7)上述条件不满足,则遍历MessageQueue,把msg插入到合适的位置,MessageQueue是按执行时间从小到大排序的,如果线程原本就是休眠,且队头是消息屏障,且消息本身是异步消息,且是最早的一条,则需要唤醒,为什么是最早一条才有可能需要唤醒呢?因为还没轮到你执行呢;
(8)如果线程需要被唤醒,则通过nativeWake唤醒线程,nativeWake 就是往管道eventFd写了一个数,监听者是直监听这个eventFd。

Tips:为什么没有采用wait、notify方法呢?旧的版本是采用这两个方法来唤醒线程的,因为后面设计到native的处理,所以就通过native层来唤醒线程。
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) {
                ......
                msg.recycle();
                return false;
            }

            msg.markInUse();
            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.
                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;
    }
发消息流程.png
取消息
4.3 Looper.loop()

主要工作:
(1)queue.next,从MessageQueue中取出消息,如果MessageQueue为空,则queue.next一直阻塞不会返回,如果MessageQueue退出了,则queue.next返回null;
(2)msg.target.dispatchMessage(msg),把msg丢给相应的target来处理;
(3)msg.recycleUnchecked,把消息放到复用池中,重复使用。

Tips:主线程的Looper是在ActivityThread.main方法中启动的,而子线程需要手动调用,并且主线程的Looper是不能退出的;
public static void loop() {
      ......
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            ......
            try {
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            }
           ......
            msg.recycleUnchecked();
        }
    }
MessageQueue.next()

主要工作:
(1)当ptr 等于0,则表示退出了MessageQueue;
(2)第一次循环,nextPollTimeoutMillis 为0,则nativePollOnce立即返回,线程不休眠,直接执行下面的代码取链表头消息;nativePollOnce会调到native层epoll_wait监听管道内的事件,当管道内的事件fd与期望的fd一致,则唤醒线程;
(3)考虑一个线程发消息跟一个线程取消息,synchronized 关键字保证了取消息是线程安全的;
(4) if (msg != null && msg.target == null) ,条件满足,则表示队头为消息屏障,后面的普通消息延迟执行,取第一个异步消息优先执行,常用在优先执行绘制任务;
(5)上述(4)不成立条件,可能无消息或队头不是消息屏障,则取头消息;
(6)上述(4)(5)获取到的消息不为空(无论是普通消息还是异步消息),消息执行时间到了(now < msg.when条件不成立),则返回msg,退出该循环,并把mBlocked 置为false,否则设置休眠时间nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE),线程进入休眠等待超时唤醒;
(7)上述(4)(5)获取到的消息为空,nextPollTimeoutMillis则赋值为-1,线程需要一直休眠,直到被唤醒;
(8)如果MessageQueue没有消息或消息没有到要执行的时间,mIdleHandlers的消息将得到执行机会;mIdleHandlers集合保存延迟执行的消息,常用在延迟初始化第三方sdk;
(8)如果mIdleHandlers为空,则mBlocked 置为true,并进入下一次循环;
(9)如果mIdleHandlers不为空,执行 idler.queueIdle方法,mIdleHandlers处理完,则nextPollTimeoutMillis赋值为0,并进入下一次循环;

Tips:最后一步为什么休眠时间为0呢?可能在执行延迟任务的时候,有一个任务已经放入了队列或者延迟任务执行比较久,前面设置timeout已经到了,所以需要立马进入下一次的循环。
Tips:计算时间采用了SystemClock.uptimeMillis而不是我们常用的System.currentTimeMillis(),如果系统时间修改了,System.currentTimeMillis()获取时间不准,前者获取从开机到目前的时间,不包括休眠时间,相对比较准。
Tips:一个取消息周期,mIdleHandlers的消息可以得到一次执行机会,因为不能把资源浪费在Idle消息,普通消息要得到被执行的机会;
Message next() {
        final long ptr = mPtr;
        if (ptr == 0) {//(1)
            return null;
        }
        ......
        int nextPollTimeoutMillis = 0;
        for (;;) {//(2)
        ......
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {//(3)
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {//(4)
                    // 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) {//(5)
                    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 {//(6)
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

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

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {//(7)
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {//(8)
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

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

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {//(9)
                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(TAG, "IdleHandler threw exception", t);
                }

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

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;//(10)
        }
    }
取消息流程.png
处理消息
Handler.dispatchMessage()

主要工作:
(1)如果是调用sendMessage(Message msg)发送消息,则msg的callback为空,如果是调用post(Runnable r)发送消息,则msg的callback为Runnable,handleCallback(msg)则是回调Runnable的run方法;
(2)如果Handler有全局mCallback,则触发mCallback.handleMessage处理消息,并且返回值为true的话,则后续的Handler.handleMessage不会被触发:
(3)如果Handler没有全局mCallback,默认情况下没有,或者mCallback.handleMessage返回false,则触发handleMessage进行消息处理;
(4)这里有一点责任链设计模式的影子;

   public void dispatchMessage(Message msg) {
       if (msg.callback != null) {
           handleCallback(msg);
       } else {
           if (mCallback != null) {
               if (mCallback.handleMessage(msg)) {
                   return;
               }
           }
           handleMessage(msg);
       }
   }
处理消息流程.png
补充Message成员变量:
// 用户自定义,主要用于辨别Message的类型
public int what;
// 用于存储一些整型数据
public int arg1;
public int arg2;
// 可放入一个可序列化对象
public Object obj;
// Bundle数据
Bundle data;
// Message处理的时间。相对于1970.1.1而言的时间
// 对用户不可见
public long when;
// 处理这个Message的Handler
Handler target;
// 使用Handler post方法发送消息时,对应的Runnable就是callback
Runnable callback;
// MessageQueue是一个链表,next表示下一个
Message next;
常见面试题:

1.谈谈Android的消息机制
从它是什么东西、解决了什么问题、发消息、取消息、分发消息、休眠唤醒是如何实现的这几个方面回到。
2.post(Runnable r)和sendMessage(Message msg)区别?
前者会构造一个Message,然后给callback赋值为r,后者没有,两者最终都会调到enqueueMessage方法中。
2.Looper.loop 是一个死循环,为什么不会出现ANR或者卡顿呢?
卡顿本质:主线程执行耗时任务或者频繁GC,导致无法执行绘制任务,上一帧重复绘制。
ANR本质:没有在规定时间内完成任务。
正因为这个死循环才保证发往主线程的任务及时响应,例如绘制任务,既然绘制任务能够及时响应,那就不会出现卡顿或者ANR,而且没有消息需要处理,线程会休眠让出cpu。
3.消息延迟是如何实现的
MessageQueue是一个单链表,按Msg的when从小到大进行排序,这样就实现了延迟消息的处理,而且计算时间采用了SystemClock.uptimeMillis而不是我们常用的System.currentTimeMillis(),如果系统时间修改了,System.currentTimeMillis()获取时间不准,前者获取从开机到目前的时间,不包括休眠时间,相对较为准。
4.当有消息正在被处理,新插入队头的消息如何处理?
当前消息被处理完,会从头开始执行消息。因为在MessageQueue.enququeMessage中,当新消息需要插入队头,mMessages会指向新消息,那么在MessageQueue.next中,就会取mMessages消息就行处理。
5.MessageQueue如何保证线程安全?
在MessageQueue.enqueueMessage以及next方法都加了锁,保证线程间安全;
6.如何保证发消息、取消息不死锁?
情况1,取消息next获得了锁,那么enqueueMessage就处于等待,
取到了消息需要处理就返回,释放锁,退出for循环,那么enqueueMessage就可以获得锁,没有取到消息或者消息没有达到执行执行,虽然不退出for循环,但是会释放锁而且进入nativePollOnce等待,那么enqueueMessage就可以获得锁,反之enqueueMessage成功插入消息,也是会释放锁的。
6.为什么Looper.loop以及MessageQueue.next都有一层for循环,那么岂不是两层循环?
Looper.loop保证消息分发以后,继续从MessageQueue取消息;
MessageQueue.next,保证被唤醒继续取消息进行分发,直到有效消息。
7.Handler如何实现线程切换?
这个题目感觉有点废话,Handle就是实现线程切换的啊,有什么好问的??但是仔细想想,从代码层面是如何实现呢?关键就在于Looper.loop方法是在哪里执行的?以主线程Looper为例,主线程的Looper是在ActivityThread.main方法创建,也是在它里面执行的,那么就是说Looper.loop是在主线程执行的,从而保证了Message.next取消息分发是在主线程。那么,当子线程存在Handler拷贝,利用拷贝往Handler对应的MessageQueue发送消息,这样就现实了线程间切换,MessageQueue是线程共享资源。
8.Handler的唤醒机制
是Linux的唤醒机制,等待方通过nativePollOnce会调到native层epoll_wait监听管道内的事件,当管道内的事件fd与期望的fd一致,则唤醒线程。

以上分析有不对的地方,请指出,互相学习,谢谢哦!

相关文章

网友评论

      本文标题:源码分析->Handler

      本文链接:https://www.haomeiwen.com/subject/wfuykktx.html