美文网首页Android知识Android技术知识Android开发
掌握Handler、Looper、MessageQueue的基友

掌握Handler、Looper、MessageQueue的基友

作者: 落魄的安卓开发 | 来源:发表于2016-11-05 17:55 被阅读212次

    首先我们来实现主线程给子线程发送消息的一个简单的例子,然后我们再带着问题去搞明白它们是怎么关联在一起的以及发送消息、取出消息、处理消息整个流程是怎样实现的。

    /**主线程给子线程发送消息的Handler*/
    private Handler threadHandler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test_handler);
        tvTest = (TextView) findViewById(R.id.tvName);
        new MyThread().start();
    }
    public void testHandler(View v){
        threadHandler.sendEmptyMessage(0);
        L.D("消息发送者所在线程:" + Thread.currentThread());
    }
    class MyThread extends Thread{
        @Override
        public void run() {
            Looper.prepare();
            threadHandler= new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    L.D("收到主线程发送过来的消息" + "---所在线程:" + Thread.currentThread());
                }
            };
            Looper.loop();
        }
    }
    

    打印日志:
    消息发送者所在线程:Thread[main,5,main]
    收到主线程发送过来的消息---所在线程:Thread[Thread-11699,5,main]

    说一下这里为什么能够从主线程发送到子线程吧:threadHandler 在子线程进行初始化的时候,构造参数如果没有传入Looper,那么内部会调用Looper.myLooper(),给它的mLooper赋值,而Looper.myLooper()就是从当前子线程的ThreadLocal.get()取一个Looper,因为我们之前调用了Looper.prepare(),所以当前Looper轮询的是子线程的消息,处理的也就是子线程的消息。

    Handler、Looper、MessageQueue、Message这四个类、及他们的部分属性
    Handler:
    final MessageQueue mQueue;
    final Looper mLooper;

    Looper:
    final MessageQueue mQueue;

    MessageQueue:
    Message mMessages;

    Message:
    Handler target;

    那么问题来了:

    问题一:Looper.prepare(),Looper.loop()这两个方法是干嘛的,为什么要调用这两个方法?

    我们通过看Looper.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));
    }
    

    其实就是为了得到一个Looper。这里先从ThreadLocal中取Looper,如果没有的话就new一个Looper,然后set到ThreadLocal中。
    (ThreadLocal,它是用来存储线程中的一些变量,它有set get 方法。这里的我们存储的变量就是Looper。关于ThreadLocal可以参考大神的博客:http://blog.csdn.net/singwhatiwanna/article/details/48350919

    接下里查看Looper的构造方法

      private Looper(boolean quitAllowed) {    
        mQueue = new MessageQueue(quitAllowed);    
        mThread = Thread.currentThread();
      }
    

    我们发现在Looper的构造方法中会new MessageQueue(),给它的mQueue属性赋值。

    接下来再看Handler的构造方法
    注意:Handler创建时会采用当前线程的Looper来构建内部的消息循环系统

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

    可以看到在Handler构造方法中,调用了
    mLooper = Looper.myLooper();
    mQueue = mLooper.mQueue;
    分别给它的mLooper、mQueue属性赋值;至此他们三者就关联起来了。

    问题二:为什么handler.sendMessage(msg)之后,handler会回调handlerMessage(Message msg)方法?(即:从new 一个Message 到 handler 去处理这个消息整个流程是怎么下来的。)

    查看sendMessage(msg)源码,不管什么方式sendMessage,都会调用到下面的方法:

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
    

    跟进去看enqueueMessage(queue, msg, uptimeMillis)方法:

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

    这个方法就是将Message压入到消息队列MessageQueue中(注意:MessageQueue并不是队列而是单链表,单链表在插入和删除上比较有优势。enqueueMessage方法第一句给msg的target属性赋值为Handler自己);我们知道Looper是不断的从MessageQueue中取消息的,接下来我们看一下是他是怎么取消息的,查看一下Looper.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();
        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
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }
            msg.target.dispatchMessage(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()源码我们可以看到,里面是个for(;;){}死循环在不断的从MessageQueue中取出Message,在for循环中我们可以看到,msg.target.dispatchMessage(msg),通过前面我们知道Message的target属性就是Handler,所以这里的disptachMessage(msg)就是调用了Handler的disptachMessage(msg),我们去看看这个方法:

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    

    可以看到,在Message的callback == null时候,就会调用handleMessage(msg),这是个空方法,让我们自己实现去处理消息,如下:

    public void handleMessage(Message msg) {}
    

    到这里我们就知道了,从new Messags()得到一个消息Message,然后通过handler.sendMessage(msg)把消息放入MessageQueue中,然后Looper.loop不断从MessageQueue中取出消息,再把消息交给handler.handlerMessage(msg)进行处理整个流程。

    问题三:为什么我们在UI线程中使用Handler的时候,不用自己去调用Looper.prepare()和Looper.loop()?

    因为ActivityThread在创建Main线程的时候会调用Looper.prepareMainLooper();其实,这个Looper.prepareMainLooper方法内部就是调用了Looper.prepare()方法,即得到一个Looper,所以就不需要我们自己去实现了。

    如有错误的地方劳烦提醒下,以便及时修正,辛苦。

    相关文章

      网友评论

        本文标题:掌握Handler、Looper、MessageQueue的基友

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