Handler与Looper

概述

先看一个Android中的HandlerThread是如何使用Looper的。

public class HandlerThread extends Thread {
    @Override
    public void run() {
    	......
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        ......
        Looper.loop();
        ......
    }

    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }
}

在线程的run方法中调用Looperprepare()方法进行准备工作,准备之后就可以通过Looper.myLooper获取到当前的线程的Looper了。使用然后调用loop方法进入循环处理。使用的时候非常简单。接下来分析Looper的实现。

而且可以quit来退出线程,quit方法中获取当前线程的looper,不为null会调用looper对象quit方法退出。

Looper

prepare方法

先分析一下静态的方法prepare

public final class Looper  {
    ······
    public static void prepare() {
        prepare(true);
    }
    ······
}

对外的静态方法prepare调用私用的带有参数的prepare方法。

public final class 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));
    }
    ······
}

参数quitAllowed标识是否允许looper退出。

首先调用sThreadLocal参数获取是否有Looper

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

sThreadLocal线程的本地对象,用于保存线程相关的变量。如果从sThreadLocal获取Looper对象不为null,说明线程已经绑定了Looper,直接抛出异常。如果为从sThreadLocal获取的Looper对象为null,就创建一个Looper,并设置到sThreadLocal中。

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

Looper的构造方法中创建MessageQueue,用于保存消息处理。并保存当前的线程到mThread变量中。

总结一下:

  • Looper.prepare()会调用私有带有参数的prepare方法
  • 在私有的带有参数的prepare方法中会创建Looper对象,并添加到线程的本地对象中。
  • Looper的构造方法中会创建MessageQueue消息队列对象。

loop方法

public final class Looper  {
    .......
	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;
   	    ······
	}
    .......
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
    ......
}

loop静态方法中,首先调用myLooper获取当前的线程的looper。如果为null,说明线程未绑定Looper,直接抛出异常。如果不为null,获取Looper中的消息队列MessageQueue

public final class Looper  {
    .......
	public static void loop() {
    	······
        for (;;) {
            Message msg = queue.next(); // 消息队列为空,将一直阻塞
            if (msg == null) {
                return;
            }
            ······
        }
        ·····
    }
}

接着是死循环,一直从消息队列中获取消息message。当msgnull说明MessageQueue正在退出,这里就直接从死循环中退出。接着分析。

public final class Looper  {
	public static void loop() {
        for (;;) {
            ······
            try {
                msg.target.dispatchMessage(msg);
            } finally {
            }
            ······
        }
        ······
    }
}

消息对象的target对象实际为Handler对象。也就是调用了HandlerdispatchMessage对象来处理消息。

public final class Looper  {
    ······
	public static void loop() {
        ······
        for (;;) {
            ······
            msg.recycleUnchecked();
        }
        ······
    }
    ······
}

最后是调用msgrecycleUnchecked。也是就回收msg,以备下次使用。

总结一下Looper.loop()方法:

  • 获取当前线程绑定的Looper的消息队列。
  • 死循环中不断从消息队列中取出Message来处理。有消息就调用消息的targetHandler对象)的来处理。
  • 最后回收Message

quit方法

public final class Looper  {
    ······
	public void quit() {
        mQueue.quit(false);
    }
    ······
}

Looperquit方法会调用消息队列的quit方法。

分段阅读MessageQueuequit方法。

public final class MessageQueue {
    ······
	void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;

            if (safe) {
                removeAllFutureMessagesLocked();
            }
            ······
        }
    }
    ······
}

MessageQueue方法中先检测mQuitAllowed用来判断是否支持退出,主线程的looper是不支持的。

检测mQuitting值来判断MessageQueue是否已经退出了。如果mQuittingtrue,说明MessageQueue已经退出,就直接返回了。

如果mQuittingfalse,接着执行,设置mQuitting = true,接着safe为true,说明是安全退出,会调用 removeAllFutureMessagesLocked()。我们来分析一下removeAllFutureMessagesLocked()

private void removeAllFutureMessagesLocked() {
        final long now = SystemClock.uptimeMillis();
        Message p = mMessages;
        if (p != null) {
            //比较当前的message的执行的时间是否大于当前的时间。如果是就直接的移除所有的消息。
            if (p.when > now) {
                removeAllMessagesLocked();
            } else {
                Message n;
                //循环比较获取时间点大于当前的时间点的消息。
                for (;;) {
                    n = p.next;
                    if (n == null) {
                        return;
                    }
                    if (n.when > now) {
                        break;
                    }
                    p = n;
                }
                p.next = null;
                //循环删除消息队列中时间点大于当前时间点的消息。
                do {
                    p = n;
                    n = p.next;
                    p.recycleUnchecked();
                } while (n != null);
            }
        }
    }

