在面试的过程中,反复的被问到Handler机制,在此对其做一个深入的剖析.
Google源码对Handler的定义
看Handler源码开关注释:
Handler允许你发送和处理和线程的MessageQueue相关的Message和Runnable对象,每一个Handler对象都对应一个单一的线程。
一.先看看我们平时怎么使用Handler
平时我们Handler用的最多的就是子线程刷新UI,在Activity里new一个Handler,实现handleMessage方法,然后在子线程里调用handler实例发送消息,handleMessage处理消息,刷新UI.
二.那怎么Handler就能实现刷新UI的呢
就是,怎么就从子线程切到主线程了呢?建议大家有空看看操作系统的入门书籍,理解下线程和进程的概念.(我给大家推荐一本吧<<操作系统真象还原>>,第9章,详细介绍了线程和进程的概念和区别)
1.Handler创建
1)通过构造方法创建
第1种,不带Looper参数。
public Handler(Callback callback, boolean async)
第2种,不带Looper参数。
public Handler(Looper looper, Callback callback, boolean async)
注意这个Callback的实现方法handleMessage的返回值为true,则Handler的handleMessage不会被执行。看下面的这个源码就能明白:
/**
* 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);
}
}
-
子线程中创建Handler
在子线程中,不可直接调用Handler的构造函数创建Handler对象,否则会报错:
Can't create handler inside thread xxx that has not called Looper.prepare()
看源码可以知道Handler的构造方法获取的Looper为空就会报这个错
mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()"); }
那么疑问来了,我们平时使用Handler的时候,直接就是在Activity里new Handler,也没有什么设置Looper的操作,为啥就没有报错呢?请往下看。
-
在Activity里直接创建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(); }
只有Looper.prepare()方法被调用了,sThreadLocal才会有值。
// sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal
sThreadLocal = new ThreadLocal (); 那么就找到一个线索了,主线程肯定某个地方调用了Looper.prepare(),so where is it?
强烈建议详细(滴水不漏)看一遍ThreadLocal和Looper的源码,答案自然就揭晓了。
ThreadLocal的源码分析
ThreadLocal类并非是android特有的类,而是Java解决线程问题的一种手段。
- AtomicInteger (原子自增操作,线程安全)
https://blog.csdn.net/a260724032/article/details/81940785
https://blog.csdn.net/fanrenxiang/article/details/80623884 -
ThreadLocal定义
https://www.jianshu.com/p/6fc3bba12f38 (推荐)
https://www.jianshu.com/p/69ae8c213b30
https://www.imooc.com/article/45196
https://blog.csdn.net/u011860731/article/details/48733073看了这么多文章,也没有对ThreadLocal这个类完完全全地理解。大致的意思就是ThreadLocal通过变量备份的方法解决了线程安全问题。
在Looper类中,ThreadLocal就起着为线程存放记录Looper的作用。 -
Looper的源码分析
- 官方定义
为一个线程启动消息循环,线程默认是没有消息循环的。 - ActivityThread
没有错,主线程的Looper就是在此绑定的。这就是为什么在主线程里可以直接new Handler的原因,系统已经给我们执行了Looper.prepare()和Looper.loop()方法。
如果你勤学好问,那么,请看ActivityThread这个类,它并不是继承自Thread,更不是我们常说的主线程(UI线程)。Android主线程到底是个什么,你真正明白吗?
https://blog.csdn.net/u011631275/article/details/47337385 -
2)通过静态方法创建
注意8.0之前的Handler是没有静态方法来创建的。
//9.0开始有此方法
@NonNull
public static Handler createAsync(@NonNull Looper looper) {
}
//9.0开始有此方法
@NonNull
public static Handler createAsync(@NonNull Looper looper, @NonNull Callback callback) {
}
//8.0开始有此方法
/** @hide */
@NonNull
public static Handler getMain() {
}
//8.0开始有此方法
/** @hide */
@NonNull
public static Handler mainIfNull(@Nullable Handler handler) {
}
2.Handler使用
上面创建了Handler,现在我们就来使用Handler
1)执行Runnable
/**
* Causes the Runnable r to be added to the message queue.
* The runnable will be run on the thread to which this handler is
* attached.
*
* @param r The Runnable that will be executed.
*
* @return Returns true if the Runnable 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 post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
看看官方的注释,执行runnable就是在Handler关联的线程里执行runnable。
可以看到postXXX各种方法,最终还是通过getPostMessage方法将Runnable封装在Message里
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
然后执行sendMessageXXX方法,最终也就是执行sendMessageAtTime方法。
post与sendMessage的区别就在于post,Handler的handleMessage是没有回调的,至于为什么没有回调下面会讲到。
2)发送Message
下面看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);
}