欢迎访问移动开发之家(rcyd.net),关注移动开发教程。移动开发之家  移动开发问答|  每日更新
页面位置 : > > > 内容正文

详解Android消息机制完整的执行流程,

来源: 开发者 投稿于  被查看 39094 次 评论:155

详解Android消息机制完整的执行流程,


目录
  • 从Handler.post()说起
  • MessageQueue.enqueueMessage()添加消息至队列中
  • Looper.loop()分发消息
  • MessageQueue.next()获取消息
  • 总结

从Handler.post()说起

Handler.post()是用来发送消息的,我们看下Handler源码的处理:

public final boolean post(@NonNull Runnable r) {
   return sendMessageDelayed(getPostMessage(r), 0);
}

首先会调用到getPostMessage()方法将Runnable封装成一条Message,然后紧接着调用sendMessageDelayed()方法:

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

这里我们介绍下sendMessageDelayed()的第二个参数delayMillis,这个表示消息延时执行的时间,而post()方法本身代表着非延迟执行,所以这里delayMillis的值为0.

而如果是我们另一个常用的函数postDelay(),这里的delayMillis的值就是传入的延迟执行的时间

继续往下走,会调用到Handler.sendMessageAtTime()方法:

public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    //...
    return enqueueMessage(queue, msg, uptimeMillis);
}

获取到Looper对应的消息队列MessageQueue,继续往下走,作为参数传给enqueueMessage()方法,这个方法主要是对上面封装的Message进行填充:

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);
}

比如将Message被负责分发的target赋值成当前Handler对象,然后根据是否为异步Handler来决定是否给Message添加异步标识。

MessageQueue.enqueueMessage()添加消息至队列中

boolean enqueueMessage(Message msg, long when) {
    //...
    synchronized (this) {
        //...
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        //1.
        if (p == null || when == 0 || when < p.when) {
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            //2.
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p;
            prev.next = msg;
        }
        //3.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

这个方法的使用很明确,就是将Message添加到消息队列中,下来我们主要讲解这个方法的三个核心点,对应上面的注释标识:

1.如果当前消息队列本来为null、消息执行的时间戳为0、消息执行的时间小于消息队列队头消息的执行时间,只要满足上面三个条件之一,直接将该条Message添加到消息队列队头;

这里说下消息执行的时间戳什么时候会为0,就是调用Handler.sendMessageAtFrontOfQueue()这个方法,就会触发将当前发送的Message添加到消息队列队头。

2.如果上面的三个条件都不满足,就遍历消息队列,比较将要发送的消息和消息队列的消息执行时间戳when,选择适当的位置插入;

3.判断是否需要唤醒当前主线程,开始从消息队列获取消息进行执行;

Looper.loop()分发消息

这个方法会开启一个for(;;)循环,不断的从消息队列中获取消息分发执行,没有消息时会阻塞主线程进行休眠,让出CPU执行权。

for(;;)循环会不断的调用Looper.loopOnce(),开始真正的消息获取和分发执行:

private static boolean loopOnce(final Looper me,
        final long ident, final int thresholdOverride) {
    Message msg = me.mQueue.next(); // might block
    if (msg == null) {
        return false;
    }
    try {
        msg.target.dispatchMessage(msg);
    }
    msg.recycleUnchecked();
    return true;
}

上面是经过简化的代码,首先调用MessageQueue.next()从消息队列中获取消息,然后调用关键方法msg.target.dispatchMessage(msg)开始消息的分发执行,这个方法之前的文章有进行介绍,这里就不再过多介绍了。

接下来我们看下MessageQueue.next()如何获取消息的。

MessageQueue.next()获取消息

Message next() {
    //...
    for (;;) {
        //1.休眠主线程
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            //2.获取异步消息
            if (msg != null && msg.target == null) {
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            //3.获取普通消息
            if (msg != null) {
                if (now < msg.when) {
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    msg.markInUse();
                    return msg;
                }
            } else {
                nextPollTimeoutMillis = -1;
            }
            
            //...
            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }
        //4.执行Idle消息
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null;

            boolean keep = idler.queueIdle();
            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }
        //...
    }
}
  • 如果当前消息队列中没有消息或者还没到下一条消息的执行时间,就调用nativePollOnce()方法休眠主线程,让出CPU执行权;
  • 如果Message的target为null,就代表是一个消息屏障消息,之后就只能从消息队列获取异步消息了,如果不存在,就尝试执行Idle消息;
  • 如果不存在消息屏障,则就从消息队列中正常尝试获取Message,如果不存在,就尝试执行Idle消息;
  • 执行Idle消息,只有在主线程空闲(当前消息队列中没有消息或者还没到下一条消息的执行时间)的情况下才会去尝试执行Idle消息,这种类型的消息非常有用,具体的可以参考我之前写的文章:IdleHandler基本使用及应用案例分析

总结

本篇文章主要是详细分析了Android消息机制的整个执行流程(不包括native层),最核心的就是HandlerLooperMessageQueueMessage四个类及构成的关联。

到此这篇关于详解Android消息机制完整的执行流程的文章就介绍到这了,更多相关Android消息机制内容请搜索3672js教程以前的文章或继续浏览下面的相关文章希望大家以后多多支持3672js教程!

您可能感兴趣的文章:
  • Android消息机制Handler深入理解
  • Android Handler消息机制分析
  • 代码分析Android消息机制
  • Android 消息机制详解及实例代码
  • Android的消息机制
  • 深入剖析Android消息机制原理

用户评论