此方法主要是根据删除执行的时间点大于当前时间点的消息。我接着分析MessageQueuequit方法。

void quit(boolean safe) {
	······
    synchronized (this) {
      	······
        if (safe) {
            removeAllFutureMessagesLocked();
        } else {
            removeAllMessagesLocked();
        }
        
        nativeWake(mPtr);
    }
}

如果safefalse,不是安全退出,直接调用removeAllMessagesLocked()删除回收所有的Message

总结一下Looperquit方法:

  • Looperquit方法会调用消息队列MessageQueuequit方法
  • MessageQueuequit方法中,如果是非安全退出,直接移除所有的消息。如果是安全退出直接移除执行时间点大于当前时间点的Message

主线程中的Looper

主线程中的消息的管理也是通过Looper来实现的。它与普通线程的Looper不同的是有特殊的方法,但原理基本一致。

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

    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }
}
  • 主线程的Looper有单独的变量sMainLooper保存。
  • prepareMainLooper方法用于准备主线的Looper,它是在ActivityThreadmain方法中调用的,也就是创建主线程时就会创建了。
  • getMainLooper方法获取主线程的Looper

Handler

A Handler allows you to send and process Message and Runnable objects associated with a thread’s MessageQueue. Each Handler instance is associated with a single thread and that thread’s message queue.

从描述可以总结出Handler一些特性:

  1. 每个Handler实例都会与一个线程以及线程的messageQueue关联。
  2. 发送消息。
  3. 处理消息,处理Runnable

HandlerLooper的关系图:

Handler与Looper
从上面的特性展开了三个问题:

  1. Handler是如何与线程的Looper关联的?
  2. Handler是如何发送消息的,发送到哪里了?
  3. Handler是如何处理消息的?

我们一个一个分析。

Handler与Looper关联

Handler关联Looper其实就是在构造方法中。以默认函数为例。

public class Handler {
	public Handler() {
        this(null, false);
    }
    /**
     * @hide
     */
    public Handler(Callback callback, boolean async) {
        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;
    }
}

不传递任何参数将调用一个其他的构造方法。构造方法中直接获取Looper.myLooper()来获取当前线程的Looper,保存到mLooper中,然后获取mLooper的消息队列mQueue保存到mQueue中。保存Callback用于处理消息。最后一个是mAsynchronous,标识消息是否为异步处理。

这样就关联到了Looper

Hanlder处理消息

我们在Looperloop方法中分析到,处理消息时回调用MessagetargetdispatchMessage方法处理,Messagetarget对象就是Handler对象。

msg.target.dispatchMessage(msg);

那我们来分析dispatchMessage来看看如何处理消息的。

   public void dispatchMessage(Message msg) {
        if (msg.callback != null) { 
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
	private static void handleCallback(Message message) {
        message.callback.run();
    }
  • 如果msgcallback不为null,就是直接调用callbackrun方法。一般是通过post方法发送的Runnable
  • 然后判断HandlermCallback是否为null,不为null就调用mCallbackhandleMessage的方法处理。
  • 最后调用HandlerhandleMessage来执行。

Handler发送消息

发送消息通过两种方式:

  • post相关的方法,来发送Runnable
  • sendMessage相关的方法,发送一个Message

sendMessage方法

我们先来分析sendMessage方法

public class Handler {
    ······
	public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
    ······
}

此方法会调用sendMessageDelayed方法,延迟的时间为0

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

此方法又会委托给sendMessageAtTime来发送消息。

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来完成发送消息。

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

设置msgtarget对象为当前对象,用于处理msg

并调用消息队列MessageQueueenqueueMessage来添加到队列中去。

总结:

  • sendMessage的方法最终会调用sendMessageAtTime方法,然后调用enqueueMessage()方法,而在enqueueMessage()方法中会设置message.target为此Handler,这样Looper获取到的Message可以调用message.target来处理了。

post方法

我们来分析post相关方法。

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

post方法显示通过getPostMessage获取一个Message

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

然后通过sendMessageDelayed发送消息。

总结:

  • post方法实际上就是封装成Message再发送。