Looper、Handler和MessageQueue解析

  • 概述

    在Android的实际开发中,我们经常使用java的Handler来进行线程间通信,我们通常只是简单地构造它,然后调用它的sendMessageXxx方法或者post方法来向其他线程发送数据,可是它是如何做到的呢?为什么使用在哪个线程创建的Handler发送数据,数据就会被发送到对应线程去了呢?我在当前线程发送的,Handler怎么知道去哪个线程处理呢?发送后线程又是如何察觉到的呢?带着这些疑问我们去源码里找答案。

  • 构造Handler

    使用Handler的第一步当然是构造它,我们来看不传入Looper的默认配置:

    public Handler(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());
            }
        }
    
        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;
    }
    

    这里会通过Looper.myLooper()来获取Looper,如果为空则会报错,这说明什么?说明如果需要使用Handler,该线程必须先初始化过Looper。

    同时,mQueue保存了Looper的mQueue,也就是MessageQueue。

  • 构造Looper

    在使用Handler之前需要先初始化Looper:

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

    Looper的构造方法是私有的,可以看到,这里会初始化MessageQueue,并持有当前线程对象。

    外部需要通过Looper.prepare方法创建Looper对象:

    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对象不允许被外部来创建使用,且每个线程只能存在一个Looper对象,在唯一方式prepare方法创建后会自动被设置到一个ThreadLocal对象中去:

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    

    getMap方法获取的是当前线程对象的threadLocals变量,它是一个ThreadLocal.ThreadLocalMap对象,这里可以简单的把它当做一个普通的Map,也就是说,ThreadLocal中会有一个Map,会以当前的ThreadLocal为key,新创建的Looper对象为value存放起来。

    前面的Looper.myLooper()方法就是和这个对应起来的:

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
    
  • Looper.loop

    Looper的准备工作还没有完成,要想Handler发送的消息能够被对应线程响应的话,该线程的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;
    
        ... ...
    
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
    
              ... ...
              
            try {
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ... ...
    
            msg.recycleUnchecked();
        }
    }
    

    可见,该方法用一个死循环来一直尝试从该Looper的MessageQueue中取待处理Message,如果这是主线程的Looper,那这里为什么不会堵塞主线程呢?这就得说到Linux的pipe/epoll机制了:

    简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。

    如果取到消息则通过该Message的target的dispatchMessage方法来发送自己,这个target就是Handler。

  • Handler的Message发送

    不管是postXxx系列方法还是sendMessageXxx系列方法,最终都是调用的enqueueMessage方法:

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

    可见,这里会把Message的target赋值为Handler本身,然后推到MessageQueue中。然后MessageQueue的next方法会通过底层判断CPU是否响应该线程了(也就是是否切换到该线程执行了),如果切换回去了,next就会取出Message,然后调用dispatchMessage方法又回到Handler这里:

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

    Handler构造的时候会和当前线程创建的Looper以及Looper的MessageQueue绑定起来,Looper.myLooper()方法是从当前线程对象的threadLocals中取出来的,Looper创建的时候是被存放在这个变量中的,Looper创建时要调用loop方法来开启循环从MessageQueue中读取Message,因为Handler和当前线程的Looper及MessageQueue绑定,所以当在其他线程通过该Handler对象来发送数据时就会被放入Handler创建时所在的线程的MessageQueue中,因此该线程的Looper在loop中读取到了就会通过Message拿到该Handler对象,然后通过它来处理数据。

你可能感兴趣的:(Looper、Handler和MessageQueue解析)