简要
Handler消息机制是有Handler,Message,MessageQueue和Looper组成的消息机制
Handler相关:
Handler在Android消息机制中主要职责:负责发送消息和接收消息。
MessageQueue相关:
Message在Android消息机制中主要职责:主要负责消息实体的封装,传递相关参数(what ,arg1,obj,callback等)
MessageQueue在Android消息机制中主要职责:负责以链表的形式管理Message。
Looper相关:
Looper在Android消息机制中主要职责:主要负责从MessageQueue中取出Messgae,并将Message交给Handler进行处理。
Handler相关问题
1.Handler中方法 sendMessage 和 post 的区别
通过源码查看
sendMessage源码:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
//当前时间 + 延迟时间(默认为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);
}
post的源码:
public final boolean post(Runnable r)
{
//注意:这里和sendMessage中的Message是不一样的
return sendMessageDelayed(getPostMessage(r), 0);
}
//内部创建了一个Messgae
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r; //注意这个callback就是我们的runnable
return m;
}
从sendMessage和post的源码可以看出方法执行:
- sendMessage → sendMessageDelayed → sendMessageAtTime → enqueueMessage → ...
- post → sendMessageDelayed → sendMessageAtTime → enqueueMessage → ...
有上述源码可知:sendMessage和post方法,他们只是得到的消息实体对象Message不一样。
sendMessga是通过外部传入的Message,一般我们外部会通过Handler的obtainMessgae获取得到一个Message,然后给Messgae的 what ,arg1 ,obj变量进行赋值
post是通过内部创建了一个Message,并给Message的callback变量进行赋值 m.callback = r;
由于Messgae对象不一样,导致了Handler的dispatchMessage接收消息时候的逻辑也不一样,通过源码可以看出:
/**
* Handle system messages here. 在这里处理系统消息。
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) { //由于post发送的消息messgae.callback不为空,所有执行handleCallback
handleCallback(msg); //post执行这个方法
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg); //sendMessgae方法执行这个方法
}
}
结论:
从dispatchMessage方法中,可以得出,sendMessage和post发送的消息,在handler接收消息时,会优先交给post发送的(或者Message的callback赋值发送的),如果不是再交给Handler的CallBack对象(在创建Handler对象时传进来的)如果还不是,最终交给sendMessage发送的。
Handler接收消息的优先级:
1.先交给通过post方法发送的
2.交给通过创建Handler对象时传递进来的回掉接口Handler.Callback
3.最后交给通过sendMessage方法发送的
问题解答:
通过sendMessage和post发送的消息,发送时的消息实体对象Message的赋值是不一样的,sendMessage通过外部获取一个Message,并给what ,arg1 ,obj变量进行赋值。post是通过内部创建了一个Message,并给callback属性进行赋值。在Handler接收消息时,会优先处理post发送的消息,其次采取处理sendMessage发送的消息。
2.使用Handler发送一个延时10s的消息,再发送一个延时5s的消息,让主线程睡5s,最后消息执行顺序是?
通过查看源码可知:
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;
//当p(Message)为空时,或者消息延时间为0,或者消息的执行时间小于当前链表第一个元素的执行时间,就会将当前消息放入链表的首位。
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;
//根据链表中的每个消息的执行时间(也就是Message的when变量值)对链表进行排序。所以延时时间越短最先执行。
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;
}
结论:
从上述源码中可知,消息队列的消息时以单链表的数据结构进行管理Message,并以Messgae中when的属性值大小进行排序。所有时间越短,优先级越高。
问题解答:
通过Handler的源码可知,MessgaeQueue中的消息是以单链表的数据结构进行管理Message。并通过每条Message中的when属性的大小进行排序。所以时间越短,优先级越高。所以延时5s的先执行,10s的后执行。
3.如何发送一个消息如何将消息添加到MessageQueue的首位?
通过问题2可知:
当消息队列MessageQueue为空时,或者消息的延时时间为0时,或者消息的执行时间小于当前队列中第一个元素的延时时间,此时就可以将该消息放入链表的首位。
1.队列为空:
MessageQueue提供了 isIdle方法判断,当前队列是否为空。
调用:通过Looper.mQueue.isIdle()
如果消息为空,直接发送消息就可以。
public boolean isIdle() {
synchronized (this) {
final long now = SystemClock.uptimeMillis();
return mMessages == null || now < mMessages.when;
}
}
2.消息延时时间为0:
通过前边源码可知,当我们sendMessage或者post一个消息时,都会调用sendMessageAtTime方法,该方法第二个参数uptimeMillis会传递到MessageQueue.enqueueMessage方法中,此时就会赋值给msg.when,所以我们只需要给uptimeMillis设置为0就可以。
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
//当前时间 + 延迟时间(默认为0)
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
//修改:
handler.sendMessageAtTime(handler.obtainMessage(),0);
3.Handler其实还提供了一个方法,sendMessageAtFrontOfQueue方法:
public final boolean postAtFrontOfQueue(Runnable r)
{
return sendMessageAtFrontOfQueue(getPostMessage(r));
}
public final boolean sendMessageAtFrontOfQueue(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;
}
//时间为0
return enqueueMessage(queue, msg, 0);
}
问题解答:
可以分为几种通过判断消息队列是否为空,或者消息的延时时间为0,或者消息延时时间小于当前队列第一个元素的延时时间,就可以将当前消息放入链表首位。通过Looper.mQueue.isIdle()方法进行判断,如果为空,直接发送一条消息,如果当消息队列不为空时,可以通过调用Handler的sendMessageAtFrontQueue方法,将消息放入链表首位。
4.在子线程中可以使用Handler发送和处理消息吗?
在Android中提供了一个类HandlerThread,就是在子线程创建Handler。
查看HandlerThread源码:
public class HandlerThread extends Thread {
...
@Override
public void run() {
mTid = Process.myTid();
//1.创建当前线程的Looper
Looper.prepare();
synchronized (this) {
//2.获取当前线程的Looper
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
//3.开始消息循环
Looper.loop();
mTid = -1;
}
}
当我们调用HandlerThread的start方法后,执行run方法,该方法中做的事情。
1.Looper.prepare():创建当前线程Looper。
2.Looper.myLooper():获取当前线程的Looper。
3.Looper.loop():开启消息循环。
1.Looper.prepare()源码:
//通过ThreadLocal来存储当前线程的Looper对象。
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
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对象
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); //创建MessageQueue
mThread = Thread.currentThread(); //获取当前线程
}
public void set(T value) {
Thread t = Thread.currentThread();
//获取当前线程的threadLocals
ThreadLocalMap map = getMap(t);
if (map != null)
//如果不为空,添加集合,this表示当前线程,value表示当前线程对应的Looper。
map.set(this, value);
else
//创建
createMap(t, value);
}
2.Looper.myLooper()源码:
//通过当前线程key获取对应的Looper对象
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
问题解答:
可以在子线程使用Handler发送和处理消息,需要在子线程中创建对应的Looper对象,使Looper对象和Handler对象关联起来,HandlerThread提供了这样的功能。
5.为什么在主线程中没有调用Looper.prepare()创建Looper对象也可以呢?
通过Application的主入口ActivityThread源码查看:
public static void main(String[] args) {
...
//1.创建Looper对象
Looper.prepareMainLooper();
...
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...
2.消息循环
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
public static void prepareMainLooper() {
//发现和HandlerThread的run方法一样
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
问题解答:
通过源码查看,应用程序的主入口AntivityThread的main方法中,默认会创建Looper对象,开启消息循环。
6.在Looper的loop方法是一个无限循环,为什么不会造成应用程序的卡顿?
通过Looper.loop源码查看:
public static void loop() {
final Looper me = myLooper();
...
final MessageQueue queue = me.mQueue;
boolean slowDeliveryDetected = false;
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);
...
} finally {
...
}
...
}
}
//next方法也是无限循环
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
...
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. 没有更多message时
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
...
nextPollTimeoutMillis = 0;
}
}
nativePollOnce(ptr, nextPollTimeoutMillis);方法作用:
nativePollOnce是阻塞操作,其中nextPollTimeoutMillis代表下一个消息到来前,还需要等待的时长;当nextPollTimeoutMillis = -1时,表示消息队列中无消息,会一直等待下去。当处于空闲时,往往会执行IdleHandler中的方法。当nativePollOnce()返回后,next()从mMessages中提取一个消息。
问题解答:
Loope.loop()开启消息循环不断的从消息队列MessageQueue中取出消息,交给Handler进行处理,当调用MessageQueue,next方法时,则会调用native的方法nativePollOnce,该方法会进行判断是否还有消息,如果没有消息,那么CPU则进入休眠状态,当等到信息消息进来时在唤醒CPU,也就是当我们sendMessage或者post往队列添加消息时,会调用native的nativeWake方法唤醒CPU,继续执行,所以虽然loop方法是一个无限循环的,但是不会造成应用卡死,因为当没有消息时CPU会进入休眠状态。