关于Handler的学习

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

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

 public Handler(@Nullable Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class 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 sThreadLocal = new ThreadLocal();

来看下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.
     * 

* 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. *

* 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. *

*/ //在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.
     * 

* 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. *

*/ 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的学习)