美文网首页Android面试相关
一个线程能否创建多个Handler,Handler跟Looper

一个线程能否创建多个Handler,Handler跟Looper

作者: 秀叶寒冬 | 来源:发表于2019-08-10 21:47 被阅读0次

    1 一个线程能否创建多个Handler,Handler跟Looper之间的对应关系 ?

    一个线程能够创建多个Handler,Handler跟Looper没有对应关系,线程才跟Looper有对应关系,一个线程对应着一个Looper,如下所示:

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //将Looper存到当前线程的ThreadLocal中
        sThreadLocal.set(new Looper(quitAllowed));
    }
    

    创建Handler时,需要先调用Looper的prepare方法,在该方法中,会首先判断sThreadLocal.get()是否为空,如果不为空就抛出异常,内容是:一个线程只能创建一个Looper。如果sThreadLocal.get()为空,则会创建一个Looper对象并存入sThreadLocal中。

    创建多个Handler方式如下:

          Looper.prepare();
          Handler handler = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                }
          };
          Handler handler1 = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                }
          };
          Looper.loop();
    

    上述代码并不是最完美的代码,没有考虑内存泄漏,代码规范等,只是简单的为了说明该如何使用而已。在主线程中,我们只需要创建Handler就行,不需要调用Looper.prepare()和Looper.loop(),因为在android应用启动时,主线程已经调用了Looper.prepareMainLooper方法帮我们创建了Looper对象,因此不需要我们再自己重新创建。有人会问,那么我们如何知道哪条消息是哪个handler发送的呢?Looper取出消息又如何发送到指定的Handler呢?

    2 Looper取出消息又如何发送到指定的Handler呢?

    上述我们说到,一个线程对应一个Looper,可以对应多个Handler,这里的Looper和handler都属于同一个线程。而每个线程都有一个属于自己的ThreadLocal,Looper对象就存储在线程的ThreadLocal中(如上述代码所示)。从handler发送消息,最终会调用Handler类中的enqueueMessage方法,该方法源码如下所示:

        private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    

    我们看该方法的第一句

    msg.target = this;
    

    我们可以看下Message类中的target是什么

        /*package*/ Handler target;
    

    可以看到该target是一个Handler类型,所以msg的target指向了当前Handler对象,然后msg被加入到消息队列,通过调用下面代码

    queue.enqueueMessage(msg, uptimeMillis);
    

    消息存入消息队列后,就会唤醒Looper,然后Looper就会从loop方法中获取消息,如下所示:

    public static void loop() {
          final Looper me = myLooper();
          ......
    }
    public static @Nullable Looper myLooper() {
          return sThreadLocal.get();
    }
    
    

    me就是线程中prepare方法里创建的Looper对象,该Looper对象里有个消息队列,如下方法获取消息队列

    final MessageQueue queue = me.mQueue;
    

    该消息队列中存储着通过handler发送过来的Message,而MessageQueue通过下列方法从消息队列中取出消息

    public static void loop() {
        ......
        for (;;) {
            Message msg = queue.next(); // might block
            ......
        }
    

    该消息中封装着发送消息的handler对象,取出消息后,就会通过dispatchMessage方法将消息分发出去,如下所示:

        msg.target.dispatchMessage(msg);
    

    这句的意思就是将msg发送到指定的Handler中。

    3 总结

    • 一个线程可以创建多个Handler,但只能创建一个Looper,一个MessageQueue。Handler跟Looper之间没有对应关系
    • Handler通过sendMessage发送消息时,就会将handler对象存储到message中,然后Looper在loop中通过MessageQueue的next方法取出消息后,会通过之前消息封装的handler将消息发送到指定的handler,即通过调用msg.target.dispatchMessage方法。

    相关文章

      网友评论

        本文标题:一个线程能否创建多个Handler,Handler跟Looper

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