首先,需要明确一点,Handler延时消息机制不是延时发送消息,而是延时去处理消息;举个例子,如下:,今天小编就来说说关于android延迟执行优化?下面更多详细答案一起来看看吧!
android延迟执行优化?首先,需要明确一点,Handler 延时消息机制不是延时发送消息,而是延时去处理消息;举个例子,如下:,今天小编就来说说关于android延迟执行优化?下面更多详细答案一起来看看吧!
android延迟执行优化
首先,需要明确一点,Handler 延时消息机制不是延时发送消息,而是延时去处理消息;举个例子,如下:
handler.postDelayed(() ->{Log.e("zjt", "delay runnable");}, 3_000);
上面的 Handler 不是延时3秒后再发送消息,而是将消息插入消息队列后等3秒后再去处理。
postDelayed 的方法如下:
public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {return sendMessageDelayed(getPostMessage(r), delayMillis);}
其中的 getPostMessage 就是将 post 的 runnable 包装成 Message,如下:
private static Message getPostMessage(Runnable r) {// 使用 Message.obtain() 避免重复创建实例对象,达到节约内存的目的Message m = Message.obtain();m.callback = r;return m;}
sendMessageDelayed 方法如下:
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {if (delayMillis < 0) {delayMillis = 0;}// 延时的时间是手机的开机时间(不包括手机休眠时间)+ 需要延时的时间return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}
sendMessageAtTime 如下:
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);}
这里面的代码很好理解,就不说了,看看 enqueueMessage:
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) {msg.target = this; // 设置 msg 的 target 为Handlermsg.workSourceUid = ThreadLocalWorkSource.getUid();// 异步消息,这个需要配合同步屏障来使用,可以看我之前的文章,这里不赘述if (mAsynchronous) {msg.setAsynchronous(true);}// 插入到 MessageQueue 中return queue.enqueueMessage(msg, uptimeMillis);}
MessageQueue 的 enqueueMessage 的方法如下:
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;// 表示要唤醒 Hander 对应的线程,这个后面解释needWake = mBlocked;} else {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.nextprev.next = msg;}// 唤醒Handler对应的线程if (needWake) {nativeWake(mPtr);}}return true;}
举个例子,假设我们消息队列是空的,然后我发送一个延时10s的延时消息,那么会直接把消息存入消息队列。
从消息队列中获取消息是 通过 Looper.loop() 来调用 MessageQueue 的 next()方法,next()的主要代码如下:
Message next() {// Return here if the message loop has already quit and been disposed.// This can happen if the application tries to restart a looper after quit// which is not supported.final long ptr = mPtr;if (ptr == 0) {return null;}int pendingIdleHandlerCount = -1; // -1 only during first iterationint nextPollTimeoutMillis = 0;for (;;) {if (nextPollTimeoutMillis != 0) {Binder.flushPendingCommands();}// 表示要休眠多长时间,功能类似于wait(time)// -1表示一直休眠,// 等于0时,不堵塞// 当有新的消息来时,如果handler对应的线程是阻塞的,那么会唤醒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) { // 计算延时消息的剩余时间 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.nextPollTimeoutMillis = -1;}.......// 判断是否有 idle 任务,即主线程空闲时需要执行的任务,这个下面说if (pendingIdleHandlerCount <= 0) {// 这里表示所有到时间的消息都执行完了,剩下的如果有消息一定是延时且时间还没到的消息;// 刚上面的 enqueueMessage 就是根据这个变量来判断是否要唤醒handler对应的线程mBlocked = true;continue;}......} }
其实,从这里就可以看出来,Handler 的延时消息是如何实现的了。
比方说 发送一个延时10s的消息,那么在 next()方法是,会阻塞 (10s + 发送消息时的系统开机时间 – 执行next()方法是系统的开机时间),到达阻塞时间时会唤醒。或者这时候有新的消息来了也会 根据 mBlocked = true来唤醒。
IdleHandler是什么?
在 MessageQueue 类中有一个 static 的接口 IdleHanlder:
public static interface IdleHandler {boolean queueIdle();}
当MessageQueue中无可处理的Message时回调; 作用:UI线程处理完所有事务后,回调一些额外的操作,且不会堵塞主进程;
接口中只有一个 queueIdle() 函数,线程进入堵塞时执行的额外操作可以写这里, 返回值是true的话,执行完此方法后还会保留这个IdleHandler,否则删除。