1.什么是Handler
我们平时在开发中,经常用到Handler,用来发送消息,处理消息。 或者做一些延迟发送消息,跨线程发消息,或者更新UI,或者去实现一些定时轮询的操作。 安卓发展至今,已经有很多框架,可以代替这样原生Handler的通信方式。 比如Eventbus, Rxjava,AyncTask...等等。但是实际上底层依然是对Handler的封装。那么Handler究竟是什么?
简而言之:
Handler就是Android系统提供给我们,用来更新UI的一套机制,也是一套消息处理机制。通过Handler,可以用来发送消息,处理消息。如果不遵循这样的机制,就没有办法更新UI,会抛出异常。
2. Handler消息机制的五个重要角色
1.Handler: 消息的发送和处理者
2.Message: 消息
3.MessageQueue: 消息存放的队列
4.Looper : 从消息队列里面一条一条取消息的消息侦听器,或者消息泵
5.线程: 当前是在哪个线程
3.通俗的理解这个消息机制
也许很多人在网上都看过很多资料去解释这样一个消息机制,但是如果向别人阐述这样一个原理的时候,或者面试的时候,我相信很多人还是模棱两可,弄不太清楚。 那么,通过一个简单的生活案例,来帮助理解一下:
eg: 我们都知道读书的时候,我们经常需要向老师请假之类。
比如:报告老师,我要上洗手间。然后老师说:允许,快去吧。
过了一会。
又给老师报告:报告老师,后面有同学踢我凳子。老师说:你先坐前面来,不要理他。
当然,这只是比喻。我们通过这个比喻,来比较生动形象的去理解handler就容易多了。
1.handler:学生。
2.message:报告老师的内容
3.Looper :老师自己
4.messagequeue: 老师的耳朵和听力记忆
解释:
-
学生(handler)向老师(Looper)举手
-
说 “我要上洗手间”这个报告(message),
-
然后老师的耳朵(messagequeue)听到了这个报告,
-
先是反馈了学生的请求(dispatchHandle),告诉这个同学,同意他的请求,
-
于是学生自己就上洗手间去了(handleMessage)
ps: 老师: 在这个过程中,就是一个消息的接受者,源源不断的接受学生的各种报告或者消息,存在老师的记忆里, 老师又没有分身术,只有一张嘴,所以同一时间,只能根据记忆中的消息,一条一条的反馈给学生,处理他们的请求。 学生: 在这个过程中,我们可以发现,学生既是消息的发出者,又是消息的处理者。 收到老师的同意后,于是就高高兴兴的去上洗手间去了。
整个过程如图:
由图可以看出:
1.handler负责发送消息,接收处理消息
2.Looper负责接收handler发过来的消息
3.MessageQueue就做为Looper消息泵内部的消息容器
3.Looper在内部,将发送过来的消息,交给自身的消息队列,并按时间顺序加入其中 4.Looper在内部,通过不断循环的方式,将消息从队列中一个一个取出,回传给handler
此时你有可能会问:那么Handler它怎么知道应该往哪发消息,并且发给哪个Looper,加入这个Looper的消息队列呢?答案在构造方法中。
4.Handler的创建和Looper的关联关系
Handler的创建,调用Handler的构造方法即可。 new Handler(). 那么我们看下Handler的构造方法都做了些什么呢?Handler的构造方法有好几个,我们先从空构造看起:
//从这开始看起,空构造
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) {
this(looper, callback, false);
}
public Handler(boolean async) {
this(null, async);
}
//然后看到这个构造方法
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());
}
}
//构造方法内部,有一个默认的looper对象,并且这个对象是通过myLooper得到,也就是当前线程的looper。
//当前线程:默认情况就是主线程,为什么会是主线程,后面会讲
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;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
上面代码可以看出,在handler的默认构造器中,有一句:mLooper=Looper.myLooper(). 这说明Handler 默认的时候,有一个Looper对象。但是这个Looper对象是怎么创建得来的呢?点击去发现:就是根据当前线程返回一个looper对象。当前线程是什么呢?默认情况下,就是主线程,也就是MainLooper.
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
//解释一下:根据当前线程,返回looper对象。如果线程没有与之关联的Looper,那么返回空
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
好了,看到这里,我们就看到了Handler和一个默认looper对象如何关联的。至于looper对象的如何创建,后面再详细讲解。
我们可以得出结论:
-
默认情况下:new Handler()中的looper对象是主线程的looper对象 ,那么消息就是发给主线程的handler进行处理。
-
需要和特定线程的Handler通信,我们就需要调用new Handler(Looper looper) ,传入特定线程的Looper对象即可。
-
Looper对象是属于什么线程,那么handler,就是往哪个线程进行发送消息和处理消息。
5. 发送消息的过程
搞清楚Handler和Looper如何进行关联的关系以后,我们从消息的发送开始理解。
Handler 如何发送消息,消息从哪里产生?
要搞清楚消息的产生,我们首先要知道handler发送一条空消息是sendEmptyMessage().那么我们就跟踪这个方法: 方法的参数what ,就是一个标记位,先不管它。
/**
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
再继续往下跟踪
//这里可以看到,sendEmptyMessage()默认会调用sendEmptyMessageDelayed()方法
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
//内部,通过Message.obtain(),产生了一条消息Message
Message msg = Message.obtain();
msg.what = what;
//然后再调用sendMessageDelayed()方法
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
这里可以看到发送空消息时,Message是在方法内部,通过Message.obtain产生的。 并且会层层调用,最终是这么一个方法路径:
sendEmptyMessage-->sendMessageDelay-->sendMessageAtTime ,最终都是调用sendMessageAtTime()来发送消息的。
6.消息发出后的过程
通过上面sendMessageAtTime()发送消息,消息产生后,按照前面的图的理解,它是会发送出一个Messgae,并且发给Looper,由Looper加入到消息队列的。那么代码上是怎样的呢?
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
//1.首先获取到queue对象,
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
//2. 将消息,插入到这个消息queue队列中去,并且附带了一个时间值
return enqueueMessage(queue, msg, uptimeMillis);
}
我们可以看出:
在最终发送消息的地方,拿到了Queue队列对象,然后就把消息加入到了这个Queue队列中去了。 这个Queue对象的获取,我们可以观察handler的构造看到:每一次Handler创建的时候,会拿到looper对象,再通过looper对象来获取到内部的这个Queue队列对象。回顾handler的创建可发现,如下:
public Handler(Callback callback, boolean async) {
....
....
//1先拿到线程的looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//2通过looper获取到消息queue队列
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
再看下消息加入queue队列的过程
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//这里可以发现 消息msg的target属性:是this,也就是handler 自身当前对象
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
在加入队列的时候,可以看到消息msg的地址,也就是处理者target(由谁来最终处理消息),赋值为this。
这也就说明了,handler默认情况下,发送消息的是它自己,处理消息的也是它自己。
总结一下上面消息的发送过程:
-
发送出去 sendEmptyMessage(),消息的产生在这个里面
-
调用sendEmptyMessageDelay()
-
内部调用sendMessageDelay()
-
内部调用sendMessageAtTime()
-
内部通过当前线程,首先获取到mQueue对象,也就是 消息队列对象MessageQueue
-
当queue不为空的情况下,设置 target,发送给谁, 默认是this --handler自己
-
然后把消息message,放到消息队列中。queue.enqueueMessage(消息,时间);
7. 侦听消息和取消息的过程
我们知道,消息发送到Looper中后,就进入到了队列中,然后等待Looper的循环取出进行分发。那么Looper.loop() 这个循环的过程是在什么时候触发的呢?由于Android默认的线程是主线程,所以在应用进程启动之后,就会进入ActivityThread这个主线程中,而在这个ActivityThread中的main()方法,就会触发Looper.loop()开始侦听主线程的消息。
//main方法
public static void main(String[] args) {
...
...
...
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);
//1. 触发loop
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
可以看到,在主线程的ActivityThread main()方法内部,触发Looper.loop()。学过java的人都知道,main方法是一个类的主入口.
那么loop是如何进行取消息的呢?点进去继续往下看
public static void loop() {
// 1.拿到当前线程looper对象
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
2. 得到 looper对象内部的消息队列queue
final MessageQueue queue = me.mQueue;
...
...
//3.死循环,配合队列进行取消息
for (;;) {
//4.取出一条消息
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 {
//4.分发消息
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
}
}
可以看到:
-
- Looper.loop()方法中
-
2.通过myLooper,拿到一个Looper对象
-
3.取出looper中的消息队列 mQueue
-
4.然后通过一个死循环,里面通过mQueue.next来取消息 如果消息为空了,就return掉,
-
5.如果消息不为空, 就会调用 msg.target.dispatchMessage(消息)。将消息发出去
msg.target:就是handler自己 handler.dispatchMessage(消息)方法,将消息回调到dispatch中去
消息如何分发给handler处理的呢
通过上面Looper取消息的过程。我们看到了。消息在最终取出后,会通过 dispatchMsg进行回传,交给处理者。 这个处理者,被赋值在Msg.target中. Msg的target本质上就是handler。
在前面发送消息,将消息加入队列的时候,我们看到过,msg.target=this,是在那个时候将消息的处理者进行了赋值。
所以这里,当取出消息后,就又通过handler,通过它的dispatchMessage(msg)进行回传的。
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
//消息分发,回传给处理者 。也就是handler自己。
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
继续往下跟踪可以发现:
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
//如果消息的callback 处理者不为空,就通过这个callback进行处理。这个callback是什么呢? 后续再讲解
if (msg.callback != null) {
handleCallback(msg);
} else {
//默认情况下,走这个分支
if (mCallback != null) {
//如果全局的callback不为空,就会执行这里,不再执行handlemessgae
if (mCallback.handleMessage(msg)) {
return;
}
}
// 回传给消息处理者,处理消息,
handleMessage(msg);
}
}
分发消息后,默认会进入第二个分支。通常情况下callback是没有进行设置,所以直接就会回调handleMessage(msg).走完整个流程.进入处理消息流程
可是这个msg.callback是什么呢? 还有全局的mCallback又是什么呢? 分发消息的时候,如果这些不为空。又会触发什么呢?继续往后看
handler中的Callback又是什么
由于我们知道通知UI刷新的机制有4种方式:
- view.post(runnable)
- handler.post(runnable)
- runOnUiThread(runnable)
- handler.sendMsg(runnable)
我们跟踪一下view.post(runnable).进入view源码:
//传入一个runnable
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
//然后将runnable,交给handler的post执行
return attachInfo.mHandler.post(action);
}
// Postpone the runnable until we know on which thread it needs to run.
// Assume that the runnable will be successfully placed after attach.
getRunQueue().post(action);
return true;
}
然后进入到handler的内部中post
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
可以看到runnable,被包装进入到了发送消息的getPostMessage(r)方法中。跟进去:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
//看到没!!! callback,原来就是指 我们使用view.post(runnbale)的这个对象
//这个runnable,会赋值给msg.callback。
//所以当我们使用别的方式进行更新UI或者消息通信的时候,在handleMessage前,就会进入callback不为空的判断
m.callback = r;
return m;
}
也就是这里msg.callback: 再贴一遍代码
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
//如果消息的callback 处理者不为空,就通过这个callback进行处理。这里就是使用别的方式进行更新UI或者消息的时候,进入这里处理,不再进入handleMessage
if (msg.callback != null) {
handleCallback(msg);
} else {
//默认情况下,走这个分支
if (mCallback != null) {
//如果全局的callback不为空,就会执行这里,不再执行handlemessgae
if (mCallback.handleMessage(msg)) {
return;
}
}
// 回传给消息处理者,处理消息,
handleMessage(msg);
}
}
而mCallback 则是我们初始化构造handler的时候,传入的callback,进行拦截消息处理使用。
总结一下
我们可以发现,最终都是进入到了handler的post方法中,通过handler机制来实现。
未完待续....
继续讲解更深入的细节...
8Looper的创建过程
这里发现有一个sThreadLocal对象,我们的looper对象就是从这个对象中获取的。这个ThreadLocal对象,其实就是一个与线程相关的对象,保存了线程的相关变量,状态等等。再继续往下看:发现最终就是从这个线程相关的对象中,内部有一个map对象,从里面获取得来。这个地方只是一个单纯的get
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
这个地方依然不是我们想要的答案,我们想看的是Looper对象是在哪里创建的。