从 sendEmptyMessage 开始,一步步解剖Handler

3,047 阅读13分钟

工作的时候发现自己对于很多东西用起来得心应手,原理机制也背诵的滚瓜烂熟,但是一问到源码脑子就....瓦特了!所以最近准备从头开始学习源码,学习大神们优秀的思想!

本文是对Handler机制的源码分析,目的是为了能够从源码角度一点点的理解Handler机制,里面会出现大量的源码,所以会比较枯燥,但是只要认真看完,相信你一定会对Handler机制的实现方法有更加清晰的认识 Handler是用起来非常简单!

private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //处理接收到的消息
        }
    };

初始化之后,在子线程进行完耗时操作之后,使用

 handler.sendEmptyMessage(what)

好了,现在就从sendEmptyMessage 方法开始,一步步的解析handler整个工作流程 — — 注意,Handler开始向消息队列发送消息了;

点进去之后,我们会发现,sendEmptyMessage 、sendMessage 最终都是在调用 sendMessageAtTime 方法,将发送的消息放入messgeQueue;需要注意的一点是,sendMessageDelayed方法中,已经将 delayMillis 延迟时间转换成了 SystemClock.uptimeMillis() + delayMillis,指的是该消息被取出来执行的时间,这一点会在MessageQueue中显的比较重要

    //直接调用 sendEmptyMessageDelayed 方法
    public final boolean sendEmptyMessage(int what){
        //直接调用 sendEmptyMessageDelayed 方法
        return sendEmptyMessageDelayed(what, 0);
    }

// 被sendEmptyMessage方法调用,delayMillis 为0,同时将参数转换成message后调用 sendMessageDelayed 此处已经和 sendMessage方法调用同一个路径了
    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
      //将参数进行复制,转换成 Message 
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }

    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
      //此处的 SystemClock.uptimeMillis() + delayMillis 用来计算消息的执行时间
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

   //最终,发送消息的方法都会走到这里,将消息放入MessageQueue 
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
      //mQueue 是从哪里来的?此处先放下,待会回头来分析
        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);
    }

