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

推荐阅读