Q:简述 Android 消息机制原理
- 首先,创建和线程绑定的 Looper 对象,同时会创建一个与之关联的 MessageQuue 对象用于消息的存储
- 然后,开启 Looper 的消息循环,从 MessageQueue 中获取待处理的消息,无消息则阻塞线程
- 通过 Handler 发送消息,会将消息添加到 MessageQueue 中,同时 Looper 被唤醒处理消息
- Looper 获取到消息,将消息投递到 Message 对象对应的 Handler 进行处理
Android 的消息机制主要是指 Handler 的运行机制,Handler 的运行需要底层的 Looper 和 MessageQueue 支持,MessageQueue 可以理解为消息队列,用来存储 Handler 发出的消息,而 Looper 可以理解为消息循环,由于 MessageQueue 只用来存储消息,不能处理消息,所以 Looper 就是用来处理消息的,Looper 会以无限循环的形式去消息队列中获取新消息,如果有的话就处理消息,没有就一直等待着。而 Looper 还涉及到一个概念就是 ThreadLocal,ThreadLocal 可以让数据在不同线程中实现不同的数据副本,当 Handler 创建的时候,会获取当前线程的 Looper 来构建消息循环系统,而这就是通过 ThreadLocal 来获取的。需要注意的是,线程默认是没有 Looper 的,要使用 Handler 就必须为线程创建 Looper,而我们可以在主线程中直接创建 Handler,是因为主线程在创建的时候就初始化了 Looper 并开启了消息循环。
Q:为什么在主线程可以直接创建 Handler?
public static void main(){
// 1: 创建主线程 Looper 对象
Looper.prepareMainLooper();
// 2: 开始循环
Loop.loop();
}
Android 的主线程为 ActivityThread,在入口函数 main 里面已经初始化了 Looper 对象并调用 Looper.loop() 方法开启了消息循环。
Q:Looper 是如何绑定 MessageQueue 的?
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
在 new Looper 的时候,会在构造方法里创建 MessageQueue 对象并赋值给成员变量 mQueue。
Q:Looper.loop() 方法是如何工作的?
public static void loop() {
// step1: 获取当前线程的 Looper 对象
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException(
"No Looper; Looper.prepare() wasn't called on this thread.");
}
// step2: 获取 Looper 保存的 MessageQueue 对象
final MessageQueue queue = me.mQueue;
...
// step3: 循环读取消息,如果有则调用消息对象中储存的 handler 进行发送
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
try {
// step4: 使用 Message 对象保存的 handler 对象处理消息
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
msg.recycleUnchecked();
}
}
- 获取当前线程中的 Looper 对象,如为空,则抛异常
- 获取 Looper 对象保存的 MessageQueue 对象
- 开启循环读取消息,调用 MessageQueue 对象的 next() 方法获取消息,如果没有消息,就会进行阻塞,如果有消息,则使用 Message 对象保存的 Handler 对象来处理消息,调用 Handler 对象的 dispatchMessage 方法
- 处理完成释放消息对象
Q:消息分发的优先级是怎样的?
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
- Message 的回调方法 callback 优先级最高
- Handler 的回调方法 mCallback.handlerMessage 优先级第二
- Handler 的空方法 handlerMessage 优先级最低
Q:Handler 如何往 MessageQueue 中插入消息,Message 是如何绑定 Handler 的?
final MessageQueue mQueue;
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);
}
// 处理消息,赋值 Message 对象的 target,消息队列插入消息
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
调用 Handler 的 sendMessage 方法,最终会调用 enqueueMessage 方法,在这个方法里,会将 Handler 赋值给 msg.target,这样两者之间就产生了联系,最后,会调用 MessageQueue 的 enqueueMessage 方法来插入消息。
Q:Handler 如何绑定 MessageQueue 的?
public Handler(Callback callback, boolean async) {
// step1:获取当前线程 Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
// step2:获取 Looper 对象绑定的 MessageQueue 对象并赋值给 Handler 的 mQueue
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
Handler 在创建的时候会通过 ThreadLocal 取出当前线程存储的 Looper 对象,并且将 Looper 对象的 MessageQueue 赋值给自身的成员变量 mQueue。
Q:在任何地方下创建 Handler,那么 Handler 是处于什么线程?
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
// 第一个参数是 looper 对象,第二个 callback 对象,第三个消息处理方式(是否异步)
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
- 不传递 Looper 对象创建 Handler,那么就会默认获取当前线程的 Looper 对象,那么 Handler 的操作就都是在当前线程执行的
- 传递 Looper 对象创建 Handler,那么传递的 Looper 对象是哪个线程的,Handler 的操作就在哪个线程执行
Q:子线程怎么创建 Handler?
new Thread() {
@Override
public void run() {
// step1
Looper.prepare();
// step2
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
if(msg.what == 1){
runOnUiThread(new Runnable() {
@Override
public void run() {
// 执行操作
}
});
// step5
Looper.myLooper().quit();
}
}
};
// step3
handler.sendEmptyMessage(1);
// step4
Looper.loop();
}
}.start();
Q:ThreadLocal 的原理是什么?
ThreadLocal 可以实现数据在不同线程下拥有不同的副本,互不干扰。
public class Thread implements Runnable {
ThreadLocal.Values localValues;
}
public class ThreadLocal<T> {
Values values(Thread current) {
return current.localValues;
}
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
public T get() {
// Optimized for the fast path.
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);
}
}
Thread 类内部有一个成员变量 ThreadLocal.Values localValues
专门用来存储 ThreadLocal 的数据。
从 ThreadLocal 的 set 和 get 方法可以看出,他们所操作的对象都是当前线程的 localValues 对象中的数组,因此在不同线程中访问同一个 ThreadLocal 的 set 和 get 方法所做的读写操作仅限于各自线程内部,所以 ThreadLocal 可以在多个线程中互不干扰的存储和修改数据。
网友评论