在sendMessageAtTime 方法中,消息放入了我们熟知的 handler机制中的MessageQueue;现在我们分析 sendMessageAtTime 方法,这个方法中主要执行了一个 MessageQueue 的非空判断,然后就直接执行了enqueueMessage方法,mQueue 是从哪里来的呢?为了逻辑的连贯性,此处先不分析,会放到下面进行分析,这里先接着enqueueMessage进行分析;

  private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
      //将此Hanlder赋值给Message,用来在分发消息时候接收消息的hanlder
        msg.target = this;
        if (mAsynchronous) {//是否是异步,此处为false,不会执行
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

enqueueMessage方法也很简单,主要就是将此Hanlder赋值给Message,用来在分发消息时候接收消息的hanlder,然后将消息交给messageQueue来处理; 好了,到这里,我们开始进入messageQueue 了-- 当然,并不是Handler源码已经分析完毕了,只是我按照代码的脚步一步一步的走下去,目的是为了让自己能清除的看到Handler机制走过的所有道路,以便于最终能够完整、清晰的理解Handler消息机制,最后还是会回到Handler中的;

现在,我们进入MessageQueue — — 注意:Handler发送的消息,要进入消息队列了;

直接找到 Handler中 enqueueMessage方法里面执行的MessageQueue方法, enqueueMessage(Message msg, long when);

 boolean enqueueMessage(Message msg, long when) {
     
// msg.target ,这个参数是不是很熟悉,对的,就是在Handler进入MessageQueue之前的enqueueMessage方法中赋值的,是当前的Handler
       if (msg.target == null) {
           throw new IllegalArgumentException("Message must have a target.");
       }
       if (msg.isInUse()) {//此处判断当前Message是否是被执行了,一个消息不能被执行使用两次
           throw new IllegalStateException(msg + " This message is already in use.");
       }
synchronized (this) {
           if (mQuitting) {//mQuitting默认一直是false,只有执行quit 方法,并且该MessageQueue能被安全退出的时候回被赋值为true,主线程中是不能被退出的,所以一直都是false,因此不会被执行进来,直接跳过去
               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;
         //此处进行判断,如果mMessages为Null(p == null),则表示现在消息队列里面已经没有消息缓存了,可以直接放入消息队列的最顶端,同时唤醒正在阻塞的消息队列
         //when==0,表示该消息需要被立即执行
        //如果mMessages!=null,表示目前消息队列里面已经有消息了,此时比较消息队列里最顶端要被取出来的消息使用时间,如果when < p.when,表示新传入的消息执行的时间在消息队列中最靠前,所以也放到顶端(注意:mMessages 是一个链表结构,且mMessages是该链表中最顶端的消息)
           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 {
               //如果不满足以上三个条件,就把该消息放到队列里对应的位置

//是否需要唤醒此时的阻塞
//msg.isAsynchronous();这个是不是很熟悉?没错,就是在Handler的enqueueMessage方法中,有这么一行代码: msg.setAsynchronous(true);在这里可以看到,如果message是Asynchronous的话,才会需要唤醒阻塞的线程
             needWake = mBlocked && p.target == null && msg.isAsynchronous();
               Message prev;
       
     //下面一个循环,将传入进来的消息根据执行的时间 when 插入到消息队列中指定的位置
               for (;;) {
                   prev = p;
                   p = p.next;
                   if (p == null || when < p.when) {
//此处,(when < p.when)表示最新传入进来等待插入消息队列的消息执行时间早于当前这个消息的执行时间,则将最新传入的消息插入这个位置,
//(p==null)当队列中的消息已经遍历完成,每个消息的执行时间都早于最新传入的消息,那就把这个消息放到最后
                       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;
   }

消息已进入队列,开始等待被取出消费了 — — 注意,Looper开始表演了;

到这里,消息队列的插入已经完成了,根据我们熟知的Handler机制,这时候就要开始等待队列中的消息被取出来执行了;但是消息是在哪里被取出来进行执行的呢,到这里好像代码已经断掉了,根本没有找到下一步调用的方法啊! 别着急,Hanlder的使用除了sendEmptyMessage的调用,还有初始化操作啊,我们点进去之后,发现最终会执行到这个构造方法;

 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 被赋值,Hanlder和MessageQueue关联起来了
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

还记得我再Handler中为了不打断逻辑连贯性忽略掉的那一行代码吗?

   MessageQueue queue = mQueue;

这个MessageQueue 是从哪里来的呢,在这里我们已经找到了赋值地方,在构造方法中,mQueue被赋值,并且来自Looper,这里,已经将Hanlder、MessageQueue、Looper关联起来了;

现在开始进入Looper了; Looper本身是一个轮询器,用来从消息队列中取出消息;我们知道,Handler是在主线程进行初始化并执行的,因此,在Handler构造方法中的代码:

 mLooper = Looper.myLooper();
 mQueue = mLooper.mQueue;
 mCallback = callback;
 mAsynchronous = async;

我们打开 Looper.myLooper(),发现只有一行代码,mLooper 来自ThreadLocal;并且在在sThreadLocal 上发现 是在prepare()中进行的初始化

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

我们来看一下prepare()方法;

   /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    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));
    }

    /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

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

好了,通过这三个方法,我们知道 prepare()和loop()方法是同步出现的,并且prepare()中进行了Looper的初始化,此时也找到了MessageQueue的初始化;

看到这里,对于不熟悉Looper和Handler机制的童鞋来说,Handler里面也没有调用初始化方法啊!!!!

此处线索已掉线,大家散了吧!

恩,我再想想办法,串起来!

Handler本身是在主线程中初始化的,并且ThreadLocal只能在当前线程中才能获取到里面的信息,所以初始化操作肯定是在 主线程完成的;因此,我们在ActivityThread 的main方法中,找到了Looper的初始化和启动; 还有一个方法prepareMainLooper(),这个方法同样是Looper的初始化方法,并且作为Application的主线程中的Looper使用,并且建议我们自己永远不要调用; ok,这个意思大概就是我们能在主线程中找到初始化方法prepareMainLooper()吧。。。。 最后,确实是在ActivityThread 的main方法中找到了Looper的初始化操作;

 public static void main(String[] args) {
  //省略掉部分不相关代码
      //.........
        Looper.prepareMainLooper();
        //..........
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

到这里,Looper终于开始启动循环了;

喜大普奔,来看看loop是怎么循环的吧

  
    public static void loop() {
    //此处确保已经初始化,调用过prepare方法
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

      //此处开始大家熟知的死循环,取出消息队列中的消息
        for (;;) {
          //第一步就是从MessageQueue中取出最顶端的消息,next()方法可能会被阻塞
            Message msg = queue.next(); // might block
            //如果取出消息为空,就结束循环
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
        //仅仅打印log,跳过
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;

      //到这里,开始对取出来的消息进行分发
    // msg.target 这个是不是很熟悉,没错,就是Handler,在Handler的enqueueMessage方法中进行的赋值,目的就是用来判断当前消息处理的Hanlder,在此处终于显示出来他的作用了,没错,就是调用了Hanlder的dispatchMessage方法,在此处,所有的流程终于又回到了Handler,分发完成之后,loop()方法的代码对于Hanlder已经不是很重要了,可以直接跳过不看
            try {
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (slowDispatchThresholdMs > 0) {
                final long time = end - start;
                if (time > slowDispatchThresholdMs) {
                    Slog.w(TAG, "Dispatch took " + time + "ms on "
                            + Thread.currentThread().getName() + ", h=" +
                            msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                }
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }

现在,我们重新回到Handler

绕了半天,终于回来了 赶紧看看,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);
        }
    }

handleMessage(msg);方法终于出现了有木有,这个就是我们处里Handler方法的地方;到此,终于把Handler机制的源码过了一遍!

再补充一下:

在Looper.loop()方法中,从消息列表MessageQueue中取出Message的方法 queue.next()是一个可阻塞的方法,阻塞也是

Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

          //一个navtie方法,nextPollTimeoutMillis表示阻塞时间,0阻塞,-1代表意义阻塞,直到被唤醒,在消息队列里面没有消息的时候,就会一直阻塞下去,直到被唤醒,这也是loop方法不会执行完的原因,因为在消息为空的时候就被阻塞在这里了,直到有消息进来唤醒线程,调用nativeWake()唤醒线程才会继续走下去
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // 次数开始获取下一条Message,如果有就返回
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // 通过循环,返回当前处于队列最底部,需要被取出来的消息
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                
                if (msg != null) {
                    //msg.when表示当前消息需要被执行的时间,now < msg.when,表示当前时间还没到队列底部部(最后一条)消息被执行的时间
                  //队列执行是先进先出,所以此时的消息位于队列的最底部,会被首先被取出来的,这个位置我统一称为底部(方便理解)
                    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 {
                        // 如果此时消息应该被立即执行
                        mBlocked = false;
                        if (prevMsg != null) {
//如果消息队列不仅仅有一个消息,则此时将倒数第二条消息的下一条消息只想meg.next(是null)
                            prevMsg.next = msg.next;
                        } else {
                          //此时表示仅有一条消息,被取出来后mMessages 就为null
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                //此时返回被取出来的消息,next()方法结束
                        return msg;
                    }
                } else {
                    // 如果此时消息队列里面已经没有消息了,则等待时间更为-1
                    nextPollTimeoutMillis = -1;
                }

                // 是否是退出状态,主线程里面不会有退出,mQuitting 恒为false,不会执行
                if (mQuitting) {
                    dispose();
                    return null;
                }


             //到这里正常的Message消息已经结束了,但是google大大巧妙的设计了另一种消息类型,IdleHandler,这种消息类型并不会定义执行时间,而是会在Message 消息阻塞时,利用阻塞的空闲时间来执行
                // 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.
            //此处获取IdleHandler的数量;
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
            //如果没有IdleHandler消息,则继续等待,不再向下执行
                if (pendingIdleHandlerCount <= 0) {
                    // 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.
         //如果有IdleHandler消息,则取出来执行了,并移出已经执行的消息
            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(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;

          //立即执行 IdleHandler   不再阻塞线程
            nextPollTimeoutMillis = 0;
        }
    }

最后总结一下

Handler机制到这里已经完全串联起来了,整个流程最后总结起来其实确实很简单: 1、ActivityThread中,初始化Looper,Looper初始化时候会创建MessageQueue;Looper初始化完成之后调用loop()开启死循环,不断取出MessageQueue中的消息,并分发出去 2、Handler初始化后,将Looper,MessageQueue、Handler关联起来,并等待接受来自MessageQueue中的消息 3、Handler通过send相关方法,发送消息到MessageQueue,MessageQueue通过Message信息,将Message放到队列中相应位置,等待被取出使用; 4、Looper取出消息,并根据Message中的信息分发出去,给相应的Handler使用,此时Hanlder接收到消息,我们开始处理消息,进行更新UI等主线程的操作