Android的消息机制主要是指Handler的运行机制,再加上很多公司面试直接问Handler的机制,所以本篇文章就用"Handler的机制"代替"消息机制"。。
- Android的消息机制概述
- ThreadLocal的工作原理
- Looer的工作原理
- Handler的工作原理
- 消息队列的工作原理
- 主线程的消息循环
Handler的运行需要底层的MessageQueue和Looper的支撑。
1.Android的消息机制概述
开发角度来说,Handler是Android消息机制的上层接口,这使得在开发过程中只需要和Handler交互即可。
Handler的使用过程很简单,通过它可以轻松地把一个任务切换到Handler所在的线程去执行。
常用使用场景:在子线程中做耗时操作(如网络请求数据、读取文件或读取数据库数据等),操作完成需要更新UI,这个时候使用主线程的Handler很容易的将更新UI操作切换到主线程执行。
2.ThreadLocal的工作原理
Handler机制中源码不太好理解的代码ThreadLocal<T>类
ThreadLocal是一个线程内部的数据存储类,通过它可以在指定线程中存储数据,数据存储后只有在指定的线程中才可以获取到存储的数据,对于其他线程则无法获取到数据。
ThreadLocal类中set方法来保存线程数据,保存数据交由ThreadLocalMap对象实现。
//保存数据到ThreadLocalMap中
public void set(T value) {
Thread t = Thread.currentThread();
//获取线程中的ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else{
createMap(t, value);
}
}
ThreadLocalMap类是ThreadLocal类中定义的一个静态内部类。
线程首次调用ThreadLocal对象的set方法map为null,会调用createMap()方法来初始化ThreadLocalMap对象。保存线程对象到Entry类型的数组中。
//初始化过程,保存线程数据到Entry对象中
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
//默认创建数组大小为16
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
Entry类是ThreadLocalMap类中定义的一个静态内部类,找到数据保存源头了,Entry类才是最终保存线程数据的。
//登记类,用来存储TreadLocal对象与数据T
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
ThreadLocal类的代码
//保存数据到ThreadLocalMap中
public void set(T value) {
Thread t = Thread.currentThread();
//获取线程中的ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else{
createMap(t, value);
}
}
//从ThreadLocalMap中获取数据
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();
}
//获取线程中的ThreadLocalMap对象
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
//实例化线程中的ThreadLocalMap对象
void createMap(Thread t, T firstValue) {
//第一个参数把当前ThreadLocal对象作为参数传递给ThreadLocalMap构造方法
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
static class ThreadLocalMap {
//存储线程数据的数组
private Entry[] table;
//初始化过程的同时保存线程数据到table数组中
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
//默认创建数组大小为16
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
//登记类,用来存储TreadLocal对象与数据T
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
private void set(ThreadLocal<?> key, Object value) {
//创建Entry数组
Entry[] tab = table;
//把ThreadLocal对象与数据保存到数组tab中
tab[i] = new Entry(key, value);
//如果数组中已经存在了ThreadLocal对象的数据,那就修改数据值
e.value = value;
}
}
线程Thread类中定义了ThreadLocalMap类型变量threadLocals。
//声明一个ThreadLocalMap类型变量
//具体初始化在ThreadLocal类中的createMap()
//默认threadLocals对象可以存储线程的16个对象数据
ThreadLocal.ThreadLocalMap threadLocals = null;
Android利用该特性把Looper对象存储到ThreadLocal中,这样保证了只有在当前线程中才能获取到与当前线程关联的Looer对象。
3.Looper的工作原理
Looper类中定义了全局常量ThreadLocal<Looper>类型mThreadLocal。
static final ThreadLocal<Looper> mThreadLocal = new ThreadLocal<Looper>();
初始化Handler前需要调用Looler.prepare()方法--只能调用一次,否则抛出异常。把Looper实例对象保存到mThreadLocal实例中。
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的初始化过程,创建了消息队列实例mQueue。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
这样初始化Handler的准备工作已经完成。
初始化Handler对象
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler();
//接下来的工作
}
这样Handler还不能工作,就算你向消息队列中插入了消息,Handler也不会处理的。
需要开启循环命令Looer.loop()。这样Handler就开始工作了。
进入loop方法内部一探究竟,都做了什么工作。
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
这就是经常被面试官问到的问题“子线程中Handler能直接初始化么?”
获取线程中的对象数据Looper
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
下一行代码,获取消息队列
final MessageQueue queue = me.mQueue;
再接着看有一个无限循环for(;;),如果从消息队列中获取到的消息为null则结束循环,loop方法返回。
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
}
从消息队列中获取到消息对象,调用消息对象中的targer对象的dispatchMessage()方法。
msg.target.dispatchMessage(msg);
Message类中的target对象就是Handler类型。
Handler target;
4.Handler的工作原理
Handler发送消息方法sendMessage(),通过一系列的方法调用最终调用enqueueMessage(),在该方法中给target赋的值msg.target = this;把当前Handler对象赋值给target变量。
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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类中,调用了Handler中的dispatchMessage()方法。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
该方法中分了三种情况
1 msg的callback对象不为空,调用callback对象的run方法。
Message类中的属性
Runnable callback;
Handler类中的成员方法
private static void handleCallback(Message message) {
message.callback.run();
}
2 mHandler对象中的mCallback对象不为空,调用mCallback的handlerMessage()方法。
final Callback mCallback;
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public boolean handleMessage(Message msg);
}
3 就是我们常用的,调用我们重写的handlerMessage()方法。该方法默认实现为空。
public void handleMessage(Message msg) {
}
5.消息队列的工作原理
Looper循环是调用消息队列的next()方法来获取消息的。
Handler中调用消息队列的enqueueMessage()方法向消息队列中插入消息的。
可知MessageQueue主要包含两个操作:插入和读取。读取操作本身会伴随着删除操作
尽管MessageQueue叫消息队列,但是它的内部实现并不是用的队列,实际上它是通过一个单链表的数据结构来维护消息列表,单链表在插入和删除上比较有优势。
插入消息方法enqueueMessage
boolean enqueueMessage(Message msg, long when) {
...
//多线程需要排队插入消息
synchronized(this) {
...
msg.markInUse();
msg.when = when;
//当前链表的表头元素
Message p = mMessages;
boolean needWake;
//如果当前链表为空
//当前消息时需要插入链表的表头
if (p == null || when = 0 || when < p.when) {
msg.next = p;
mMessage = msg;
needWake = mBlocked;
} else {
Message pre;
for(;;) {
prev = p;
p = p.next;
//已经到了链表表尾
//或消息已经插入到了合适的位置
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous) {
needWake = false;
}
}
msg.next = p;
pre.next = msg;
}
if (needWake) {
//唤醒阻塞方法next
nativeWake(mPtr);
}
}
return true;
}