天天看点

Handler Looper MessageQueue HandlerThread的那些事

概述:

Handler是Android系统提供的异步消息通信机制,在程序开发中,经常用到Handler在不同的组件之间发送消息,不同的线程之间也可以利用Handler通信,在Handler通信中,包含3个重要部分Handler,Looper,MessageQueue。

Looper创建消息队列:

在日常开发中,我们经常在UI线程中创建一个Handler对象,然后重写其handleMessage方法,在其他线程或者组件可以利用该Handler往UI线程中发送异步消息,回调相关方法进行处理,那么其内部是怎样工作的呢? 下面我们将分析UI线程中消息通信机制的运作过程。 消息处理机制中,所有消息都是由Handler发送到Looper创建的消息队列,Looper对象不停从消息队列中获取消息,最后分发给Handler处理

那么问题来了: 1,UI线程的消息队列怎样创建,并且进入消息循环

2,Handler如何将消息发送到消息队列

3,Looper从消息队列取到消息后又是怎样处理。

先来看一下UI线程中消息队列的创建过程。大家都知道,Android启动一个新的应用程序,会创建一个新的进程,执行ActivityThread的main函数,创建Looper消息队列,并进入消息循环。同时还会创建一个Handler对象,绑定到UI线程的消息队列,在Activity生命周期中,onCreate,onPause等方法都是由该Handler对象往UI线程发消息,最后调起来的。

public final class Looper {
    private static final String TAG = "Looper";

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class

    final MessageQueue mQueue;
    final Thread mThread;

    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));
    }


    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

    /** Returns the application's main looper, which lives in the main thread of the application.
     */
    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the 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.recycle();
        }
    }

    /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static Looper myLooper() {
        return sThreadLocal.get();
    }


    public static MessageQueue myQueue() {
        return myLooper().mQueue;
    }

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



    /**
     * Return the Thread associated with this Looper.
     */
    public Thread getThread() {
        return mThread;
    }

    /** @hide */
    public MessageQueue getQueue() {
        return mQueue;
    }

}
           

Looper类中sMainLooper即UI线程中的Looper对象,通过调用prepareMainLooper,最终会进入prepare函数,创建一个Looper对象,在Looper的构造函数中,创建一个消息队列。

调用loop()函数会进入消息循环,通过next函数获取消息,如果消息队列中没有消息,这里会阻塞,获取消息后通过msg.target.dispatchMessage(msg)将消息分发到Handler处理。

至此,UI线程中消息队列和循环的创建过程已经分析完毕,接下来我们再看Handler实现方式,Handler是怎样将消息发送到消息队列,以及从消息队列中取出消息后,Handler如何处理。

Handler: 

public class Handler {

    private static final boolean FIND_POTENTIAL_LEAKS = false;
    private static final String TAG = "Handler";


    public interface Callback {
        public boolean handleMessage(Message msg);
    }
    
    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }
    
    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

    /**
     * Default constructor associates this handler with the {@link Looper} for the
     * current thread.
     *
     * If this thread does not have a looper, this handler won't be able to receive messages
     * so an exception is thrown.
     */
    public Handler() {
        this(null, false);
    }


    public Handler(Callback callback) {
        this(callback, false);
    }


    public Handler(Looper looper) {
        this(looper, null, false);
    }

    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

   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);
    }

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

  public final Looper getLooper() {
        return mLooper;
    }
}
           

每个Handler对象必须关联一个Looper对象,Handler发送消息和接收消息都是基于这个Looper,Handler有多个构造函数,大致可以分为两类,含有Looper参数的和不含Looper参数的。含有Looper参数的构造函数,会使用参数传递的Looper对象,不含Looper参数使用当前线程中的Looper对象,如果线程没有创建一个Looper对象,则会抛出异常。

Handler关联一个Looper对象后,就可以往Looper对象创建的消息队列中发送消息,从源码来看,发送消息的方式有两种,一种是post,一种是send。

对于post方式,需要传入一个Runnable对象,这是一个回调对象,具体用途,在消息处理的时候再说,post传入runnable对象后,会调用gePostMessage方法构建一个msg,设置msg.callback为传入的runnable对象,最后调用send方式将消息发送到消息队列。所以,不管是post方法还是send方法,最后都是调用sendMessageAtTime,然后调用enqueueMessage方法,将消息放入到消息队列中。

消息发送到消息队列后,该如何处理呢。上面我们提到。Looper.loop()调用后,会不停的查询消息队列是否有消息,如果有,会调用msg.target.dispatchMessage()处理消息,msg.target即为发送该msg的Handler对象,dispatchMessage()方法中:

/**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
           

首先会判定msg.callback是否为空,这个msg.callback就是上面提到的post方式发送消息传入的Runnable对象,如果不为空,则调用handleCallback(msg); 这个函数里面会导致Runnable对象的run方法被调用。如果msg.callback为null,则判定mCallback是否为空,这个callback对象是Handler内部定义的接口,Handler构造函数可以传入一个Callback对象,如果Handler对象创建没有传入callback对象,那么mCallback为null,否则,调用该callback的handleMessage函数, 最后,调用Handler重写的handleMessage函数。整个过程可以用下面的时序图来表示。

Handler Looper MessageQueue HandlerThread的那些事

上面分析了消息队列的创建与循环,Handler与Looper以及消息队列的关系,以及Handler如何发送和处理消息。可以用下面这个结构图来表示整个异步消息机制的运作流程

Handler Looper MessageQueue HandlerThread的那些事

HandlerThread:

上面分析都是针对UI线程,如果我们想在自定义的线程中定义一个Handler,该怎么操作呢。 从上文分析可知,Handler对象必须依赖一个Looper对象,自定义线程中,并没有创建Looper对象。 有三种方式可以在自定义线程中创建Hander

1,使用带Looper对象的Handler构造函数,将主线程的Looper对象传入Handler构造函数中 Handler mHandler = new Handler(getMainLooper()); 这种方式定义的Handler对象,使用了UI线程的消息队列,消息处理还是在UI线程中,和主线程中定义Handler,传入自定义线程没有任何区别。

2,自定义线程类,创建Looper对象,并且调用Looper.loop()方法开始消息循环。

3,使用HandlerThread类,这种方式其实就是第二种,只是Android系统已经帮我们做好了所有的工作,调用方便。

@Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
    

    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }
           

从HandlerThread的源码可以看到,实现方式和UI线程消息队列创建的方式类似,这里我们不再赘述。

HandlerThread类使用很简单:

HandlerThread ht = new HandlerThread("test");
Handler mHandler = new Handler(ht.getLooper());
mHandler.post(new Runnable() {
    public void run(){
        Log.e("HandlerThreadTest", "test handlerThread");
}
}
           

Handler消息通信机制是Android系统提供的非常重要的组件,开发中,应用非常广泛,我们不仅要知其然,还要知其所以然,只有这样,才能真正掌握其用法。

PS:文章如有错误,欢迎大家提出意见,一起讨论,共同进步~