Android面试——Handler 机制
一、Handler机制
Handler机制围绕的三个点
1.Handler负责消息的发送和接受
发送给消息MessageQueue和接受Looper返回的消息并且处理。
2.Looper负责管理MessageQueue
Looper不断从MessageQueue中取出消息,并且交给Handler处理。
3.MessageQueue负责存放Handler发送过来的消息
下面会根据一些博客和源码的角度来分析Handler原理,也是从面试被问到的一些问题进行反思为了更深入的理解进行一次总结。
二、Handler创建
Handler面试中可以遇到的问题,带着这些问题去看源码会加深对代码的理解,这些问题我们都可以在源码中找到答案。
问题一:一个线程可以有几个 Handler和Looper?
问题二:为何主线程可以直接使用Handler?子线程使用Handler应该如何创建?
Handler.java ,Looper.java 代码中只提取我们需要的部分,看下上面问题在代码中是什么样的流程,我们就能够找到答案,Handler构造函数中可以看到:
1.handler创建时会获取当前looper,如果为空会抛出异常。
2.myLooper()方法会获取ThreadLocal对应Looper,那在get之前我们肯定需要做set操作,在prepare()方法中我们看到了set Looper的操作。所以在子线程中创建handler,我们需要先调用Looper.prepare,prepare()方法如果获取ThreadLocal对应的Looper不为空则抛出异常,由此可知当前线程只能对应一个Looper,然后Looper.loop开启消息循环。这个是在子线程中创建,那么主线程为什么不需要创建Looper,因为在ActivityThread已经帮我们做好了,ActivityThread—>main()方法中会调用Looper.prepareMainLooper()来创建Looper。
拓展:
1.ActivityThread即UI线程,这里不做拓展,感兴趣的可以自行百度。
2.ThreadLocal,可以在不同的线程之中互不干扰地存储并提供数据,内部是使用ThreadLocalMap存储线程为key,值为value。ThreadLocal并不是线程,不要按照字面意思理解。
3.其实google官方提供了HandlerThread类,该类实现了子线程创建Handler,HandlerThread实际是一个Thread,在run()方法中初始化Looper,Looper.prepare,开启消息循环Looper.loop。
public Handler() {
this(null, false);
}
public Handler(@Nullable Callback callback, boolean async) {
......
//handler创建时会获取当前looper,如果为空会抛出异常
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
//获取looper成功以后创建MessageQueue对象
mQueue = mLooper.mQueue;
//这边对应的是Handler.Callback,后面Handler执行优先级会讲到
mCallback = callback;
mAsynchronous = async;
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
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();
}
}
/**
* Returns the application's main looper, which lives in the main thread of the application.
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
三、 Handler发送消息
问题三:Handler的发送方式有哪些方式。
3.1send方案发送消息(需要回调才能接收消息)
1、sendMessage(Message) 立即发送Message到消息队列
2、sendMessageAtFrontOfQueue(Message) 立即发送Message到队列,而且是放在队列的最前面
3、sendMessageAtTime(Message,long) 设置时间,发送Message到队列
4、sendMessageDelayed(Message,long) 延时若干毫秒后,发送Message到队列**3.2post方案 立即发送Message到消息队列
1、post(Runnable) 立即发送Message到消息队列
2、postAtFrontOfQueue(Runnable) 立即发送Message到队列,而且是放在队列的最前面
3、postAtTime(Runnable,long) 设置时间,发送Message到队列
4、postDelayed(Runnable,long) 在延时若干毫秒后,发送Message到队列
3.3 sendMessage的流程
问题四:Handler优先处理消息需要怎么做?
注意看上面代码Handler在初始化的时候创建的MessageQueue,handler执行sendMessage(),通知MessageQueue插入消息enqueueMessage()。最后调用MessageQueue的enqueueMessage()方法。
重点看下sendMessageAtFrontOfQueue,sendMessageAtFrontOfQueue 立即发送消息到Message队列的头部,在Handler.java—>enqueueMessage()传递参数when = 0;MessageQueue.java—> enqueueMessage()方法
// New head, wake up the event queue if blocked.
判断如果when = 0时,会在队列头部创建一个Message ,如果当前是阻塞状态立即唤醒!
问题五:既然可以存在多个 Handler 往 MessageQueue 中添加数据(发消息时各个 Handler 可能处于不同线程),那它内部是如何确保线程安全的?
MessageQueue.java—>enqueueMessage(),在往消息队列里面存储消息时,会拿当前的 MessageQueue 对象作为锁对象,这样通过加锁就可以确保操作的原子性和可见性了。
消息的读取也是同理(MessageQueue.java—>next()),也会拿当前的 MessageQueue 对象作为锁对象,来保证多线程读写的安全性。
Handler.java
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull 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);
}
public final boolean sendMessageAtFrontOfQueue(@NonNull Message msg) {
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, 0);
}
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
MessageQueue.java
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;
}
3.4 post(Runnable r)方式的流程
在handler.java中可以看到post方式,除了getPostMessage以外与send流程是一致的,getPostMessage()最终会把callback封装进Message,callback变成Message的参数,后面dispatchMessage会用到callback。
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
三、Looper循环
问题六 :主线程 Looper 与子线程 Looper 有什么不同?
主线程中在ActivityThread—>main() ,调用Looper.loop(),在Looper—>prepareMainLooper(),
主线程prepare(boolean quitAllowed),quitAllowed 默认值为false,不允许退出。
需要注意的是子线程如果创建的是当前线程的Looper,调用Looper.prepare ,Looper.loop,由于Looper.loop是一个无限循环,子线程中在明确不需要使用Handler时,需要退出循环。
1.Looper.quit
实际上执行了MessageQueue中的removeAllMessagesLocked方法,该方法的作用是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息,还是非延迟消息。
2.Looper.quitsafely
实际上执行了MessageQueue中的removeAllFutureMessagesLocked方法,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理,quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息。
无论是调用了quit方法还是quitSafely方法只会,Looper就不再接收新的消息。即在调用了Looper的quit或quitSafely方法之后,消息循环就终结了。
这里我们截取部分代码,Looper.java—>loop()是不断循环消息,queue.next(),并且 使用Handler来分发消息msg.target.dispatchMessage(msg),这里msg.target就是Handler,在Handler.java—>enqueueMessage()中赋值,msg.target = this;
Looper.java
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the 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;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
}
}
四、Handler处理消息
问题七:Handler收到消息执行优先级
Handler分发消息并且处理消息,这里涉及到Handler收到消息执行优先级,有三种方式。
Message的回调方法:message.callback.run(),优先级最高;
Handler的回调方法:Handler.mCallback.handleMessage(msg),优先级仅次于1;
Handler的默认方法:Handler.handleMessage(msg),优先级最低。
1.如果msg.callback != null 就直接处理消息,还记得上面Handler post方式发送消息吗?post实际是把Runnable封装进Message对象也就是msg.callback;
2.mCallback 这里的mCallback指的是Handler.Callback();一般在handler初始化的时候创建处理,内部实现的也是handleMessage()方法;
3.handleMessage Handler的默认方法,优先级最低。
Handler.java
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
五、总结
我们从Handler创建,发送消息,到Looper初始化开始消息循环,再通过Looper 把消息分发给Handler,最后到Handler处理消息的大致流程梳理了一遍。