Android Handler学习笔记(一)

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

Android Handler机制
Android 消息机制之MessageQueueshell

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的同时,保证线程安全。bash

相关概念

    和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的工做流程以下图所示:app

    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();
    复制代码
    相关文章
    相关标签/搜索