做为Android开发,Handler想必你们都不陌生了,并且是比较基本的知识,在面试中通常也会说起到,并且很容易使用,可是若是错误使用也会形成内存泄漏的问题,这里暂时不作解析,如不知道请自行翻阅handler内存泄漏的文章。言归正传,做为Android开发要想进一步提高本身,知道原理也是理所应当的,相似的文章也不少,想必你们都看过,写这篇文文章目的也是回顾下原理,同时也但愿能帮助到有须要的人,若有错误,欢迎指正。android
做为一名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就负责四大组件的启动和结束的消息传递等。数组
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的) 若是不为空则进行存贮,为空那么新建。bash
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()方法:async
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的数组里边,如何存在这个值就替换掉,如不存在须要往数组里新增。ide
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()方法会阻塞到这里。
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所在线程来处理事件了。
最后来看下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这个线程存储利器,还有他们之间的调用关系,最后但愿你们能够一块儿保持学习精神,不管是哪一个行业,只要不断学习才能让你不断进步。