handler基本原理基本都理解,但是有些细节时间一久仍会有所遗忘,做些整理也还是有必要的。
一、handler原理整体概述
首先,Handler相关的原理机制形象的描述为以下情景:
Handler:快递员 Message:包裹 MessageQueue:快递分拣中心(分拣传送带) Looper:快递公司(处理包裹去向)
MessageQueue 是一 个消息队列 , 它是采用单链表的数据结构来存储消息的,因为单链表在插入删除上 的效率非常高。
Looper持有MessageQueue。在循环开启后,不断的从MessageQueue中取出MessageQueue进行处理。
Handler机制可以简述为: Handler将Message发送到Looper的消息队列中,即MessageQueue,等待Looper的循环读取Message,处理Message,然后调用Message的target,即附属的Handler的dispatchMessage()方法,将该消息回调到handleMessage()方法中,然后完成更新UI操作。
Handler发送消息Handler.post最终也是调用的sendMessageAtTime()。所以MessageQueue中的Message是按设定的消息处理时间,按照时间先后顺序排列的。
二、源码解析
Handler在创建的时候就会获取当前线程的Looper来构造消息循环系统,获取的方式就是通过ThreadLocal。 在Android的系统机制中,以下机制中都使用到了ThreadLocal:Looper、ActivityThread、ActivityManagerService。
1、新建Handler源码分析
Handler、Looper、MessageQueue关系: Handler中持有Looper和MessageQueue。Handler创建的时候需要传入一个Looper,或者从ThreadLocal中取当前的Looper。 Looper中持有MessageQueue,在创建Looper的时候,MessageQueue对象也被创建好了,MessageQueue的数据结构其实并不是队列,而是采用了单链表的形式,因为单链表在插入和删除时,比队列有优势。
Handler中必须持有一个Looper。这个Looper如果没有传入,则会从ThreadLocal中取。
Handler.java
public class Handler {
final Looper mLooper;
final MessageQueue mQueue;
···
public Handler() {
this(null, false);
}
}
public Handler(@Nullable 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 " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
2、ThreadLocal分析
Handler内部通过ThreadLocal获取到当前线程的Looper。
看下myLooper方法 Looper.java
public final class Looper {
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
final MessageQueue mQueue;
···
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
}
sThreadLocal是静态的。从源码中可以看出Looper是持有MessageQueue的。ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,最终通过ThreadLocalMap存储。
ThreadLocal并不是线程,它的作用是可以在每个线程中存储数据。ThreadLocal可以在不同的线程之中互不干扰地存储并提供数据,通过ThreadLocal可以轻松获取每个线程的Looper。当然需要注意的是,线程是默认没有Looper的,如果需要使用Handler就必须为线程创建Looper。
ThreadLocal是一种多线程间并发访问变量的解决方案。与其synchronized等加锁的方式不同,ThreadLocal完全不提供锁,为每个线程提供变量的独立副本,以保障线程安全,其实现原理就是每个thread都有一个ThreadLocalMap。使用ThreadLocal可以在一定程度上减少锁竞争。
ThreadLocal.java
public class ThreadLocal<T> {
···
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();
}
//ThreadLocalMap是从当前线程中获取的
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
}
从源码中可知ThreadLocalMap存在于每个thread中。
ThreadLocalMap.java
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
/**
* The initial capacity -- MUST be a power of two.
*/
private static final int INITIAL_CAPACITY = 16;
/**
* The table, resized as necessary.
* table.length MUST always be a power of two.
*/
private Entry[] table;
ThreadLocalMap里面有一个table,里面通过Entry保存了键值对数据。
从ThreadLocal的set和get方法可以看出,它们所操作的对象都是当前线程的localValues(ThreadLocalMap)对象的table数组,因此在不同线程中访问同一个ThreadLocal的set和get方法,它们对ThreadLocal所做的读写操作仅限于各自线程的内部,这就是为什么ThreadLocal可以在多个线程中互不干扰地存储和修改数据。
3、子线程中创建Handler
Android中新开启的线程,是没有开启消息循环的,如果要在线程中使用Handler,那么就要先调用Looper.prepare。android中在子线程使用Handler,可以参考HandlerThread。
主线程中在ActivityThread类中通过调Looper.prepareMainLooper(),自动创建了Looper对象。
Looper.java
public final class Looper {
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的prepare源码如上所示,当从sThreadLocal没获取到Looper时,preprare方法会先new一个Looper,并保存到ThreadLocal中。
参考: https://blog.csdn.net/singwhatiwanna/article/details/48350919 https://blog.csdn.net/qq_16188829/article/details/76922757 https://blog.csdn.net/yu540135101/article/details/82668686
|