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 为handler
msg.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.next
prev.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 iteration
int 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,否则删除。
上一篇: Android线程池源码阅读记录介绍