前言
作为Android开发,Handler想必大家都不陌生了,而且是比较基本的知识,在面试中一般也会提及到,而且很容易使用,但是如果错误使用也会造成内存泄漏的问题,这里暂时不做解析,如不知道请自行翻阅handler内存泄漏的文章。言归正传,作为Android开发要想进一步提升自己,知道原理也是理所应当的,类似的文章也很多,想必大家都看过,写这篇文文章目的也是回顾下原理,同时也希望能帮助到有需要的人,如有错误,欢迎指正。
一、Handler
作为一名andorid开发人员,如果提到消息的都会知道handler,或者使用evenbus,不过它的底层也是基于handler实现的,要想知道它的原理就首先要知道它的作用,这样更有助于理解源码,我们都知道它的作用是要将其他线程要进行的UI操作必须切换到主线程下进行UI操作,因为在android早期的版本中规定执行UI操作只能在ui线程,如果不这样做,就可以在不同线程调用对UI进行操作,可能会出现并发问题,因为UI线程是属于线程不安全的,而且调用情况比较复杂,在并发下调用ui更新操作会出现很多不可想像的问题。
继续回归我们的消息机制,Handler消息机制并不是独立完成的,而是和MessageQueue,Looer,三者共同完成整个消息机制的,里边还会涉及到ThreadLocal、message的相关知识,它们也是至关重要的。而我们平时在UI线程中使用handler并没有发现使用这两个方法,而实际上在ActivityThread 创建的最初时,zygote孵化出子进程之后在main方法里进行的初始化Looper.prepareMainLooper()的操作并且在ActivityThread 创建了Handler 而这个Handler就负责四大组件的启动和结束的消息传递等。
二、Handler ,MessagerQueue,Looer 及存储类ThreadLocal的详解。
1.ThreadLocal
ThreadLocal 主要作用在不同线程中会存在不同副本也就是说不同线程的间的数据存储子在这里是互不影响的,我们在做多线程的业务是也不妨使用它,它会简化很多代码而且避免很多不必要麻烦。在Looper、ActivityThread、AMS中都有使用。接下来就说下它的好处,第一种就是当Handler要获取当前线程的Looper时,那么存在ThreadLocal中获取就很方便取,如果不这样做可能需要做一个全局的HashMap用来存储looper用来存储并查询,如果存在多个Looer并且多个线程里,或许得添加一个Looper的管理类来管理这么多的looper,这样很麻烦写代码谁不想写简单点,第二种情况,在复杂的逻辑下的传递对象无疑是个痛苦的决定,例如写一个监听器他是全局的,那么如果不用Thread就会需要一个全局静态变量,如果多个线程需要,那么需要创建多个静态变量,这样显然不如Threadlocal来的方便。ThreadLocal内部实现原理:当在使用时会根据线程不同而生成不同的数组,再根据索引找到值,这正是在不同线程中有可以获取不同数据的根本原因。
接下来我们看下TheadLocal的实现, 它是一个泛型类可以存多种不同数据,首先我们开看他的set方法:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
根据当前线程来获取一个ThreadLocaMap,(ThreadLocaMap也是个相当于map,内部有个Entry便是存储ThreadLocal的) 如果不为空则进行存贮,为空那么新建。
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
可以看出新建也是在当前线程下进行的。 接下来看下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();
}
同样也是根据当前线程获取存储的数组map,如果发现为空那么就重新初始化。
源码如下:
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
接下来进入ThreadLocalMap的map.set()方法:
private void set(ThreadLocal<?> key, Object value) {
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();
}
存储在一个Entry的数组里边,如何存在这个值就替换掉,如不存在需要往数组里新增。
2.MessageQueue 原理解析
MessageQueue的原理最主要有两个方法,即插入和读取。enqueueMessage()的作用是向队列中插入一条消息,而next()则是在队列中读取一条数据并且删除这条消息。MessageQueue根据表面意思是消息队列,然而实现却不是队列形式,通过观察内部源码看到它,
msg.next = p; // invariant: p == prev.next
prev.next = msg;
是采用单链表数据结构来维护消息列表的。
先插入一段Hhandler的源码,下面的msg.tatarget=this代表的是handler对象后边会用到。
源码如下:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
然后继续看最后调用的MessageQueue的enqueueMessage方法:
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
//根据链表形式存储结构知道消息没有跳出循环
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
上面就是根据链表插入最新数据,然后跳出循环,接下来再看下MessageQueue的next()方法:
Message next() {
...
for (;;) {
...
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
...
}
}
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
上面是一个死循环当有消息时,next()方法跳出这个会返回消息并清除这个消息,如果没有消息那么这个next()方法会阻塞到这里。
3.Looper 的工作原理
Looper的功能就是循环消息取出,当有消息来到时就立即处理它,没有消息就一直在那阻塞着,因为MessageQueue的next()方法也在阻塞在哪里,后边代码会讲到原因。
那么我们先看下Looper构造方法:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
我可以看到在它的构造法中创建了消息队列对象MessageQueue和存储当前线程mThread。而我们都知道在子线程中,如果使用Handler发送消息,因为子线程没有Looper对象所以会在发送消息时候是抛出异常。
Looper 提供了两个创建消息循环的方法即Looper.prepare()和Looper.PreparMainLooper()
源码如下:
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));
}
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
Looper.prepare()由源码可以知道在当前线程创建新的looper并且存储到threadlocal中。
Looper.PreparMainLooper()由源码可知初始化当前线程的looper并标记为主线程Looper。
Looper 还提供了一个方法getMainLooper 用来获取主线程Looper,Looer 还提供了两个退出looper方法分别为 quit(safe:false),quitSafely(safe:ture),来看MessageQueue的quit()方法如下:
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
quit方法执行完立即离开,而quitSafely为标记安全退出,当前消息队列处理完毕后,安全退出,为了减少篇幅,后面代码就不贴了。
当在子线程创建消息循环时候,如果looper正在退出时,这时handler发送消息就会返回false,查阅MessageQueue的enqueueMessage可知道这里不再说,这里如果没有消息并且还不调用Looper的退出方法,那么Looper.loop()则会一直阻塞在当前线程里,也就是说这个线程不会被关闭,所以建议在子线程处理完所有消息完毕后最好关掉消息循环,用来保证线程的正常退出,从而减少性能问题,或者偶尔会出现内存泄漏等问题,上面的结论在下面源码中即可找到答案。
Looper 循环开启 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;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
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 {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
msg.recycleUnchecked();
}
}
从源码可以看出loop()是一个死循环函数只有当msg=null时候才跳出循环,只有当looper.quit被调用时,messagequeue的quit()才会被调用,看下源码他是调用的尾部函数,中间函数不贴了。
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
这时 mMessages才置为空这时looper()才会跳出循环并且退出,否则会一直走循环这里。方法内部Looper方法会调用messagequueue的next方法,如果没有新消息那么会一直阻塞
在那里,如果有消息到来,looper就会处理这条消息。
msg.target.dispatchMessage(msg);
这个msg.target也就是发送消息的handler(前边说过这个msg.target)对象,那么它会调用dispatchMessage(msg)这个方法是在创建handler的looper线程中调用的,这样也就切换到了handler所在线程来处理事件了。
4.Handler的原理
最后来看下handler的作用,它主要用于消息的发送和处理,源码如下:
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);
}
看源码handler 发送消息仅仅将消息插入一个队列中,因为Looper一直在取消息,一旦有消息,然后调用messagerQueue.next()就会返回消息给looper()方法并,且looper()就会处理这条消息并且交由msg.target的dispcathMessage来处理。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
处理函数中有两个回调方法,如果msg.callback不为空,那么就调用handlercallback,msg.callback是一个Runable对象,也就是当调用handler的post方法发送消息实际上也就是
调用Runable的run方法,然后在一个就检查mCallback(它是接口被handler实现的)方法不为空调用 handler的handlerMessage(Messagemsg),如果mCallback也为空那么执行handler的handleMessage(msg)方法,最后说明一点Callback的创建用途,这点非常好理解,可用过 Handler handler=new Handler(Callback)这个创建,Callback这样写的意义,就是可以再不派生子类的情况下在构造函数下直接通过new Callbcak来重写handlermessage()方法实现的,这也是简化可代码的很好方式。
最后我们来看下为什么不能再子线程不创建looer的情况下使用handler跑出异常的原因了。
public Handler(Callback callback, boolean async) {
...
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
...
}
Handler上边的构造方式如果当前线程没有Looper对象的话就会抛出"Can’t create handler inside thread that has not called Looper.prepare()"),这就解释了线程没创建handler抛出异常的原因了,而主线程在不会抛异常(开篇已经解释原因了)。
总结
本文主要讲解了Handler基本原理,是由Handler、Messagequeue、Looper及ThredLocal这个线程存储利器,还有他们之间的调用关系,最后希望大家可以一起保持学习精神,无论是哪个行业,只要不断学习才能让你不断进步。