電腦wifi共享軟件哪個好用 電腦wifi共享軟件哪個好用
2023-08-25
{ Log.e("zjt", "delay ru" />
更新時間:2023-08-25 09:08:02作者:未知
首先,需要明確一點,Handler 延時消息機制不是延時發(fā)送消息,而是延時去處理消息;舉個例子,如下:
handler.postDelayed(() ->{ Log.e("zjt", "delay runnable"); }, 3_000);
上面的 Handler 不是延時3秒后再發(fā)送消息,而是將消息插入消息隊列后等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() 避免重復(fù)創(chuàng)建實例對象,達到節(jié)約內(nèi)存的目的 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; // 設(shè)置 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) { // 判斷發(fā)送消息的進程是否還活著 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(); // 標(biāo)記消息正在使用 msg.when = when; Message p = mMessages; // 獲取表頭消息 boolean needWake; // 如果隊列中沒有消息 或者 消息為即時消息 或者 表頭消息時間大于當(dāng)前消息的延時時間 if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; // 表示要喚醒 Hander 對應(yīng)的線程,這個后面解釋 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對應(yīng)的線程 if (needWake) { nativeWake(mPtr); } } return true; }
舉個例子,假設(shè)我們消息隊列是空的,然后我發(fā)送一個延時10s的延時消息,那么會直接把消息存入消息隊列。
從消息隊列中獲取消息是 通過 Looper.loop() 來調(diào)用 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時,不堵塞 // 當(dāng)有新的消息來時,如果handler對應(yīng)的線程是阻塞的,那么會喚醒 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 任務(wù),即主線程空閑時需要執(zhí)行的任務(wù),這個下面說 if (pendingIdleHandlerCount <= 0) { // 這里表示所有到時間的消息都執(zhí)行完了,剩下的如果有消息一定是延時且時間還沒到的消息; // 剛上面的 enqueueMessage 就是根據(jù)這個變量來判斷是否要喚醒handler對應(yīng)的線程 mBlocked = true; continue; } ...... } }
其實,從這里就可以看出來,Handler 的延時消息是如何實現(xiàn)的了。
比方說 發(fā)送一個延時10s的消息,那么在 next()方法是,會阻塞 (10s + 發(fā)送消息時的系統(tǒng)開機時間 – 執(zhí)行next()方法是系統(tǒng)的開機時間),到達阻塞時間時會喚醒?;蛘哌@時候有新的消息來了也會 根據(jù) mBlocked = true來喚醒。
在 MessageQueue 類中有一個 static 的接口 IdleHanlder:
public static interface IdleHandler { boolean queueIdle(); }
當(dāng)MessageQueue中無可處理的Message時回調(diào); 作用:UI線程處理完所有事務(wù)后,回調(diào)一些額外的操作,且不會堵塞主進程;
接口中只有一個 queueIdle() 函數(shù),線程進入堵塞時執(zhí)行的額外操作可以寫這里, 返回值是true的話,執(zhí)行完此方法后還會保留這個IdleHandler,否則刪除。