一个Handler允许您发送和处理与线程的MessageQueue相关联的消息和Runnable对象。每个Handler对象都与一个线程和该线程的消息队列相关联。当您创建一个新的Handler时,它将绑定到正在创建Handler对象的线程/消息队列——从那时开始,它将向该消息队列传递消息和runnables,并将它们(消息和runnables)从消息队列中取出时执行它们。
Handler有两个主要用途:(1)将消息(Message)和runnables在将来的某个时间点执行;(2)在不同的线程上执行要执行的操作。
调度消息是通过post(Runnable)、postAtTime(Runnable,long)、postDelayed(Runnable,Object,long)、sendEmptyMessage(int)、sendMessage(Message)、sendMessageAtTime(Message,long)和sendMessageDelayed(Message,long)方法完成的。post版本允许您在接收到的消息队列(Message)中对Runnable对象进行调用;sendMessage版本允许您对包含包数据(a bundle data)消息(Message)由Handler的handleMessage(Message)方法处理(要求您实现Handler的子类)。
在投递或发送消息给到Handler时,您可以允许在消息队列准备就绪时立即处理该内容,或者延迟处理或者指定时间处理。后两种方法允许您实现超时、计时和其他基于时间的行为。
当为应用程序创建一个进程时,它的主线程将用于运行一个消息队列,该队列负责管理顶级应用程序对象(活动-activities、广播接收器-broadcastReceiver等)以及它们创建的任何窗口。您可以创建自己的线程,并通过Handler与主应用程序线程进行通信。这是通过调用post或sendMessage方法来完成的线程间的通信。然后,给定的Runnable或消息将被安排在Handler的消息队列中,并在适当的时候进行处理。
参考官网:https://developer.android.google.cn/reference/android/os/Handler
参见:1.Handler官方介绍
Handler使开发人员可以参与到Android的线程中消息循环的机制,开发过程中大多数情况下我们使用Handler和Message就可以满足大部分开发要求,Message是Handler对开发人员暴露经常使用的类,Message也可以理解为是一种载体,通过载体只是把信息从A线程传给B线程;作为程序员,总是会好奇背后的一些东西,Handler机制又是如何实现的,Handler设计Thread、MessageQueue和Looper这几个类之间的关系是什么?
通过上面的图,可以看到Thread使用整个Handler机制的基础,即Looper和MessageQueue构建在Thread之上,Handler构建在Looper和MessageQueue之上,在调用Handler的时候间接是在调用偏低层的类来完成相应的任务;
Thread时Handler机制的基础,每个线程(Thread)维护一个消息队列---MessageQueue。
为什么会有消息队列呢?例如我们有多个后台下载文件的线程,当多个后台下载文件的线程同时下载完成时,需要通知前台下载完成的通知,因为线程某一个时刻只能处理一件事情,那么应该先显示哪个通知,这也就引入消息的队列的概念,我们将每个下载完成的通知存放到Message中,然后加入到消息队列中;Thread会依次取出消息队列中的Message,然后依次进行处理;Message可以理解为线程要处理的一件事件,MessageQueue理解成线程要处理的Message的池子;
MessageQueue既然是一个队列,那么一定会提供入队列和出队列的方法,入队列方法:boolean enqueueMessage(Message msg, long when) ,出队列方法:Message next();enqueueMessage方法负责发消息放入消息队列,next方法负责从消息队列中取Message;
上面说完了MssageQueue消息队列,那么是什么让消息队列运转起来的,那就是Looper了,那么Looper如何让怎么实现的呢?
Looper类在线程中使用消息循环方式让MessageQueue运转起来的,当我们创建线程时默认不会创建线程对应的消息队列,需要开发人员调用Looper.prepare()方法,然后调用Looper.loop()方法;典型的Looper线程的示例,伴随着Looper使用preapre()方法和loop()方法去创建一个通信的Hander。
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
preapre方法和loop方法需要在run方法中调用,通过查看源代码
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
查看到Looper构造方法是私有的构造方法,外部是不允许新建Looper对象的,也间接说明Looper和线程是一对一的关系;只能通过Looper.myLooper()方法获取当前线程所绑定的Looper;
Looper.prepare()方法介绍
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal sThreadLocal = new ThreadLocal();
/** 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));
}
prepare方法主要负责准备工作,只能准备工作完成以后才可以调用loop()方法,那么都做了哪些准备工作呢?
调用sThreadLocal.get()首先判断线程是否已经绑定了Looper,若绑定了则会抛出运行时RuntimeException异常,说明一个线程不能绑定多个Looper,接着调用new Looper(quitAllowed)新建Looper对象,然后通过sThreadLocal.set(new Looper(quitAllowed));方法建立Looper与线程的绑定关系,通过sThreadLocal.set方法建立Looper和sThreadLocal绑定;
这样就完成了线程sThreadLocal与Looper的双向绑定:
a. 在Looper内通过sThreadLocal可以获取Looper所绑定的线程;
b.线程sThreadLocal通过sThreadLocal.get()方法可以获取该线程所绑定的Looper对象。
通过Looper构造函数也可以了解到Looper和MessageQueue是如何关联上的:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
通过构造函数代码可以看到在Looper构造函数中新建了一个,MessageQueue对象同时赋值给mQueue对象,即建立Looper和MessageQueue的关联;
注意事项:Looper.prepare()方法只能调用一次,若调用多次会导致运行时异常;
调用完成prepare方法以后,做完准备工作,再已经建立相互绑定的线程中就可以调用loop()方法了,那么loop方法中都做了什么呢?
loop方法代码如下:
/**
* 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 slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (slowDispatchThresholdMs > 0) {
final long time = end - start;
if (time > slowDispatchThresholdMs) {
Slog.w(TAG, "Dispatch took " + time + "ms on "
+ Thread.currentThread().getName() + ", h=" +
msg.target + " cb=" + msg.callback + " msg=" + msg.what);
}
}
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();
}
}
下面标是loop方法执行的关键点:
a. final MessageQueue queue = me.mQueue;
调用myLooper获取当前线程绑定的Looper,然后获取Looper中的mQueue消息队列;
b.for(;;)
没有设置条件,死循环;
c. Message msg = queue.next();
我们通过消息队列MessageQueue的next方法从消息队列中取出一条消息,如果此时消息队列中有Message,那么next方法会立即返回该Message,如果此时消息队列中没有Message,那么next方法就会阻塞式地等待获取Message。
d.msg.target.dispatchMessage(msg);
将消息派发给关联的Handler进行处理,target指关联处理消息的Handler
Handler是最顶层的一个类,是构建在Thread,MessageqQueue,Looper之上的;
Handler具有多个构造函数如下:
a. publicHandler()
b. publicHandler(Callback callback)
c. publicHandler(Looper looper)
d. publicHandler(Looper looper, Callback callback)
a和b构造函数没有传入Looper,通过Looper.myLooper();方法获取当前线程绑定的Looper;
c和d方法可以传入Looper对象,保存在mLooper对象中;
b和d的方法中Callback是处理消息的另一种方式,可以实现Callback接口,重新handleMessage方法;
/**
* 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);
}
如果构造方法中没有传入Callback则需要重写Handler类中的handleMessage方法,传入则使用Callback中的handleMessage方法处理Message;类似于new Thread()使用Thread中的run方法和new Thread(Runnable)使用Runnable中的run方法;
讲完Handler构造函数,接下来就要重点讲讲Handler如何发送消息的相关方法了,Handler发送消息主要分为两种,一种是sendXXX()和postXXX()两种形式:
sendXXX相关方法介绍,源代码:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
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);
}
会发现sendMessageXXX和sendEmptyMessageXXX最终都是调用sendMessageAtTime方法将消息放入队列中;
postXXX相关方法介绍,源代码:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(Runnable r, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
public final boolean postDelayed(Runnable r, long delayMillis)
{
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
public final boolean postAtFrontOfQueue(Runnable r)
{
return sendMessageAtFrontOfQueue(getPostMessage(r));
}
通过源代码我们会发现postXXX方法最终都是调用的sendMessageXXX方法,在postXXX方法中都调用了 getPostMessage(r)和 getPostMessage(Runnable r, Object token)方法最终目的都是生成Message对象,然后调用sendMessageXXX方法;
getPostMessage源代码
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
private static Message getPostMessage(Runnable r, Object token) {
Message m = Message.obtain();
m.obj = token;
m.callback = r;
return m;
}
查看getPostMessage方法可以发现,该方法最终是生成了一个Message对象,Message对象携带了callback对象,最终postXXX方法将携带Runnable信息的Message传递给sendMessageXXX方法,最终调用sendMessageAtTime将消息加入到消息队列队列中,通过代码分析可以看到postXXX方法最终依赖sendMessageXXX方法;
我们通过下面的关系图更加清晰的postXXX和sendMessageXXX方法的关系:
有一个特殊的postAtFrontOfQueue方法调用的是sendMessageAtFrontOfQueue方法,目的就是将消息在MessageQueue中的优先级提高,优先处理;
通过分析源代码我们会发现sendMessageXXX、sendEmptyMessageXXX和postXXX最终都是调用的sendMessageAtTime方法,那么接下来看一下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);
}
a.mQueue通过获取当前线程绑定的Looper,然后获取Looper中存放的消息队列;
mLooper = Looper.myLooper();
mQueue = mLooper.mQueue;
b.enqueueMessage()方法是将消息加入到队列中,源代码如下:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
a.msg.target
对应绑定的是当前的Handler;
b.queue.enqueueMessage
对应的是Handler所绑定的消息队列,调用消息队列的enqueueMessage()方法将Message消息加入到队列中;
通过以上源代码的分析,我整理一下整个流程图,如下:
以上是Handler接收Message到入队列的整个操作流程;
下面看一下Hander是如何从消息队列中取消息并且处理的:
我们分析源代码时发现Looper会有消息循环机制,调用loop方法以后会不断的从消息对中取消息,通过查看loop方法可以看到最终取到消息以后会调用如下代码msg.target.dispatchMessage(msg),最终调用Message绑定的Handler的dispatchMessage(msg)方法处理处理消息,简单分析一下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);
}
}
a.首先检查Message中是否有Runnable信息,若有则调用线程中的run方法执行msg.callback.run(),这种方式是通过postXXX的方式加入到消息队列中的消息;
private static void handleCallback(Message message) {
message.callback.run();
}
b.检查到Message不是通过postXXX方式加入的消息队列的,就是Message.callback==null的情况,判断是否实现Handler类中的Callback方法
public interface Callback {
public boolean handleMessage(Message msg);
}
若实现了,则调用Callback中的handleMessage方法处理消息;
c.如果我们在构造函数中没有传入Callback类型的对象,那么mCallback就为null,那么我们会调用Handler自身的hanldeMessage方法,该方法默认是个空方法,我们需要自己是重写实现该方法。
综合上面的情况我们会发现,处理消息会有优先级的区分,首先检查是否是通过postXXX方法添加到消息队列,然后检查是否实现Callback接口,最后才会调用Handler.handleMessage()方法处理消息;
以上是我基于查看源代码总结的流程图,欢迎指正;
框架源码:https://github.com/aosp-mirror/platform_frameworks_base
参考:https://blog.csdn.net/iispring/article/details/47180325#commentsedit