Handler Looper MessageQueue HandlerThread的那些事

来源:互联网 发布:毕向东java笔记百度云 编辑:程序博客网 时间:2024/06/05 19:18
概述:

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以及消息队列的关系,以及Handler如何发送和处理消息。可以用下面这个结构图来表示整个异步消息机制的运作流程



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:文章如有错误,欢迎大家提出意见,一起讨论,共同进步~












0 0