Android消息队列初识
ThreadLocal
Thread类内部的一个字段ThreadLocal.ThreadLocalMap字段存放着本线程共享的数据
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadlocalMap是ThreadLocal的一个静态内部类
/**
* 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;
/**
* The number of entries in the table.
*/
private int size = 0;
可以看出ThreadLocalMap内部维护了一个Entry类型的名为table的数组,这个Entry类是ThreadLocalMap的静态内部类,并且该Entry持有存储它的ThreadLocal的弱引用。
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
这个Entry其实就是对要共享的数据进行了简单的封装。
我们再来看一下ThreadLocal的部分源码
private final int threadLocalHashCode = nextHashCode();
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
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();
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocal的set方法就是先拿到本线程的ThreadlocalMap然后将该ThreadLocal对象以及要存储的value交给ThreadLocalMap去存储,ThreadLocalMap会利用ThreadLocal的threadLocalHashCode该字段hash值以及如今table的大小-1做一些操作得到要存储在table的位置的索引,并将value封装为Entry存放在table中,并且ThreadLocalMap会做判断是否要对table数组进行扩容。可见ThreadLoca就是就是一个封装相关方法帮助我们存储与拿取值的类。threadLocalHashCode该字段的利用斐波那契求得hash值这种hash值分布的更均匀。
现在我们来看看looper中如何使用ThreadLocal
我们从looer.prepare()方法开始看起
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<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的时候Looer会先利用Looper的静态成员变量ThreadLocal sThreadLocal来获取当前线程的Looper如果拿到了当前现成的looper说明该线程已经prepare过了就会抛出异常,如果当前线程的第一次prepare则Looper会创建一个Looper对象并利用sThreadLocal存储到当前线程的ThreadLocalMap的table中。
可见Looper内部的静态变量sThreadLocal是查找各个线程Looper的重要索引。
我们再来看一下Handler
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
这个是handler最全的构造方法,我们关注一下这个Callback参数
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
boolean handleMessage(@NonNull Message msg);
}
这个是Handler的一个内部接口,只有一个handleMessage()方法注意这个handler方法是返回值的跟handler内部的方法同名
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(@NonNull Message msg) {
}
我们来看一下这个callback应用到的地方
Message的重要字段:
:callbcak字段是一个Runnalbe类型的,handler.Post()方法会将runnable封装为Message字段,就是将要执行的动作封装在其中。
: target字段是handler类型用来告诉looer将他交给哪一个Handler来处理它
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
查看dispatchMessage()可知如果message设置了callback字段就执行并返回,就不会调用Handler的handlMessage()方法,以及Handler设置的回调接口。当message没有设置该字段的时候,怎么处理Message,如果处理该Message的Handler设置了回调就先执行该回调的handleMessage()方法,如果该方法返回true就说明回调已经成功处理消息,不需要handler调用自己的handlMessage()方法了,但是如果回调返回false那么就交给Handler调用它的handlMessage()方法再处理一遍,说明回调的优先级要更改。
感觉dispatch()很有模板方法的味道,Handler严格控制Message处理的方式与顺序,并且callback相当于hook()函数。