Android Handler机制
1. ThreadLocal
在研究Handler的机制之前我们想明确一个知识点,ThreadLocal. ThreadLocal字面意思是线程本地变量,他保存的是一个变量的副本,并且只对当前线程可见。我们做个例子:
final ThreadLocal<Object> threadLocal = new ThreadLocal<>();
for(int i = 0; i < 10; i ++){
new Thread(new Runnable() {
@Override
public void run() {
String local = "Thread" + Math.random();
Log.i(TAG, "local1:" + local);
threadLocal.set(local);
Log.i(TAG, "local2:" + threadLocal.get());
}
}).start();
}
结果如下:
可以看到第一个和第二个打印的日志是一样的,也可能你觉得这个不能说明啥,一会我们看看源码也许你就明白了。 OK那我们看看他的源码:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
在set值得时候,拿到当前的线程,并拿到一个map,就这个值存放到map中。这个TheaLocalMap是什么呢?
/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
就一句话,还是从当前的线程里面去拿的,继续看下threadLocals:
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
那么这个ThreadLocalMap 是一个什么东西呢,继续往下看:
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
可以看到这个ThreadLocalMap中有一个存储结构用来存储键值对,这个value其实就是我们set进去的变量,继续看下set方法:
/**
* Set the value associated with key.
*
* @param key the thread local object
* @param value the value to be set
*/
private void set(ThreadLocal<?> key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
这个方法也不复杂,就是看看当前的数组table中是不是已经存过了,如果已经存过了那么久覆盖掉之前的Value,没有的话就创建一个Entry放到数组中。所以同一个ThreadLocal在一个线程中只能存放一个值,不然会被覆盖掉。
当然拿到当前的值时候也不难,明白了这个get方法也就很简单了。 总结下 ThreadLocal的作用 当前线程是绑定一个变量,只能当前线程访问,别的线程访问不了也修改不了他的值。在多线程的环境中, 当多个线程需要对某一个变量进行频繁的操作,但又不需要同步的时候,可以用ThreadLocal,因为他存储在当前的线程中的,效率恨到,其实就是空间换时间。
2. Handler Looper, MessageQueue, Message
Handler Looper MessageQueue Message他们之间关系其实不难理解,我们new一个Handler并实现里面的handMessage方法,参数是一个message, 然后用Handler发送message,发送的message到了MessageQuueue这个队列里面,Looper维持一个死循环,一直从MessageQueue里面取Message,之后调用相应的Handler里面的handMessage方法,这样就完成了一个循环。 说起来你不难,但是要理解里面的机制,还需要看源码: 首先一个应用在启动的时候,系统会调用ActivityThread.java中的main方法,在main方法里面会去初始化Looper。
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);
Looper.loop();
可以看到调用了Looper.prepareMainLooper();这个方法其实就是初始化了一个Looper,这个Looper是给主线程调用,接着下面调用了Looper.loop() 这个方法是开启一个死循环,一会再看,先看下Looper.prepareMainLooper()
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
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));
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
在上面的源码prepare中new了一个Looper并把它放到了ThreadLocal中,上面我们已经说了,ThreadLocal存储的变量和线程绑定在一起,那么当前的线程其实是主线程。 sMainLooper 这变量其实就是我们常用的Looper.getMainLooper().
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
那么MessageQueue是什么时候创建的,其实就是new Looper的时候创建的。
我们平常用Handler的时候都是new Handler 如下:
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case xx
break
}
super.handleMessage(msg);
}
};
mHandler.sendEmptyMessageDelayed(1, 100);
那么Handler发送的的message是怎么到队列里面,并且之后是怎么回调的,继续看源码:
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);
}
所有的send方法都会掉到上面这个方法里面来,然后插进队列:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
这个时候要注意上面的那个msg.target = this;这一句,可以看到message将会绑定当前发送的Handler,这也就为以后出来message埋下了伏笔,其实说白了就是拿到这个target指向的Handler来调用handmessage方法处理message。
那你有没有注意到那个mQueue 是从哪里冒出来的?
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());
}
}
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;
}
其实就是在new Handler的时候初始化的,可以看到上面Looper.myLooper();这句话,如果你有心的话其实上面已经有这个方法的实现了:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
其实就拿的当前线程里面的Looper,因为这个是在主线程里面的new的所以其实就是拿到ActivityThread里面初始化的Looper。 既然已经拿到messageQueue并且将message放到队列里面的那么接下来是怎么出队列进行处理的呢,那就是我们的一开始就说的Looper.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;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
msg.recycleUnchecked();
}
}
看上面的代码,我删除了一些东西,最主要的是就拿到messageQueue,并有一个死循环,不断的从messageQueue里面拿去message,接着调用msg.target.dispatchMessage(msg); 在直接发送message的时候,我们看过这个target其实就是绑定的当前的Handler,在dispatchMessage这个方法中就会去调用handMessage这个方法,到这里,整个流程就走完了。
关于死循环的问题,可以看看https://www.zhihu.com/question/34652589/answer/90344494 可以看看这个链接,牵扯到了Linux系统管道的问题了。
通过上面的分析我们也就知道了在主线程中可以newHandler 而在子线程中new Hander 必须自己手动调用Looper的原因了,因为主线程已经帮我实现好了
posted on 2018-01-31 16:46 xiaodong135 阅读(120) 评论(0) 编辑 收藏 举报