美文网首页
关于Handler的学习

关于Handler的学习

作者: 36f872af9773 | 来源:发表于2020-07-08 14:39 被阅读0次

    声明:个人笔记,无参考学习价值

    1.为什么不能在子线程中创建Handler

     public Handler(@Nullable 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());
                }
            }
    //1.在Handler的构造函数里面,会调用Looper.myLooper(),来获取Looper对象,如果Looper对象为null,就会抛出异常
    //Handler需绑定 线程才能使用;绑定后,Handler的消息处理会在绑定的线程中执行, 先指定Looper对象,从而绑定了 Looper对象所绑定的线程(因为Looper对象本已绑定了对应线程)
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
            }
      //Looper对象,是和消息队列绑定的可以在Looper()的构造参数中看到消息队列的构建
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
    至此,Handler的构造参数中,指定了Looper对象,从而也就绑定了线程和消息队列
    ---------------------------------------------------------------------------------------------------------------------
      private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
        }
        }
    

    下面看一看Looper.myLooper(),我们跳转到Looper类中:

     /**
         * Return the Looper object associated with the current thread.  Returns
         * null if the calling thread is not associated with a Looper.
         */
    //返回与当前线程关联的Looper对象,如果正在调用的线程没有关联一个Looper,那么返回null
    //拿到Looper对象,就等于绑定了线程
        public static @Nullable Looper myLooper() {
    //我们再去看一下sThreadLocal
            return sThreadLocal.get();
        }
    

    除非你已调用了prepare(),否则sThreadLocal.get()将会返回null

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

    来看下prepare()

     public static void prepare() {
            prepare(true);
        }
    
        private static void prepare(boolean quitAllowed) {
           //如果sThreadLocal.get() != null,根据上面的代码可以知道,不为null,说明了已经调用了prepare,那么就不能再次重复调用了,所以抛出异常
            if (sThreadLocal.get() != null) {
                throw new RuntimeException("Only one Looper may be created per thread");
            }
    //,然后调用这句创建Looper
            sThreadLocal.set(new Looper(quitAllowed));
        }
    
    小结: 那么看到这里,就稍微有点眉目了,要调用Handler对象,就必须拿到Looper对象,要拿到Looper对象呢,又必须调用prepare()方法,这也就印证在在Handler构造参数中的这句抛出的异常 "Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()":
      mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
            }
    

    我们没有手动调用prepare().那就是说我们需要在子线程中的代码里手动调用了

    Looper.prepare()
    

    2.那么问题来了,我们在主线程中调用Handler也没有去调用Looper.prepare()呀?

    首先看一下在主线程中Looper调用的方法:

        /**
         * 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()}
         */
    //皇家塑料翻译:
    //将当前的线程初始化为一个looper对象,并将它标记为应用的主looper.这个应用的主looper已经在android环境被创建,因此你绝对不需要自己去调用这个函数
        public static void prepareMainLooper() {
            //prepare(false)这一句稍后分析
            prepare(false);
            synchronized (Looper.class) {
    //这个判断和prepare()的一样,只能创建一个
                if (sMainLooper != null) {
                    throw new IllegalStateException("The main Looper has already been prepared.");
                }
    //这里和prepare()不一样,直接从myLooper()中自信拿到sMainLooper,而且之前的myLooper()我们也看过, return sThreadLocal.get(); 那么也就是说必然会返回一个sMainLooper对象
                sMainLooper = myLooper();
            }
        }
    

    那么我们再把myLooper()源码上的注解塑料翻译一遍,懂得自行跳过:

      /**
         * Return the Looper object associated with the current thread.  Returns
         * null if the calling thread is not associated with a Looper.
         */
    返回一个和当前线程关联的looper对象,如果调用的线程没有关联一个Looper返回null
    我们知道,一个android程序中默认会有一个主线程
    直接看 Return the Looper object associated with the current thread,这个current thread在prepareMainLooper()中,当然是主线程了,
    所以它一定为我们做了一些事情,所以这里才会自信的直接调用 sMainLooper = myLooper();
    而子线程是我们自己创建的,它并没有关联一个Looper,所以需要手动去Looper.prepare()(禁止套娃,又回到上面去了)
    

    那么去看看主线程中怎么调用的吧 去ActivityThread中,有一个main()方法

        public static void main(String[] args) {
                  ....
            //在这里调用了prepareMainLooper(),那么这个方法中就调用了prepare(false),那么sThreadLocal.get()中必然有值,那么myLooper(),就必然有值,那么sMainLooper,就成功赋值拉
            Looper.prepareMainLooper();
    
           .....
    
            ActivityThread thread = new ActivityThread();
            thread.attach(false, startSeq);
    
            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }
    
            if (false) {
                Looper.myLooper().setMessageLogging(new
                        LogPrinter(Log.DEBUG, "ActivityThread"));
            }
              ....
    
            Looper.loop();
    
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }
    
    

    好了,再来说一下prepare(false),直接跟进源码 prepare(boolean quitAllowed)

    //quitAllowed这个boolean值就传进了Looper的构造参数
     sThreadLocal.set(new Looper(quitAllowed));
    
    private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
        }
    //传进了MessageQueue的构造参数
    MessageQueue(boolean quitAllowed) {
            mQuitAllowed = quitAllowed;
            mPtr = nativeInit();
        }
    并成为一个成员变量,供全局调用
    

    mQuitAllowed在MessageQueue中只有一次调用

     void quit(boolean safe) {
    //就是这里了, 如果设置了false,那么就抛出这个异常,表示prepareMainLooper()执行后,再执行quit,就会抛出"主线程不允许退出的错误"
            if (!mQuitAllowed) {
                throw new IllegalStateException("Main thread not allowed to quit.");
            }
    
            synchronized (this) {
                if (mQuitting) {
                    return;
                }
                mQuitting = true;
    
                if (safe) {
                    removeAllFutureMessagesLocked();
                } else {
                    removeAllMessagesLocked();
                }
    
                // We can assume mPtr != 0 because mQuitting was previously false.
                nativeWake(mPtr);
            }
        }
    

    顺带一看,至于quit(boolean safe),是在Looper的quit()和quitSafely()分别被调用,这两个方法都是退出looper的方法,不过更建议使用quitSafely()安全退出

     public void quit() {
            mQueue.quit(false);
        }
      /**
         * Quits the looper safely.
         * <p>
         * Causes the {@link #loop} method to terminate as soon as all remaining messages
         * in the message queue that are already due to be delivered have been handled.
         * However pending delayed messages with due times in the future will not be
         * delivered before the loop terminates.
         * </p><p>
         * Any attempt to post messages to the queue after the looper is asked to quit will fail.
         * For example, the {@link Handler#sendMessage(Message)} method will return false.
         * </p>
         */
    //在looper被要求退出之后,任何企图发送消息到队列的行为都将失败,举例:Handler的sendMessage(Message)将会返回失败
      public void quitSafely() {
            mQueue.quit(true);
        }
    

    3.既然说了Looper.prepare(),那么顺带也把Looper.loop()也说了,毕竟Looper要执行还得用这个方法

     /**
         * Run the message queue in this thread. Be sure to call
         * {@link #quit()} to end the loop.
         */
        public static void loop() {
            //拿Looper对象,没有的话就去调用Looper.prepare,就不用多说了
            final Looper me = myLooper();
            if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            }
        //有looper对象,就取出他的消息队列
            final MessageQueue queue = me.mQueue;
             .........
          //循环,取出消息
            for (;;) {
    //------->MessageQueue.next()单独再看
                Message msg = queue.next(); // might block
                if (msg == null) {
                    // No message indicates that the message queue is quitting. 
                    return;
                }
    
                   .................
                try {
    //        msg.target,从Message中可以看到是Handler对象,那么后面调用的就是Handler中的dispatchMessage()方法
                    msg.target.dispatchMessage(msg);
                    if (observer != null) {
                        observer.messageDispatched(token, msg);
                    }
                    dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
                } catch (Exception exception) {
                    if (observer != null) {
                        observer.dispatchingThrewException(token, msg, exception);
                    }
                    throw exception;
                } finally {
                    ThreadLocalWorkSource.restore(origWorkSource);
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
                  .......
                //msg进入Message对象回收复用的环节,可见下面第四点中的Message对象创建
                msg.recycleUnchecked();
            }
        }
    

    看下msg.target.dispatchMessage(msg),这里是我们处理消息中比较关心的内容

        /**
         * Handle system messages here.  
         */
        public void dispatchMessage(@NonNull Message msg) {
            if (msg.callback != null) 
    // 若msg.callback属性不为空,则代表使用了post(Runnable r)发送消息,则执行  handleCallback(msg),即回调Runnable对象里复写的run()
         handleCallback(msg);           
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);   //这个handleMessage(msg),就是我们在调用时复写的handleMessage,而处理UI的操作就是在这里完成的,他是一个空方法
            }
        }
    

    然后,说一下MessageQueue中的next()

      * 分析1:queue.next()
      * 定义:属于消息队列类(MessageQueue)中的方法
      * 作用:出队消息,即从 消息队列中 移出该消息
      */
      Message next() {
    
            ...
    
            // 该参数用于确定消息队列中是否还有消息
            // 从而决定消息队列应处于出队消息状态 or 等待状态
            int nextPollTimeoutMillis = 0;
    
            for (;;) {
                if (nextPollTimeoutMillis != 0) {
                    Binder.flushPendingCommands();
                }
    
            // nativePollOnce方法在native层,若是nextPollTimeoutMillis为-1,此时消息队列处于等待状态 
            nativePollOnce(ptr, nextPollTimeoutMillis);
    
            synchronized (this) {
         
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
    
                // 出队消息,即 从消息队列中取出消息:按创建Message对象的时间顺序
                if (msg != null) {
                    if (now < msg.when) {
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // 取出了消息
                        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 {
    
                    // 若 消息队列中已无消息,则将nextPollTimeoutMillis参数设为-1
                    // 下次循环时,消息队列则处于等待状态
                    nextPollTimeoutMillis = -1;
                }
    
                ......
            }
               .....
           }
    }//
    

    4.为什么使用Messag.obtain(),而不是new Message()来创建消息对象

    首先看源码中Message的构造方法 : 最好的方式是通过调用Message.obtain()来获取一个消息对象

      /** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
        */
        public Message() {
        }
    

    有八个不同参数的静态obtain()方法

     /**
         * Return a new Message instance from the global pool. Allows us to
         * avoid allocating new objects in many cases.
         */
        public static Message obtain() {
            synchronized (sPoolSync) {
                if (sPool != null) {
                    Message m = sPool;
                    sPool = m.next;
                    m.next = null;
                    m.flags = 0; // clear in-use flag
                    sPoolSize--;
                    return m;
                }
            }
            return new Message();
        }
    

    可以看到,obtain()时,先判断sPoo中不为null的情况下,从sPool中取出一个Message返回,达到复用的效果,如果sPool为null的情况下就new Message()来创建一个新的

    5. 那么来了解一下Message的Pool是怎么实现的

    来看一下Message中的这两个方法:
    首先recycle()的注释第一句什么意思,返回一个Message实例到全局的pool中

        /**
         * Return a Message instance to the global pool.
         * <p>
         * You MUST NOT touch the Message after calling this function because it has
         * effectively been freed.  It is an error to recycle a message that is currently
         * enqueued or that is in the process of being delivered to a Handler.
         * </p>
         */
        public void recycle() {
            if (isInUse()) {      //return ((flags & FLAG_IN_USE) == FLAG_IN_USE);  isInUser()返回一个判断标识符
                if (gCheckRecycle) {  //这个全局变量赋值为true,还有个判断是5.0系统一下的为false
                    throw new IllegalStateException("This message cannot be recycled because it "
                            + "is still in use.");      //这个消息不能被回收,因为他仍然在被使用
                }
                return;
            }
            recycleUnchecked();
        }
    
        /**
         * Recycles a Message that may be in-use.
         * Used internally by the MessageQueue and Looper when disposing of queued Messages.
         */
      // 回收一个可能正在被使用的Message    当处理排队的信息时,在消息队列和Looper内部调用
        @UnsupportedAppUsage
        void recycleUnchecked() {
            // Mark the message as in use while it remains in the recycled object pool.
            // Clear out all other details.
            flags = FLAG_IN_USE;
            what = 0;
            arg1 = 0;
            arg2 = 0;
            obj = null;
            replyTo = null;
            sendingUid = UID_NONE;
            workSourceUid = UID_NONE;
            when = 0;
            target = null;
            callback = null;
            data = null;
        //以上   flags = FLAG_IN_USE为确保消息不被回收,剩余的都是将消息置空
            synchronized (sPoolSync) {
    //如果池里的数量<设置的最大值50,sPool赋值给next,当前的消息对象赋值给sPool,sPoolSize用来记录数值
                if (sPoolSize < MAX_POOL_SIZE) {
                    next = sPool;
                    sPool = this;
                    sPoolSize++;
                }
            }
        }
    

    可以看出Message在调用recycle()回收消息的时候,对Message做出存储,当下次调用obtain()时,从sPool中取出复用

    6.那么Message的recycle()是什么时候调用的呢,不急,我们先看Handler的sendMessage(),毕竟创建的消息是要先干活的嘛

    通过sendMessage(msg) 方法,一路跟进了

     private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
                long uptimeMillis) {
          //将当前这个Handler对象赋值给msg.target
            msg.target = this;
            msg.workSourceUid = ThreadLocalWorkSource.getUid();
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
      //Message携带这三个参数,和uptimeMills被MessageQueue的enqueueMessage执行
    //而这个MessagQueue,如果你还记得之前Handler构造参数是在做什么操作,那你就应该知道,它是跟着Looper来的
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    

    接下来,去MessagQueue中去

     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;
    // 判断消息队列里有无消息
                            // a. 若无,则将当前插入的消息 作为队头 & 若此时消息队列处于等待状态,则唤醒
                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;
    // b. 判断消息队列里有消息,则根据 消息(Message)创建的时间 插入到队列中
                    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;
        }
    
    // 之后,随着Looper对象的无限消息循环
    // 不断从消息队列中取出Handler发送的消息 & 分发到对应Handler
    // 最终回调Handler.handleMessage()处理消息
    

    注:文中所有图片来源https://www.jianshu.com/p/b4d745c7ff7a

    总结

    • 根据操作步骤的源码分析总结
    image
    • 工作流程总结

    下面,将顺着文章:工作流程再理一次

    image image

    相关文章

      网友评论

          本文标题:关于Handler的学习

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