Android Handler学习笔记(一)

279 阅读20分钟

 这篇笔记主要参考了以下博客的文章:

Android Handler机制
Android 消息机制之MessageQueue

Handler通信机制的工作原理

Handler的作用

    为什么使用Handler?

  1. 在Android中,如果在主线程中进行耗时操作,则容易出现ANR,新版本的Android系统中,如果在主线程中进行耗时操作,还会抛出异常。因此需要将耗时操作放在子线程中进行。
  2. 在子线程中进行完耗时操作之后,如果需要对UI进行修改,则需要使用Handler将需要更新UI的操作包装成一个Message发送到主线程,然后在主线程中进行更新UI的操作。

    在上面的描述中,涉及到一些问题,如下:

  1. ANR:Application Not Responding,出现这个问题之后APP会弹出提示框询问用户继续等待或者结束应用。一般情况下在主线程进行耗时操作会出现这个问题。
  2. 对于一个子线程来说,在需要操作UI的时候才会用到Handler,如果子线程不用操作UI,那么就不需要Handler,子线程执行完任务结束就可以了。
  3. 主线程:也就是UI线程,在启用APP的时候会默认创建。

    综上所示,Handler一般用于多线程中,将工作线程中需要更新UI的操作信息传递到主线程中,从而实现工作线程对UI的更新处理,最终实现异步消息的处理。使用Handler的目的,也是为了在多个线程并发更新UI的同时,保证线程安全。

相关概念

    和Handler相关的概念主要包括Handler,Message,Message Queue,Looper,相关概念如下:

