Handler中的IdleHandler
1.1 IdleHandler 基本情況
IdleHandler 可以用來提升效能,主要用在我們希望能夠在當前執行緒訊息佇列空閒時 做些事情(例如UI執行緒在顯示完成後,如果執行緒空閒我們就可以提前準備其他內容)的情況下,不過最好不要做耗時操作。
IdleHandler
位於MessageQueue
類中的一個靜態介面,如下:
MessageQueue#IdleHandler class MessageQueue{ ... /** * Callback interface for discovering when a thread is going to block * waiting for more messages. * // 可以理解為訊息暫時處理完的適合回撥的 public static interface IdleHandler { /** * Called when the message queue has run out of messages and will now * wait for more.Return true to keep your idle handler active, false * to have it removed.This may be called if there are still messages * pending in the queue, but they are all scheduled to be dispatched * after the current time. */ //返回true就是單次回撥後不刪除,下次進入空閒時繼續回撥該方法,false 只回調單次執行完之後會移除 boolean queueIdle(); } //判斷當前佇列是不是空閒的,輔助方法 public boolean isIdle() { synchronized (this) { final long now = SystemClock.uptimeMillis(); return mMessages == null || now < mMessages.when; } } //新增一個IdleHandler 到空閒佇列中,ArrayList 儲存 public void addIdleHandler(@NonNull IdleHandler handler) { if (handler == null) { throw new NullPointerException("Can't add a null IdleHandler"); } synchronized (this) { mIdleHandlers.add(handler); } } // 刪除一個IdleHandler public void removeIdleHandler(@NonNull IdleHandler handler) { synchronized (this) { mIdleHandlers.remove(handler); } } //message 的獲取下一條訊息 Message next() { ... int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) {//迴圈獲取下一條訊息 if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } 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) { // Next message is not ready.Set a timeout to wake up when it is ready. 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; } // Process the quit message now that all pending messages have been handled. if (mQuitting) { dispose(); return null; } // If first time idle, then get the number of idlers to run. // Idle handles only run if the queue is empty or if the first message // in the queue (possibly a barrier) is due to be handled in the future. if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } //表示沒有設定idle handler 去執行,就阻塞 if (pendingIdleHandlerCount <= 0) { // No idle handlers to run.Loop and wait some more. mBlocked = true; continue; } //設定長度,最小長度是4 if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } //將集合轉化為陣列 mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // Run the idle handlers. // We only ever reach this code block during the first iteration. //迴圈遍歷這個空閒佇列陣列 for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean keep = false; try { //回撥取出返回值 keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } //這裡看到了吧,如果是返回false, 那就進去了,執行操作後就從集合中remove了 if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } .... } } ... }
1.2 使用場景
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() { @Override public boolean queueIdle() { //這裡做一些操作 Log.i("xx","addIdleHandler invoked..."+iv.getWidth() + "::"+iv.getHeight()); return false; } });
我記得之前我們想要獲取xml中某個控制元件的width
和height
時,在onCreate
方法中直接獲取,會獲取到是 0 , 因為這個view
還未繪製完成,所以獲取不到,當時的解決方案我記得是使用Handler
傳送一個延時訊息獲取,現在有更好的方式實現了,那就是通過IdleHandler
,如上面程式碼所示。 當然還可以做一些其它預處理的簡單操作。
IdleHandler
你明白了嗎?歡迎大家留言討論。