一、概述
Android的消息机制,其实就是指Handler运行机制,而与之相关的就是MessageQueue和Looper;
Handler常见的用法:
用于更新UI,比如在子线程中通过UIHandler来更新UI;
在工作线程中处理耗时操作,如文件读写和网络访问操作;
Handler核心作用就是消息的分发和处理,以及多线程间的线程切换,Handler可以轻松的将任务切换到Handler所在的线程中执行:Handler将消息发送给MessageQueue,接收Looper返回的消息然后处理;
MessageQueue是消息队列,它内部的存储结构是单链表的数据结构,它只负责消息的存储;
而Looper为消息循环,它负责管理MessageQueue,它工作的方式是无限循环的检查消息队列中是否有新消息,有的话就将消息交给Handler处理,否则就一直等待;
一个线程中只能包含一个Looper对象。
二、创建Handler的方法示例
1、UIHandler(主线程中的Handler)
方法一(推荐的方式):
Handler uiHandler=new Handler(Looper.getMainLooper());
方法二:
在主线程中,直接使用Handler的无参构造函数:
Handler uiHandler=new Handler();
2.workHandler的创建
方法一、利用HandlerThread来创建workHandler
HandlerThread handlerThread=new HandlerThread();
handlerThread.start();
Handler workHandler=new Handler(handlerThread.getLooper());
方法二、在子线程中直接创建workHandler,注意默认的开启一个新的线程的时候,这个新线程是不包含Looper的,所以需要为新线程添加一个Looper
new Thread(){
@override
public void run(){
Looper.prepare();//创建Looper实例,并赋值给当前Thread,细节代码下文会有解释;
Looper.loop();
Handler workHandler=new Handler();
}
}.start();
三、Thread
Thread是一个并行执行单元,每个应用程序至少有一个Thread即主线程;Thread有自己的Call Stack,用于存放方法调用相关的信息;
当新建一个线程的时候,默认的是没有Message Looper的,想要创建一个Looper需要在Thread的run()方法中去创建一个:
调用Looper.prepare(),创建一个Looper实例,将创建的实例存入当前线程的LocalValues中,然后调用Looper.loop()方法,开始处理消息;
四、Looper
1.prepare()方法
我们来看下Looper.prepare()方法:
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));
}
调用sThreadLocal.get(),从当前线程中去获取Looper对象,如果获取到的looper不为null,那么会抛出异常:
这里表明一个线程只能有一个Looper对象,如果线程中已经有了looper,再调用Looper.prepare()就会抛异常;
但是看到这里,会有一个疑惑,sThreadLocal是个是什么东西?
sThreadLocal.get()和sThreadLocal.set(new Looper(quitAllowed))两个方法是什么意思?
首先,看下sThreadLocal这个变量是什么含义:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
sThreadLocal是Looper的静态成员变量;
2.ThreadLocal
ThreadLocal<T>,官方文档:
Thread自己的存储类,每个Thread都有自己的值;
所有的threads共享一个ThreadLocal对象,但是每个thread获取的值是不一样;
然后还说每一个thread所做的改变不会影响其他thread值的获取。
前面的话好理解,后边的两句话是什么意思?
ThreadLocal是Google大神们很巧妙的设计,至于大神们是怎么想到这样的设计,我们无从得知;
理解ThreadLocal<T>的工作原理,只需要弄懂put()和get()两个方法;
2.1.ThreadLocal的set()
首先来看下ThreadLocal<T>的set()方法:
public void set(T value) {
//获取调用set()方法的线程
Thread currentThread = Thread.currentThread();
//从当前线程中获取Values,如果为空,则创建
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
//将方法参数直接存入当前线程的values中
values.put(this, value);
}
我们会看到set()会获取当前所在的线程(set()方法所在的线程,也就是Looper.prepare()所在的线程),然后会调用values(currentThread):
Values values(Thread current) {
return current.localValues;
}
获取到当前线程的localValues;也就是当前线程自己的Values对象,如果此时线程的localValues没有初始化,会对其初始化:new Values()对象;
接着调用vaules中的put()方法:
void put(ThreadLocal<?> key, Object value) {
cleanUp();
// Keep track of first tombstone. That's where we want to go back
// and add an entry if necessary.
int firstTombstone = -1;
for (int index = key.hash & mask;; index = next(index)) {
Object k = table[index];
if (k == key.reference) {
// Replace existing entry.
table[index + 1] = value;
return;
}
if (k == null) {
if (firstTombstone == -1) {
// Fill in null slot.
table[index] = key.reference;
table[index + 1] = value;
size++;
return;
}
// Go back and replace first tombstone.
table[firstTombstone] = key.reference;
table[firstTombstone + 1] = value;
tombstones--;
size++;
return;
}
// Remember first tombstone.
if (firstTombstone == -1 && k == TOMBSTONE) {
firstTombstone = index;
}
}
}
put()方法的第一个参数是sThreadLocal对象,第二个参数是要保存的值即looper对象;
mask的值则是在新建values对象的时候赋的值:
private void initializeTable(int capacity) {
this.table = new Object[capacity * 2];
this.mask = table.length - 1;
this.clean = 0;
this.maximumLoad = capacity * 2 / 3; // 2/3
}
从上边的代码可以看出mask的值为table.length-1;
上边方法中的for循环,index的初始化值为index= key.hash & mask,直接结果就是将index的值不会超出table数组下标的范围;
第一次执行的时候,获取到的k值为null,所以直接走:
if (k == null) {
if (firstTombstone == -1) {
// Fill in null slot.
table[index] = key.reference;
table[index + 1] = value;
size++;
return;
}
// Go back and replace first tombstone.
table[firstTombstone] = key.reference;
table[firstTombstone + 1] = value;
tombstones--;
size++;
return;
}
那么table[index]和table[index+1]分别被赋值为sThreadLocal对象的reference值(这个reference是一个WeakReference对象)和looper对象;
上面可以看出,将相关的一组数据保存到相邻的数组值中;
2.2.ThreadLocal的get()
再来看下,get()方法:
public T get() {
// 获取调用get()方法的线程
Thread currentThread = Thread.currentThread();
//获取当前线程的字段values;
Values values = values(currentThread);
if (values != null) {
//获取当前线程的values中的table数组;
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
首先还是获取当前线程,接着获取当前线程的localValues;
如果localValues不为null,获取localValues中的table数组,根据sThreadLocal的hash和localValues的mask值来计算index值,判断sThreadLocal的reference值是否与table[index]相等,相等的直接返回table[index+1];
我们在调用set(value)的时候,将reference和value存放到线程的values的table数组,且两者位置相邻,而我们在调用get()方法时在根据reference确定要去的值在线程的Values的table中的index,然后直接获取到相应的值table[index+1];
这里可以看到ThreadLocal的set()和get()协同工作,完美配合;
读到此时,仔细的读者可能还会有个疑问:
所有的threads共享一个Looper的静态变量ThreadLocal对象,不同的thread根据sThreadLocal.get()获取的值却不同,为什么?
这个问题进一步具体的阐述就是这样一个疑问:
不同的线程来调用Looper.prepare()来获取looper对象:
比如A线程的run()方法中调用了Looper.prepare(),然后就会调用sThreadLocal.set(new Looper(quitAllowed))方法;
B线程的run()方法中调用Looper.prepare(),也会调用sThreadLocal.set(new Looper(quitAllowed))方法;
A和B线程中通过index=hash & values.mask的算法计算出的index值其实是一样的,并且A和B线程调用是同一个sThreadLocal对象的set()方法和get()方法,存入的值不会混淆吗?
相信仔细的读者可能会有这样一个疑问,这个疑问的答案在上面对ThreadLocal的set(value)和get()方法的分析中已经给出了答案:
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
这里边根据当前线程来获取线程自己的values值,然后线程自己的 values.table中根据index的值来存入和获取相关的数据;
同样的,看下get()方法:
public T get() {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
也是先拿到当前线程的localValues值,然后再获取这个localValues中的相关数据;
也就是说不同的线程有自己的values对象,某一个线程是不能修改或者获取其他线程的values对象的;这样就保证了线程之间不会相互干扰。
接着分析thread关联looper的流程,在Looper.prepare()方法中,会新建一个Looper实例:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
我们看到新建一个Looper实例时,会初始化mQueue参数,将新建一个MessageQueue对象,然后会将当前线程赋值给Looper的成员变量mThread,也就是说Looper中的mThread引用了该Looper所在的线程;
3.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
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
内部调用myLooper():
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
获取当前线程的looper,接着获取当前线程looper的mQueue消息队列;
进入到for循环,无限循环中,调用MessageQueue的next()方法,获取消息message,注意这里
Message msg = queue.next(); // might block
注释,此方法可能会被阻塞,looper没有stop的情况下,MessageQueue中没有消息,则会被阻塞,不会返回null;
如果message不为null:
msg.target.dispatchMessage(msg);
会调用message对象中的handle对象的dispatchMessage(msg);
五、Handler
5.1
我们在新建一个Handler对象的时候,通常会使用Handler的构造方法:
new Handler(looper);
这个方法内部调用的是Handler的构造函数:
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
可以看到在构造方法中,会将looper赋值给Handler对象的成员变量mLooper,并且将looper中的looper.mQueue赋值给mQueue;
从上面的Looper.prepare()分析知道thread跟looper是一一对应的关系,而looper跟MessageQueue也是一一对应的关系
5.2
当直接调用new Handler()的时候,Handler中的成员变量mLooper给定一个默认的值,当前线程的looper对象:
public Handler() {
this(null, false);
}
内部调用的方法:
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()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
可以看到mLooper = Looper.myLooper();即如果在创建Handler的时候不指定Looper对象,那么默认使用当前线程的Looper对象,如果当前线程的Looper为空的话,就会抛出异常;
5.3.post()
handler的post(runable)方法:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
内部调用:
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
这里就可以看出,Handler消息分发最终都会走sendMessage()方法;
内部调用:
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);
}
内部调用:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
这里就会调用构造Handler对象时赋值给mQueue的MessageQueue对象的enqueueMessage()方法,将消息传入到mQueue中存储,后续Looper对象会调用loop()方法来提取存储的消息;
注意这一行代码:
msg.target = this;
这行代码表明,消息队列中的message会持有相关Handler对象,以便在后续的 message loop过程中将消息分发给对应的Handler对象来处理;
那么在界面中如果有匿名的Handler对象,就会造成内存泄漏(匿名类会隐性地持有外部类,然后message有持有Handler对象,导致外部类持有的资源不能释放,如果外部类是个Activity界面,就会导致Activity占用的资源得不到释放);
5.4.dispatchMessage()
dispatchMessage(msg):
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
判断message对象是否有callback,有的话进入 handleCallback(msg):
private static void handleCallback(Message message) {
message.callback.run();
}
该方法中会执行message.callback中的run()方法;
message对象中没有callback的话,判断当前handler中的成员变量mCallback是否为null:
不为null,执行mCallback 的handleMessage(msg);
为null,执行Handler中的 方法handleMessage(msg):
public void handleMessage(Message msg) {}
也就是说,Handler中的handleMessage()的优先级最低;
六、MessageQueue
6.1.MessageQueue中的enqueueMessage
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
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 {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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;
}
在enqueueMessage中首先判断,如果当前的消息队列为空,或者新添加的消息的执行时间when是0,或者新添加的消息的执行时间比消息队列头的消息的执行时间还早,就把消息添加到消息队列头(消息队列按时间排序),否则就要找到合适的位置将当前消息添加到消息队列。
6.2.next()方法:
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
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;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
获取消息队列中的消息,如果没有消息,则阻塞;
有的话取出消息并返回message;
七、总结
梳理下上面的逻辑:
- 假设我们新建了一个线程A,并且调用了Looper.prepare(),这个方法将looper对象赋值给了Thread A中的localValues;
- 新建一个Handler(looper),构造函数中传入线程A的looper对象,那么调用该Handler对象的post方法,会把消息传入到线程A的looper对象中的MessageQueue对象中;
- 接着调用Looper.loop()方法,Looper.loop()方法是内部有一个无限循环,所以它会检查消息队列的消息是否更新,如果有新消息,那么就会提取;获取之后会调用message中存储的Handler对象的dispatchMessage(msg)来进行处理;
- 没有新消息,该方法会被阻塞;只到有新消息,会继续提取和分发消息;