概念 定义 作用 备注
主线程 当应用程序第一次启动的时候,会同时自动开启一条主线程 处理和UI相关的事件 主线程和子线程通信的媒介=Handler
子线程 人为手动开启的线程 执行耗时操作(如网络请求等) 与主线程通过Handler通信
Message 线程间通信的数据单元 存储需操作的通信信息 一般在子线程中创建
Message Queue 一种数据结构(先进先出) 存储Handler发送过来的消息 存在于主线程中
处理者(Handler) 主线程与子线程通信的媒介,线程消息的主要处理者 添加消息(Message)到消息队列(Message Queue);
处理循环器(Lopper)分派过来的消息(Message)
/
循环器(Looper) 消息队列(Message Queue)与处理者(Handler)的通信媒介 负责消息循环,也就是:
1.消息获取:循环取出消息队列(Message Queue)的消息(Message);
2.消息分发:将取出的消息(Message)发送给对应的处理者(Handler)
  • 每个线程中只能拥有一个Looper
  • 1个Looper可绑定多个线程的Handler
  • 即多个线程可往一个Looper所持有的Message Queue中发送消息,提供了线程间通信的可能。
  • 使用方式

        Handler的使用方式因发送消息到消息队列的方式不同而不同,共分为两种:

    1. Handler.sendMessage(Message)
    2. Handler.post(Runnable)

    工作原理

        Handler机制的工作流程主要包括四个步骤:

    • 异步通信准备
    • 消息发送
    • 消息循环
    • 消息处理

        具体如下表所示:

    步骤 具体描述 备注
    1.异步通信准备 在主线程中创建:
  • 循环器对象(Looper)
  • 消息队列对象(Message Queue)
  • Handler对象
  • Looper,MessageQueue均属于主线程
  • 创建Message Queue后,Looper则自动进入消息循环体
  • 此时,Handler自动绑定了主线程的Looper,Message Queue
  • 2.消息入队 工作线程(子线程)通过Handler发送消息(Message)到消息队列(Message Queue)中 该消息内容 = 工作线程对UI的操作
    3.消息循环
  • 消息出队:Looper循环取出消息队列(MessageQueue)中的消息(Message)
  • 消息分发:Looper将取出的消息(Message)分发给创建该消息的处理者(Handler)
  • 在消息循环过程中,若消息队列为空,则线程阻塞
    4.消息处理
  • 处理者(Handler)接收循环器(Looper)发送过来的消息(Message)
  • 处理者(Handler)根据消息(Message)进行UI操作
  • /

        Handler的工作流程如下图所示:

    Handler的工作流程

        Handler的工作流程示意图如下图所示:

    Handler工作流程示意图

    Handler机制的核心类

        Handler机制中有三个重要的类,分别是:

    1. 处理器 类(Handler)
    2. 消息队列 类(Message Queue)
    3. 循环器 类(Looper)

        三个重要类的类图如下所示:

        三个重要的类的具体介绍如下图所示:

    小结

        至此,我们已经了解了Handler的基本运行机制:

    当一个应用启动的时候,会同时创建一个主线程或称为UI线程,在这个线程中会同同时创建Message QueueLooper。当我们需要在子线程中进行某一项工作,并且工作完成之后需要操作UI的时候,我们会创建一个Handler用于接收子线程操作UI的信息。

    执行完上面的操作之后,我们通常会开启一个子线程,为了能够操作UI,我们需要将上一步创建的Handler对象传递到子线程中,当子线程执行完操作之后,会将需要操作UI的信息包装成一个Message对象,通过Handler.sendMessage()方法将Message添加到Message Queue中。

    由于Looper是一个死循环,当有消息进入到Message Queue中的时候,会立即从其中取出消息,然后就开始分发消息开始执行。这样就完成了从子线程向主线程中传递消息的操作。

        但是到这里,仍然有几个问题需要解决:

    1. 主线程何时创建?
    2. 主线程中何时创建LooperMessage Queue?如何创建?
    3. Looper如何自动进入消息循环体中?
    4. Handler如何实现和LooperMessage Queue的自动绑定?
    5. 消息循环过程中,如果消息队列为空,则线程阻塞,那岂不是主线程就阻塞了?
    6. Message传递消息的时候有什么限制?

    源码分析

        Handler发送消息分为两种方式,分别是Handler.sendMessage()Handler.post(),下面根据不同方式进行源码分析:

    使用Handler.sendMessage()

      1.使用步骤

    //创建Handler
    private Handler mHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                if(msg.what == 1){
                    mBinding.tvResult.setText(msg.obj.toString());
                }
            }
        };
    //创建消息对象
    Message message = Message.obtain();
    message.what = 0;
    message.obj = this.getClass().getSimpleName();
    
    //在子线程中发送消息
    mThreadHandler.sendMessage(message);
    
    //这里的子线程我通过继承Thread实现
    class TestThread extends Thread{
        private Handler mThreadHandler;
    
        TestThread(Handler handler){
            this.mThreadHandler = handler;
        }
        @Override
        public void run() {
            super.run();
            Message message = Message.obtain();
            message.what = 0;
            message.obj = this.getClass().getSimpleName();
            mThreadHandler.sendMessage(message);
        }
    }
    

    步骤1 在主线程中通过匿名内部类创建 Handler对象

     在上面的示例中,通过匿名内部类的方式创建了Handler对象,首先会执行Handler无参的构造方法,源码如下:

        public Handler() {
            this(null, false);
        }
    

     可以看到,在无参的构造方法中,调用了Handler(callback:null,async:false)的构造方法,源码如下:

        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 " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    

     在上面的源码中,首先我们需要判断FIND_POTENTIAL_LEAKS的值是否为true,这是一个静态变量,默认为false,从名字上也可以理解,这个变量就是用来标记是否要发现隐藏的内存泄露。

     那么如果这个值为true,它的判断方法为:

    • 首先通过getClass()获取到这个类运行时真实的Class信息
    • 然后通过isAnonymousClass(),isMemberClass(),isLocalClass()判断这个类是否是匿名类(就像 new OnClickListener()这种就是匿名类,上面定义Handler的时候也是用的是匿名类),或者是否是成员类(定义在类中类),亦或者是否是局部类(定义在方法中的类)。
    • 如果是上面任意一种类,那么接下来通过kclass.getModifiers() & Modifirt.STATIC来查看当前类是否使用static修饰(getModifiers()可以获取到类的修饰符,使用整数表示,Modifier.STATIC的二进制数为1000,做与运算,只有getModifiers()返回的整数的二进制数的最高位为1,最后的结果才不会为0)。
    • 满足上面的条件则会提示"The following Handler class should be static or leaks might occur"
       接下来通过Looper.myLooper()绑定当前线程的Looper,然后判断,如果Looper为空则会抛出异常。

    Looper.myLooper()方法的源码如下:

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

    Looper中的sThreadLocal定义如下:

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

     从注释也可以看出,只有调用了prepare()方法之后,sThreadLocal.get()才不会返回null

     查看Looper中的源码也可以发现,Looper只有一个private的构造方法,也就是说,Looper只能通过prepare()方法进行初始化,源码如下:

        //Looper构造方法
        private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
        }
        
        //prepare()方法
            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();
            }
        }
    

     从上面的源码可以看出,Looper的主要作用就是将当前线程初始化为循环线程,通过prepareMainLooper()方法可以将当前线程标记为应用程序的主循环程序,主循环程序是由Android环境创建的,自己创建的话可以使用prepare()函数。

     由此,我们可以得出以下结论:

    1. 当一个应用程序启动时,会启动一个线程,这个线程是主线程,同时会通过调用Looper中的prepareMainLooper()方法将当前线程标记为一个不可以退出的主循环程序。
    2. 在标记的过程中,通过判断ThreadLocal sThreadLocalget()方法是否为空来使得一个线程中只能有一个Looper,如果sThreadLocal.get()方法返回空,那么就通过Looperprivate的构造方法创建一个Looper,并将创建的对象设置给sThreadLocal中的ThreadLocalMap进行保存。
    3. 在调用Looper的构造方法的时候会创建MessageQueue的对象,此时就已经创建好了LooperMessageQueue了。
    4. 当我们需要通过Handler进行子线程向主线程传递数据的时候,我们就可以直接在主线程中创建Handler,然后在Handler的构造方法当中设置当前线程的LooperMessageQueue对象。然后将Handler的实例传递给子线程(在外部创建的子线程需要通过这种方式),这样在子线程中的数据处理完成之后我们就就可以调用Handler的方法向MessageQueue中添加Message了。

     下面是Android APP启动时的部分源码:

    public static void main(String[] args) {
                ... // 仅贴出关键代码
    
                Looper.prepareMainLooper(); 
                // 1. 为主线程创建1个Looper对象,同时生成1个消息队列对象(MessageQueue)
                // 方法逻辑类似Looper.prepare()
                // 注:prepare():为子线程中创建1个Looper对象
                
                
                ActivityThread thread = new ActivityThread(); 
                // 2. 创建主线程
    
                Looper.loop(); 
                // 3. 自动开启 消息循环 ->>下面将详细分析
    
            }
    

    步骤一前的隐式操作:消息循环

     通过上面的分析,我们已经清楚了当APP启动的时候,系统会帮我们自动创建一个主线程,同时生成LooperMessageQueue对象,有了这两个对象之后,Looper就可以调用loop()方法进行消息循环了,下面是loop()方法的源码:

       public static void loop() {
            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();
    
            // Allow overriding a threshold with a system prop. e.g.
            // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
            final int thresholdOverride =
                    SystemProperties.getInt("log.looper."
                            + Process.myUid() + "."
                            + Thread.currentThread().getName()
                            + ".slow", 0);
    
            boolean slowDeliveryDetected = false;
    
            for (;;) {
                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
                final Printer logging = me.mLogging;
                if (logging != null) {
                    logging.println(">>>>> Dispatching to " + msg.target + " " +
                            msg.callback + ": " + msg.what);
                }
    
                final long traceTag = me.mTraceTag;
                long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
                long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
                if (thresholdOverride > 0) {
                    slowDispatchThresholdMs = thresholdOverride;
                    slowDeliveryThresholdMs = thresholdOverride;
                }
                final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
                final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
    
                final boolean needStartTime = logSlowDelivery || logSlowDispatch;
                final boolean needEndTime = logSlowDispatch;
    
                if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                    Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
                }
    
                final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
                final long dispatchEnd;
                try {
                    msg.target.dispatchMessage(msg);
                    dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
                if (logSlowDelivery) {
                    if (slowDeliveryDetected) {
                        if ((dispatchStart - msg.when) <= 10) {
                            Slog.w(TAG, "Drained");
                            slowDeliveryDetected = false;
                        }
                    } else {
                        if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                                msg)) {
                            // Once we write a slow delivery log, suppress until the queue drains.
                            slowDeliveryDetected = true;
                        }
                    }
                }
                if (logSlowDispatch) {
                    showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
                }
    
                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();
            }
        }
    
    

     在上面的方法中,首先会判断当前线程的Looper对象是否存在,不存在则会抛出异常。也就是说在执行loop()前必须执行prepare()方法,为此线程创建Looper,否则会抛出异常。

     接下来的两行代码通过注释可以知道,是确保此线程的身份是本地进程的身份,并跟踪该身份令牌的实际含义,这两行代码的实际意义并不是很懂,需要后面解决。

     接下来是一个for循环,在for循环中,首先通过queue.next()方法获取一个Message对象,注释中也说明了这个方法可能会阻塞。如果这个Message为空,那么就会跳出循环。如果这个Message不为空,那么就会执行msg.target.diapatchMessage(msg)方法,将这个Message分发给对应的Handler去处理。之后调用msg.recycleUnchecked()方法释放msg

    MessageQueue中获取Message的操作

     通过上面的源码可以发现,Looper中是通过MessageQueue中的next()方法来获取下一个需要操作的Message,下面是MessageQueue中的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();
                }
    
                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;
                    }
    
                    // 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)) {
                        pendingIdleHandlerCount = mIdleHandlers.size();
                    }
                    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.
                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;
    
                // 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;
            }
        }
    

     上面的注释已经说明:如果消息循环已经退出并且被释放,那么就直接返回null,这种情况一般发生在应用程序在退出后仍然尝试重新启动不受支持的循环。结合代码中ptr == 0的时候返回null可以知道,当ptr == 0的时候表示消息循环已经退出并且被释放掉了。 mPtr的作用是保存 native层的MessageQueue的引用地址。

     接着定义pendingIdleHandlerCount = -1;表示空闲事件处理IdleHandler的数量为-1,即还没有处理过任何的IdleHandler。然后定义nextPollTimeoutMillis = 0

     接着同样进入一个for死循环,这时会调用nativePollOnce(ptr, nextPollTimeoutMillis);,这个方法额的主要作用是设置线程等待多久之后唤醒,时间由nextPollTimeoutMillis来指定。

     接下来进入到同步方法中,正式开始获取Message。具体操作步骤如下:

    1. 获取MessageQueue中的第一个Message对象,也就是mMessages,如果mMessages不为空,并且mMessagestarget属性为null则进入如下循环:
    do {
        prevMsg = msg;
        msg = msg.next;
    } while (msg != null && !msg.isAsynchronous());
    

     在上面的循环中,可以看出,就是从第二个Message开始获取,如果获取到的Message不为空并且不是异步操作的Message则继续循环,也就是说,这个循环条件是获取到第一个是异步操作的Message或者循环完成则跳出循环。

     在这里,首先要解释一下Message的三种状态,分别是:

    • 障栈,这种状态是Messagetarget的属性为空
    • 同步,这种状态就是在设置Message时候设置为同步属性
    • 异步,这种状态就是在设置Message时设置为异步属性

     同步和异步属性的差距主要在于障栈会阻碍同步Message的执行,而不会阻碍异步Message的执行,这里并不意味着异步的Message会在异步线程执行。

    1. 继续返回到MessageQueue中的代码中,经过第一步的运行,我们拿到的Message可能是以下情况:
    • null,MessageQueue中已经没有待处理的Message,或者障栈之后没有异步的Message
    • 同步的Message,MessageQueue中的第一个Message不是障栈,那次是这里就是第一个Message
    • 异步的MessageMessageQueue中的第一个Message时障栈,并且在之后的Message中找到了第一个异步的Message

     接着进入到下面的判断中:

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

     在上面的源码中,首先判断获取的Message是否为空,不为空则继续执行如下的操作:

    • 判断是否已经到达当前Message的执行时间(通过nowmsg.when)进行判断,如果没有到达当前线程的执行时间,则设置线程需要阻塞的时间,这个时间最大为int的最大值。
    • 如果已经达到了当前Message执行的时间,那么就设置mBlocked = false,也就是不需要阻塞,然后判断prevMsg是否为空,不为空则说明当前MessageQueue的第一个Message是障栈,此时prevMsg是障栈之后第一个异步Message之前的msg,这样将prevMsgnext指向当前Message的下一个Message即可。如果prevMsg为空,那么就直接设置MessageQueue的第一个Message为当前Message的下一个Message即可。然后设置当前Messagenext为空,这样就将当前的MessageMessageQueue中脱离出来了。然后标记当前的Message正在使用中,并返回。
    1. 当前Message为空,设置nextPollTimeoutMillis = -1;表示没有更多的Message了。
    2. 接着,当没有更多的Message需要处理,或者当前的Message还没有到执行时间的话,会查看是否有退出的消息需要处理,如果有,就执行退出的消息:
    // Process the quit message now that all pending messages have been handled.
    if (mQuitting) {
        dispose();
        return null;
    }
    private void dispose() {
        if (mPtr != 0) {
            nativeDestroy(mPtr);
            mPtr = 0;
        }
    }
    

     从上面的dispose()方法中也可以看出,当MessageQueue退出之后,mPtr的值会被设置为0,表示当前MessageQueue已经退出。

    1. 如果MessageQueue不需要退出,那么判断当前MessageQueue是否是第一次空闲,判断的依据是pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when),pendingIdleHandlerCount的初始赋值为-1,并且MessageQueue中没有要处理的Message,或者当前要处理的Message的时间还没有到,那么此时就获取需要处理的IdleHandler的数量.代码如下:
    if (pendingIdleHandlerCount < 0
            && (mMessages == null || now < mMessages.when)) {
        endingIdleHandlerCount = mIdleHandlers.size();
    }
    
    1. 接着判断是否有需要处理的IdleHandler,如果没有则设置需要阻塞(因为程序执行到这里,说明已经没有待处理的Message,或者当前需要处理的Message的时间还没到,所以线程需要阻塞,等待下一个事件到来),然后跳出循环,开始执行下一次循环。代码如下:
    if (pendingIdleHandlerCount <= 0) {
        // No idle handlers to run.  Loop and wait some more.
        mBlocked = true;
        continue;
    }
    
    1. 接着判断需要处理的IdleHandlers数组是否为空,为空则会创建一个数组,数组的最大值为需要处理的IdleHandler的数量和4中较大的一个。然后将存放需要处理的IdleHandler的列表中的数据复制到数组中。
    if (mPendingIdleHandlers == null) {
        mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
    }
    mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
    
    1. 接下来就会通过循环遍历数组中的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);
            }
        }
    }
    

     可以看到具体的执行过程也是每次从数组中取出一个IdleHandler,然后执行其中的queueIdle()方法,最后执行完成后根据返回的结果,如果执行完成后不需要继续将这个IdleHandler保持起来,那么就从列表中移除它,否则不移除。

    1. 接下来重新赋值pendingIdleHandlerCountnextPollTimeoutMillis的值。

     设置pendingIdleHandlerCount = 0主要是为了保证IdleHandler只会执行一次。而之所以要设置nextPollTimeoutMillis = 0是为了防止在执行IdleHandler的时候添加了新的信息,这样是为了能够立即再次查找MessageQueue中的Message(之所以需要这样是因为在上面获取Message的时候,需要执行的那个Message的时间还没有到,所以我们在那里设置了需要等待的时间,也就是对nextPollTimeoutMillis进行了赋值,但是后面执行IdleHandler时需要时间的,如果这里不设置nextPollTimeoutMillis = 0,那么下次循环的时候就会调用nativePollOnce(ptr, nextPollTimeoutMillis);方法设置阻塞的时间,这样的话,一旦在我们执行IdleHandler的时候有新的需要立即执行的Message插入进来而没有重置nextPollTimeoutMillis = 0,那么下次循环的时候还是按照之前设置的时间阻塞,那么新加进来的Message就没法立即执行。)。

    返回Looper中的loop()方法

     前面已经了解了loop()方法的大体流程,下面再进入到其中的循环中查看代码:

    1. 首先通过MessageQueuenext()方法获取需要执行的Message,如果获取到的Message为空,则结束循环:
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        
        ···其它代码···
    

     通过上面的MessageQueue中的代码,我们已经了解了,在MessageQueue中的next()方法中,只有MessageQueue已经退出才会返回null,但是我们之前也了解过,主线程中创建的MessageQueue是不允许退出的,也就是说正常情况下,主线程中的MessageQueuenext()方法是不会返回null的,从而说明Looper中的loop()方法中的for()循环在正常情况下不会退出。

    1. 接下来的代码基本在做一些打印日志的操作,比较重要的则是在try...catch中执行的操作,在这里会将Message分发给具体的Handler去进行处理,代码如下:
    try {
        msg.target.dispatchMessage(msg);
        dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
        if (traceTag != 0) {
            Trace.traceEnd(traceTag);                }
        }
    
    1. 执行完成之后则会回收当前的Message
    msg.recycleUnchecked();