蓦然回首,发现自己已经工作多年,从事android开发已经多年,如今再次和同行谈起Handler的时候,发现互相都只是知道一个大概实现原理和用法,然后互相嗤之以鼻,都说过去也看过源码,了解过原理,可是这么许久过去了,却无法说出更多源码细节,后悔当初看源码的时候,没有好好做个笔记,刚好今天有时间再来翻翻源码,再来复习复习吧,顺便做个笔记吧。
至于用法我就不在赘述了,直接跟源码吧。
至于要用Handler,今天我们还是分几个步骤来说说Handler的原理吧,主要步骤为:
1.创建Handler及其一些细节
2.使用Handler发送消息及其一些细节
3.处理消息及其一些细节
4.最后总结使用Handler的好处和作用
5.总结使用Handler的时候需要注意的一些细节
6.Handler、Looper、MessageQueue的关系和区别
7.针对Handler 衍生扩展到HandlerThread
一、创建Handler及其一些细节
那么首先就必须的创建一个Handler对象吧,OK,new 一个Handler,直接看构造函数吧。
/**
* Use the {@link Looper} for the current thread with the specified callback interface
* and set whether the handler should be asynchronous.
*
* Handlers are synchronous by default unless this constructor is used to make
* one that is strictly asynchronous.
*
* Asynchronous messages represent interrupts or events that do not require global ordering
* with respect to synchronous messages. Asynchronous messages are not subject to
* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
*
* @param callback The callback interface in which to handle messages, or null.
* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
*
* @hide
*/
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对象的函数,而在这几个
函数中,又调用了上面这个构造函数,我这样贴是方便大家阅读,这个构造方法也很简单,就是调用了Loop.myLooper()方法获取
一个Looper对象,并赋值给了mLooper,如果mLooper为null,直接抛出异常,这里大家是不是很直接的就看到我们初学者很容易
犯下的一个错误,比如我们在一个新的子线程中,直接创建一个Handler对象的时候,会直接抛出该异常,导致程序崩溃,那么这句
话我么你应该怎么理解呢? 其实就是说不能在一个没有调用Looper.prepare()的子线程中,创建Handler,至于这个异常为什么会抛
出,我们后面再做详细介绍,至于接下来的几行代码,无非就是把构造方法中的参数赋值给了Handler的成员变量mCallback
和mAsynchronous,然后注意观察一个MessageQueue类型的变量mQueue的赋值,是通过mLooper.mQueue 获取出来赋值给了
mQueue成员变量。
/** * 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); }
这个default的构造函数应该大家很常用吧,但是我们看,这个构造函数内部就一行代码,又调用了另一个重载的构造函数,
那就是我们最开始贴出来的那个构造函数。OK,这个构造方法,相信大家都经常用吧,同样起方法内部直接调用了最开始贴出的那个构造方法。
这个方法很少用把,需要传递一个Callback对象。/** * Constructor associates this handler with the {@link Looper} for the * current thread and takes a callback interface in which you can handle * messages. * * If this thread does not have a looper, this handler won't be able to receive messages * so an exception is thrown. * * @param callback The callback interface in which to handle messages, or null. */ public Handler(Callback callback) { this(callback, false); }
/** * Use the provided {@link Looper} instead of the default one. * * @param looper The looper, must not be null. */ public Handler(Looper looper) { this(looper, null, false); }
这个构造方法相信大家很常用吧,这个构造方法需要传递一个Looper对象的构造参数,然后构造方法内部也是一行代码,直接
调用了三个参数的重载构造函数,其实比较关键的一个参数是Looper。
/**
* Use the provided {@link Looper} instead of the default one and take a callback
* interface in which to handle messages. Also set whether the handler
* should be asynchronous.
*
* Handlers are synchronous by default unless this constructor is used to make
* one that is strictly asynchronous.
*
* Asynchronous messages represent interrupts or events that do not require global ordering
* with respect to synchronous messages. Asynchronous messages are not subject to
* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
*
* @param looper The looper, must not be null.
* @param callback The callback interface in which to handle messages, or null.
* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
*
* @hide
*/
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
哈哈,这个三个参数的重载方法,超级简单吧,只不过相比最开始贴出来的那个两个参数的构造方法,多了Looper参数,然而在这个方法
构造方法中,就少了自己去调用Looper.myLooper();去获取Looper对象的步骤,以及获取出来为null的时候的抛出的异常的步,所以IZIMI
这个构造方法就不需要那么麻烦,直接赋值就好了,这里要主要,如果你传进来的Looper为null,loop.mQueue是会直接抛出
空指针异常的,所以使用的时候尽量注意这些问题。
/** * Use the provided {@link Looper} instead of the default one and take a callback * interface in which to handle messages. * * @param looper The looper, must not be null. * @param callback The callback interface in which to handle messages, or null. */ public Handler(Looper looper, Callback callback) { this(looper, callback, false); } /** * Use the {@link Looper} for the current thread * and set whether the handler should be asynchronous. * * Handlers are synchronous by default unless this constructor is used to make * one that is strictly asynchronous. * * Asynchronous messages represent interrupts or events that do not require global ordering * with respect to synchronous messages. Asynchronous messages are not subject to * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}. * * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for * each {@link Message} that is sent to it or {@link Runnable} that is posted to it. * * @hide */ public Handler(boolean async) { this(null, async); }
最后看这两个构造方法把,第一个是调用上面的三个的构造方法,一个是调用最开始贴出来的构造方法,很简单。
构造方法都已经介绍完了,都很简单,但是不难看出,在这些构造方法中,必不可少的一个元素那就是Looper和MessageQueue,
不管你用哪一个构造方法创建Handler,都离不开Looper和MessageQueue,是的,这两个是Handler机制得以实现完美功能的
最核心的元素之一。
OK,我们针对上面的几个构造参数做个总结:
Looper: mLooper是Handler的一个成员变量, 如果外部没有通过构造参数传入,那么就调用Looper.myLooper()方法获取一个。
MessageQueue: mQueue是Handler的一个成员变量,通过mLooper.mQueue直接得到Looper的成员变量,也就是mQueue是
Looper的一个成员变量,你甚至这样理解更加准确,Looper内部维护着一个MessageQueue,即消息队列。
二、使用Handler发送消息及其一些细节
我们还是由浅到深吧,接下来,我们创建好了Handler之后,是不是直接通过Handler对象的sendMessage、sendMessageDelay等系列
方法发送一个任务消息。
我们接下来看看Handler常用的几个发送消息的方法:
很多时候我们喜欢使用sendMessage(Message msg)和sendMessageDelayed(Message msg,long delayMillis)来发送消息,这些消息
发送到哪里去了呢?我们先来看看这两个方法。
/** * Pushes a message onto the end of the message queue after all pending messages * before the current time. It will be received in {@link #handleMessage}, * in the thread attached to this handler. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); }
很简单,调用sendMessage的时候,直接调用了sendMessageDelayed(Message,long delayMillis)方法,因为外界没有
设置一个delay时间,这个方法也不接受一个delay时间的参数,所以在方法内部调用sendMessageDelayed(Message,long delayMillis) 的时候,默认设置的delay时间为0,这是我们暂时这样理解,意思就是说这个消息需要立即执行处理。
这个方法也很简单,也是可被外界调用,用来发送任务消息的,其内部也很简单,就是判断,做了一个防呆处理,判断delayMillis是否小于0,如果小于0,将delayMillis重置为0,然后再调用了方法sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);注意这里的第二个参数,SystemClock.uptimeMillis() + delayMillis,SystemClock.uptimeMillis()得到的时间是从开机到当前(现在)的时间毫秒值,为什么要用SystemClock.uptimeMillis()?为什么不用SystemClock.elapsedRealtime();和System.currentTimeMillis();呢?这里又需要做一下小小说明:SystemClock.uptimeMillis(): 刚说了,这个是用来获取从开机到当前(现在)的时间毫秒值,不包括深度休眠的时间,所以用这个来处理时间间隔的任务比较合适。SystemClock.elapsedRealtime(): 这个获取的是从开机到当前(现在)的时间毫秒值,但是他包括深度休眠的时间,所以这个用来处理时间间隔任务的话,是不合适的。System.currentTimeMillis(): 这个就更加不合适,因为这个获取的是系统时间,是从1970.1.1 UTC时间到现在的时间毫秒值,按理来说这个应该也可以啊,但是关键的一个问题就是,系统时间是可以修改的,一旦修改,逻辑实现就错误了。所以System.currentTimeMillis()不适合计算时间间隔,eg,now = System.currentTimeMillis()//do something...duration = System.currentTimeMillis() -now;如果在do something的时候,改变了系统时间,那么 获取duration就不准了。所以这里最后使用SystemClock.uptimeMillis() + delayMillis,如果delayMillis>0的话,那么这个消息,将会在现在之后的未来时间执行处理这个消息,OK,接下里我们来看看sendMessageAtTime这个方法。public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }
/** * Enqueue a message into the message queue after all pending messages * before the absolute time (in milliseconds) <var>uptimeMillis</var>. * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b> * Time spent in deep sleep will add an additional delay to execution. * You will receive it in {@link #handleMessage}, in the thread attached * to this handler. * * @param uptimeMillis The absolute time at which the message should be * delivered, using the * {@link android.os.SystemClock#uptimeMillis} time-base. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the message will be processed -- if * the looper is quit before the delivery time of the message * occurs then the message will be dropped. */ 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); }
看到了吧,主角又出来了,MessageQueue,这个应该清楚了吧,这个在构造方法里面就会赋值的mQueue,如果mQueue 为空的话,直接抛出异常了,程序直接挂掉。 Ok,如果mQueue不为空的话,然后调用了enqueueMessage(queue, msg, uptimeMillis)方法,注意这里把queue也传递 进这个方法里了,我们看看这个方法。private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
OK,看第一行代码,调用了Msg.target = this; msg不就是一个Message对象,居然把this对象赋值给了msg.target对象, this不就是咱们的Handler类型的对象,那不就是说Message内部维护着一个Handler类型的成员变量,而且成员变量名为target。 OK,果然没错,Message中确实维护着一个命名为target,类型为Handler的成员变量,OK,第一行代码就是把当前的Handler赋值 给了Message的target成员变量了,最后调用了queue.enqueueMessage(msg, uptimeMillis);注意,这里调用的是MessageQueue的 enqueueMessage(msg, uptimeMillis);方法,我们来直接看看MessageQueue的这个方法。boolean enqueueMessage(Message msg, long when) { if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } synchronized (this) { if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w(TAG, e.getMessage(), e); msg.recycle(); return false; } msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else { // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }
首先这个方法一开始就做如下判断:if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); }
很明显,如果target为null直接抛出异常,也就是target不能为null,其实说到这里,有个很明显的比喻可以拿出来说说。
Message 我们因为都可以翻译为"信息","信件",是的,那我就把他看作是一封信件 ,一条消息,那么既然这样,那一封信件
,一条信息,就必须具备一个属性,那就是目的地、目标,这个消息最终到达哪里,target字面意思就有目标的意思,只不过
用在Handler机制中,发送消息,和接受消息的都是Handler而已,暂时我们这样理解吧。
接下来就是判断,如果当前Message正在使用或者正在处理,那么就会抛出状态异常。
上面两个判断很简单。
那么看接下来的逻辑,主要干嘛呢?
主要就是按照delayMillis时间的先后,把Message存入MessageQueue中,这个存储方式其实就是一个链表形式。
用过Handler系统的同学都知道,Handler发送消息,轮询取出消息消息,都是按照delayMillis大小来处理的(一般外界不设置DelayMillis的前提下默认都会设置为0),
但是如果用户设置了delayMiiils的话,比如,用户设置了delayMillis 0,200,500,1000,2000,3000....不等的毫秒值,
所以这里他要处理的逻辑就是,按照delayMillis时间大小来和mQueue中的Message的when时间进行比较,把当前新Message
安插在消息队列的一个合适位置(这里就不多提Message为null的情况),如果有人问你,Looper 轮询MessageQueue中的消息是不是
按照先进先出的方式的时候,你可以大胆的告诉他,不是的,那么接下来说说他们是怎么存的。
先把当前Message标记为正在使用,把一开始的时间毫秒值都赋值给了当前我们send的Message。
然后把当前的mMessage拿出来,赋值给了临时变量p,然后接下来的逻辑就是和当前的Mesaage比较
,如果目前这个新Message的when<0、when=0或者当前Message为null的话,就把这个当前的Message安插在
新的Message后面,另外一个else语句里面处理逻辑就是,和当前mMessage之后的每个节点使用for循环的方式挨个判断和比较
,如果遍历到最后没有节点的话,即p.next()为空的时候,把新的Message挂到p这个节点之后,或者p.next()不为空,那就比较
when的值,如果新Message的when比p.next的值更早,那就把p.next()插在新Message的后面,把新Message插在prev后面,就这样一直
遍历,直到满足p.next 为null或者p.when小于新Message的when。
OK,其实还有两个发送消息的方法我也经常使用,那就是如下两个方法:/** * Sends a Message containing only the what value. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean sendEmptyMessage(int what) { return sendEmptyMessageDelayed(what, 0); } /** * Sends a Message containing only the what value, to be delivered * after the specified amount of time elapses. * @see #sendMessageDelayed(android.os.Message, long) * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { Message msg = Message.obtain(); msg.what = what; return sendMessageDelayed(msg, delayMillis); }
那看到这两个方法都一样吧,最后还是调用sendMessageDelayed(msg, delayMillis);方法,接下里的步骤都是一样的,只不过上面两个 方法使用更加简单,不需要通过Message传递更为复杂的数据,只需要一个what标记就行,但是在内部,还是自动会创建一个Message的。 所以这样说来,我们说的发送消息,很简单的可以概括为将消息发送到了Looper的MessageQueue中去了。三、处理消息和细节
上面我知道,消息是发出去了,存在MessageQueue中了,那消息又是怎么分发给了对应的Handler来处理的呢?? 还记的我们最开是说的Looper吗?在接下来的消息分发,Looper扮演十分重要的角色,那他具体做了什么呢? 大家知道,mLooper是哪里来的?通过上面的构造方法我们知道,如果外界不设置传递一个Looper的话,那么默认会 调用Looper.myLooper()来获取一个Looper对象来赋值给mLooper,另一种方式就是外借传递进来赋值给mLooper,那么我们来 ,我们来看看Looper这个类。 通过上面可以看到,Looper这个类是不可以在有子类了,也就是说,不能在继承Looper这个类了,既然Handler需要有一个mLooper, 那我们是不是可以直接随意就创建new一个Looper呢?我们看看他的构造方法。 结果我们发现,找了半天,Looper只有一个构造方法,而且还是私有的,private的,不能直接new出来一个Looper,那我们就 联想到一开始创建Handler的时候,使用了Looper的静态方法myLooper()方法获取了一个Looper对象,我们之间来懒看这个静态方法。* Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
看到这里,发现这个方法超级简单,就一行代码,通过sThreadLocal.get()就能获取一个Looper对象,好神奇一样。我们直接来看看sThreadLocal这个变量,到底是啥玩意?// sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
至于ThreadLocal我想通过之后的另外一篇博客来讲解这个类,今天主线直接讲解Handler相关 主线上的东西。 但是至于ThreadLocal稍微提一下,维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本, 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本,放到这里来说,每个使用 ThreadLocal的线程,这就好比哪个线程调用了Looper.prepare()方法创建Looper的时候, 就为哪个线程保存了一个独立的Looper对象,并且多个线程调用Looper.prepare()方法去创建Looper的时候,每个线程的Looper 都是独立的,并不会互相干扰,并且每个只有一个Looper,换句话来说,只有一个Looper也只有一个MessageQueue,因为Looper内部 维护着一个MessageQueue,即消息队列。 我们知道一开始我们在构造方法中看到,通过Looper.myLooper()来获取Looper是很有可能为空的,所以既然ThreadLocal具有get方法 那是不是也有put或者set这样方法设置一个Looper呢? 我们找找看在Looper中是不是有这样的方法给sThreadLocal设置了这样一个Looper呢? 我们发现在Looper中,真有这样一个方法调用了sThreadLocal的set方法:sThreadLocal.set(new Looper(quitAllowed));
/** Initialize the current thread as a looper. * This gives you a chance to create handlers that then reference * this looper, before actually starting the loop. Be sure to call * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */ 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中只有这个方法调用了sThreadLocal的set方法来设置一个Looper,可是通常我们在Activity、Service
等其他主线程空间创建Handler的时候也没有去调用Looper.prepare()和Looper.prepare(boolean);方法,但是创建的时候,也没有
提示报错抛出博客最开始贴的代码中说的"不能在没有调用Looper.prepare()的子线程中创建Handler"。
那么主线程是怎么做到呢?那是不是主线程启动的时候,是不是调用了这个方法?那她在哪里调用的呢?
我们知道一个android app启动的时候,主线程是在的入口是ActivityThread的main方法的,我们去看看ActivityThread的main方法
是不是做了上面这样的操作呢?public static void main(String[] args) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain"); SamplingProfilerIntegration.start(); // CloseGuard defaults to true and can be quite spammy. We // disable it here, but selectively enable it later (via // StrictMode) on debug builds, but using DropBox, not logs. CloseGuard.setEnabled(false); Environment.initForCurrentUser(); // Set the reporter for event logging in libcore EventLogger.setReporter(new EventLoggingReporter()); // Make sure TrustedCertificateStore looks in the right place for CA certificates final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId()); TrustedCertificateStore.setDefaultUserDirectory(configDir); Process.setArgV0("<pre-initialized>"); Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
我们在main方法里面并没有找到调用Looper.prepare()方法的地方,可是我们发现了一个很相似的方法,Looper.prepareMainLooper(); 没错就是这个方法,这个方法具体是怎么实现
的呢?/** * Initialize the current thread as a looper, marking it as an * application's main looper. The main looper for your application * is created by the Android environment, so you should never need * to call this function yourself. See also: {@link #prepare()} */ public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } }
看看,这个方法第一行就是我们要找的prepare()方法,prepare方法就是去创建了一个Looper对象,并且设置给了sThreadLocal对象保存。
看到这里我们就知道为什么我们在主线程直接使用无参数,默认的构造方法创建的Handler不会抛出异常的原因了。
是的,Looper对象的问题算是搞定了,那么并没有地方去轮询消息分发消息啊,只是创建了一个Looper对象而已?看到这里还是一脸雾水对吧。
但是我们主线程创建的Handler发送消息,就能在handleMessage方法中处理,这是为啥?我们不妨接着ActivityThread的main方法,往下走,
你会发现,最后Looper.loop(); 从字面上来理解,loop本身具有循环的意思,是的没做,这个就是启动Looper的循环遍历消息的方法。
具体怎么循环的呢?我们直接来看看代码。/** * 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 final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } final long traceTag = me.mTraceTag; if (traceTag != 0) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } try { msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } 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(); } }
这是一个静态方法,首先通过myLooper()方法取出当前线程对应的Looper对象,然后取出这个对象的MessageQueue对象,在无限的for循环
中,调用queue.next()方法获取Message对象,然后通过得到Message对象的target变量(即与之对应的Handler对象),通过调用
Handler的dispatchMessage(msg)方法,一样的字面意思,就是分发消息,所以在主线程中调用Looper.loop()方法轮询分发的消息,那么他就当然在主线程中执行消息任务,所以这里我们就知道了,如果我们
要在子线程中创建Handler,就必须在子线程中run方法中去调用Looper.prepare()方法创建一个Looper,然后最后还必须调用Looper.loop()方法去轮询分发消息。
这样我们在在子线程中创建的Handler,发送消息,和处理消息,就都在线程中了。
毫无疑问,我们直接来看这个dispathcMessage()方法:/** * 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); } }
这个方法很简单咯,直接先判断Message的callbak成员变量是否为null,Message的callback成员变量是一个Runnable对象,我们知道Runnable
对象有一个run方法,最后在handleCallback方法中直接执行了Message成员对象callback的run(),方法。如果Message没有设置
callback的话,即为null,就会走else语句中来,就会判断,Handler是否设置了callback,其实我们在最开始创建Handler的时候
发现,在构造方法中部分构造方法可以接收一个Callback接口类型的回调对象。/** * Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. * * @param msg A {@link android.os.Message Message} object * @return True if no further handling is desired */ public interface Callback { public boolean handleMessage(Message msg); }
是的很简单的一个接口,在那个else语句中,如果当前有callback,就直接调用了callback的handleMessage(Message)方法,好熟悉的
统一的一个方法,所以外界可以实现一个Callback对象,Override handleMessage()方法,在这个方法中,做一些特定的业务。
再回到主线,不管if还else,最后都会调用Handler的handleMessage方法,到这里大家应该都很熟悉了把。我么来直接看看这个方法。/** * Subclasses must implement this to receive messages. */ public void handleMessage(Message msg) { }
是的,这个方法更加简单的离谱,他什么都没有做,所以,我们在使用handle的时候,可以直接覆盖这方法,这样Looper把消息
分发出去之后,处理消息的时候,就能执行到我们的逻辑。
其实呢,到这里我们的消息分发处理过程就说完了。四、使用Handler的好处和作用
我们知道,Android中,在UI线程中做耗时的操作,会导致UI线程 无法及时(或者实时,或者阻塞UI线程)更新界面UI,就会出现ANR现象, 为了出现这样的情况,我们业务操作都放在子线程中执行,可是android 的UI线程安全规则规定,不能在子线程中更新UI,假如你在子线程中 尝试去更新UI,会抛出异常,程序直接崩溃,这就使得google 推出了一个Handler机制,来更新UI,所以我们一般会在主线程创建一个Handler对象,然后在子线程中做耗时任务, 好使任务完了之后,就可以拿到Handler对象的引用,来发送消息到主线程去更新UI. 至于使用Handler的好处,很显然,决绝UI线程的安全性问题, 更新UI的同步实时问题,也实现了线程间的通信问题,还一个好处就是使得代码更加灵活健壮。
五、总结使用Handler的时候需要注意的一些细节
1.在子线程中,如果没有调用Looper的prepare()方法去创建 一个Looper的话,在该线程中创建Handler会报出异常,如果调用了 prepare()方法之后,你还必须去调用Looper的loop()方法,否则 你发的消息,都将得不到处理,因为loop()就是开启消息轮询提取发送的 启动器。 2.如果你自定义一个线程,在线程中使用Handler和Looper, 在你对该线程没有需要的时候,你应该调用该线程中Looper对象的 looper.quit()方法,否则该方法一直处于无限循环状态,一般情况下也是需要消耗资源的。 3. Looper.myLopper()方法可以获取到当前线程对应的Looper 4.Looper.getMainLooper()可以获取到主线程中的Looper,那就意味着,我们除了在主线程中创建Handler,在其他子线程中 通过使用Handler的Handler(Looper)构造方法,传递主线程的Looper也能在子线程中创建一个在子线程中执行任务的Handler。六、Handler、Looper、MessageQueue,Message的关系和区别
最后,我们根据前面关于对源码的跟读,自然而然对这三者的关系很清楚的了。 Handler: 是一个发送消息和处理消息的工具。 Looper: Looper是消息存储控制中心,Looper内部维护这一个消息队列MessageQueue,调用loop()方法就开启了消息队列的无限 循环的遍历提取分发。 MessageQueue:消息队列,是一个用来存储Message的队列载体,他被Looper控制。 Message: Handler发送的消息,都通过这个对象来进行封装,这是一个消息主体,记录的消息的细节和承载着一些数据。七、针对Handler 衍生扩展到HandlerThread
前面我们谈过自定义一个Thread,并且支持在这个线程中创建Handler, 这样的情况下,我们必须在线程起来的时候调用Looper.prepare()方法去创建Looper 和调用Looper的loop()方法,才能实现这样的功能,但其实后呢,后来google考虑到了 这一点,自己帮我实现了一个这样的东西,即HandlerThread,其实我们可以把她看成一个工具类 ,用来实现线程间的通信,其实他的实现细节,还真的就跟我们说的一样的,不信来看看吧。 首先HandlerThread是一个继承了Thread类的对象。
/** * Call back method that can be explicitly overridden if needed to execute some * setup before Looper loops. */ protected void onLooperPrepared() { } @Override public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; }
我们发现,在这个HandlerThread的run方法中,一开始就调用了Looper的prepare()方法,又在最后调用了Looper的loop()方法。使用HandlerThread的一个优点在于,在Looper prepare的时候,还有一个回调onLooperPrepare方法,这个方法空实现,所谓的空实现,明摆就是让我们自己去覆盖了,我们完全可以在创建HandlerThread的时候去覆盖就好了,这样就Looper prepare的时候可以做点什么任务了。但是住的注意的是,我们在不需要使用HandlerThread的时候,要去调用HandlerThread的quit()方法,退出Looper的循环,释放资源让应用更加性能好。/** * Quits the handler thread's looper. * <p> * Causes the handler thread's looper to terminate without processing any * more messages in the message queue. * </p><p> * Any attempt to post messages to the queue after the looper is asked to quit will fail. * For example, the {@link Handler#sendMessage(Message)} method will return false. * </p><p class="note"> * Using this method may be unsafe because some messages may not be delivered * before the looper terminates. Consider using {@link #quitSafely} instead to ensure * that all pending work is completed in an orderly manner. * </p> * * @return True if the looper looper has been asked to quit or false if the * thread had not yet started running. * * @see #quitSafely */ public boolean quit() { Looper looper = getLooper(); if (looper != null) { looper.quit(); return true; } return false; }
写到这里,这篇博客基本写完了,转载请注明出处。