Home | History | Annotate | Download | only in os
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.os;
     18 
     19 import android.util.AndroidRuntimeException;
     20 import android.util.Log;
     21 
     22 import java.util.ArrayList;
     23 
     24 /**
     25  * Low-level class holding the list of messages to be dispatched by a
     26  * {@link Looper}.  Messages are not added directly to a MessageQueue,
     27  * but rather through {@link Handler} objects associated with the Looper.
     28  *
     29  * <p>You can retrieve the MessageQueue for the current thread with
     30  * {@link Looper#myQueue() Looper.myQueue()}.
     31  */
     32 public class MessageQueue {
     33     Message mMessages;
     34     private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
     35     private IdleHandler[] mPendingIdleHandlers;
     36     private boolean mQuiting;
     37     boolean mQuitAllowed = true;
     38 
     39     // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
     40     private boolean mBlocked;
     41 
     42     @SuppressWarnings("unused")
     43     private int mPtr; // used by native code
     44 
     45     private native void nativeInit();
     46     private native void nativeDestroy();
     47     private native void nativePollOnce(int ptr, int timeoutMillis);
     48     private native void nativeWake(int ptr);
     49 
     50     /**
     51      * Callback interface for discovering when a thread is going to block
     52      * waiting for more messages.
     53      */
     54     public static interface IdleHandler {
     55         /**
     56          * Called when the message queue has run out of messages and will now
     57          * wait for more.  Return true to keep your idle handler active, false
     58          * to have it removed.  This may be called if there are still messages
     59          * pending in the queue, but they are all scheduled to be dispatched
     60          * after the current time.
     61          */
     62         boolean queueIdle();
     63     }
     64 
     65     /**
     66      * Add a new {@link IdleHandler} to this message queue.  This may be
     67      * removed automatically for you by returning false from
     68      * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
     69      * invoked, or explicitly removing it with {@link #removeIdleHandler}.
     70      *
     71      * <p>This method is safe to call from any thread.
     72      *
     73      * @param handler The IdleHandler to be added.
     74      */
     75     public final void addIdleHandler(IdleHandler handler) {
     76         if (handler == null) {
     77             throw new NullPointerException("Can't add a null IdleHandler");
     78         }
     79         synchronized (this) {
     80             mIdleHandlers.add(handler);
     81         }
     82     }
     83 
     84     /**
     85      * Remove an {@link IdleHandler} from the queue that was previously added
     86      * with {@link #addIdleHandler}.  If the given object is not currently
     87      * in the idle list, nothing is done.
     88      *
     89      * @param handler The IdleHandler to be removed.
     90      */
     91     public final void removeIdleHandler(IdleHandler handler) {
     92         synchronized (this) {
     93             mIdleHandlers.remove(handler);
     94         }
     95     }
     96 
     97     MessageQueue() {
     98         nativeInit();
     99     }
    100 
    101     @Override
    102     protected void finalize() throws Throwable {
    103         try {
    104             nativeDestroy();
    105         } finally {
    106             super.finalize();
    107         }
    108     }
    109 
    110     final Message next() {
    111         int pendingIdleHandlerCount = -1; // -1 only during first iteration
    112         int nextPollTimeoutMillis = 0;
    113 
    114         for (;;) {
    115             if (nextPollTimeoutMillis != 0) {
    116                 Binder.flushPendingCommands();
    117             }
    118             nativePollOnce(mPtr, nextPollTimeoutMillis);
    119 
    120             synchronized (this) {
    121                 // Try to retrieve the next message.  Return if found.
    122                 final long now = SystemClock.uptimeMillis();
    123                 final Message msg = mMessages;
    124                 if (msg != null) {
    125                     final long when = msg.when;
    126                     if (now >= when) {
    127                         mBlocked = false;
    128                         mMessages = msg.next;
    129                         msg.next = null;
    130                         if (false) Log.v("MessageQueue", "Returning message: " + msg);
    131                         msg.markInUse();
    132                         return msg;
    133                     } else {
    134                         nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
    135                     }
    136                 } else {
    137                     nextPollTimeoutMillis = -1;
    138                 }
    139 
    140                 // If first time, then get the number of idlers to run.
    141                 if (pendingIdleHandlerCount < 0) {
    142                     pendingIdleHandlerCount = mIdleHandlers.size();
    143                 }
    144                 if (pendingIdleHandlerCount == 0) {
    145                     // No idle handlers to run.  Loop and wait some more.
    146                     mBlocked = true;
    147                     continue;
    148                 }
    149 
    150                 if (mPendingIdleHandlers == null) {
    151                     mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
    152                 }
    153                 mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
    154             }
    155 
    156             // Run the idle handlers.
    157             // We only ever reach this code block during the first iteration.
    158             for (int i = 0; i < pendingIdleHandlerCount; i++) {
    159                 final IdleHandler idler = mPendingIdleHandlers[i];
    160                 mPendingIdleHandlers[i] = null; // release the reference to the handler
    161 
    162                 boolean keep = false;
    163                 try {
    164                     keep = idler.queueIdle();
    165                 } catch (Throwable t) {
    166                     Log.wtf("MessageQueue", "IdleHandler threw exception", t);
    167                 }
    168 
    169                 if (!keep) {
    170                     synchronized (this) {
    171                         mIdleHandlers.remove(idler);
    172                     }
    173                 }
    174             }
    175 
    176             // Reset the idle handler count to 0 so we do not run them again.
    177             pendingIdleHandlerCount = 0;
    178 
    179             // While calling an idle handler, a new message could have been delivered
    180             // so go back and look again for a pending message without waiting.
    181             nextPollTimeoutMillis = 0;
    182         }
    183     }
    184 
    185     final boolean enqueueMessage(Message msg, long when) {
    186         if (msg.isInUse()) {
    187             throw new AndroidRuntimeException(msg
    188                     + " This message is already in use.");
    189         }
    190         if (msg.target == null && !mQuitAllowed) {
    191             throw new RuntimeException("Main thread not allowed to quit");
    192         }
    193         final boolean needWake;
    194         synchronized (this) {
    195             if (mQuiting) {
    196                 RuntimeException e = new RuntimeException(
    197                     msg.target + " sending message to a Handler on a dead thread");
    198                 Log.w("MessageQueue", e.getMessage(), e);
    199                 return false;
    200             } else if (msg.target == null) {
    201                 mQuiting = true;
    202             }
    203 
    204             msg.when = when;
    205             //Log.d("MessageQueue", "Enqueing: " + msg);
    206             Message p = mMessages;
    207             if (p == null || when == 0 || when < p.when) {
    208                 msg.next = p;
    209                 mMessages = msg;
    210                 needWake = mBlocked; // new head, might need to wake up
    211             } else {
    212                 Message prev = null;
    213                 while (p != null && p.when <= when) {
    214                     prev = p;
    215                     p = p.next;
    216                 }
    217                 msg.next = prev.next;
    218                 prev.next = msg;
    219                 needWake = false; // still waiting on head, no need to wake up
    220             }
    221         }
    222         if (needWake) {
    223             nativeWake(mPtr);
    224         }
    225         return true;
    226     }
    227 
    228     final boolean removeMessages(Handler h, int what, Object object,
    229             boolean doRemove) {
    230         synchronized (this) {
    231             Message p = mMessages;
    232             boolean found = false;
    233 
    234             // Remove all messages at front.
    235             while (p != null && p.target == h && p.what == what
    236                    && (object == null || p.obj == object)) {
    237                 if (!doRemove) return true;
    238                 found = true;
    239                 Message n = p.next;
    240                 mMessages = n;
    241                 p.recycle();
    242                 p = n;
    243             }
    244 
    245             // Remove all messages after front.
    246             while (p != null) {
    247                 Message n = p.next;
    248                 if (n != null) {
    249                     if (n.target == h && n.what == what
    250                         && (object == null || n.obj == object)) {
    251                         if (!doRemove) return true;
    252                         found = true;
    253                         Message nn = n.next;
    254                         n.recycle();
    255                         p.next = nn;
    256                         continue;
    257                     }
    258                 }
    259                 p = n;
    260             }
    261 
    262             return found;
    263         }
    264     }
    265 
    266     final void removeMessages(Handler h, Runnable r, Object object) {
    267         if (r == null) {
    268             return;
    269         }
    270 
    271         synchronized (this) {
    272             Message p = mMessages;
    273 
    274             // Remove all messages at front.
    275             while (p != null && p.target == h && p.callback == r
    276                    && (object == null || p.obj == object)) {
    277                 Message n = p.next;
    278                 mMessages = n;
    279                 p.recycle();
    280                 p = n;
    281             }
    282 
    283             // Remove all messages after front.
    284             while (p != null) {
    285                 Message n = p.next;
    286                 if (n != null) {
    287                     if (n.target == h && n.callback == r
    288                         && (object == null || n.obj == object)) {
    289                         Message nn = n.next;
    290                         n.recycle();
    291                         p.next = nn;
    292                         continue;
    293                     }
    294                 }
    295                 p = n;
    296             }
    297         }
    298     }
    299 
    300     final void removeCallbacksAndMessages(Handler h, Object object) {
    301         synchronized (this) {
    302             Message p = mMessages;
    303 
    304             // Remove all messages at front.
    305             while (p != null && p.target == h
    306                     && (object == null || p.obj == object)) {
    307                 Message n = p.next;
    308                 mMessages = n;
    309                 p.recycle();
    310                 p = n;
    311             }
    312 
    313             // Remove all messages after front.
    314             while (p != null) {
    315                 Message n = p.next;
    316                 if (n != null) {
    317                     if (n.target == h && (object == null || n.obj == object)) {
    318                         Message nn = n.next;
    319                         n.recycle();
    320                         p.next = nn;
    321                         continue;
    322                     }
    323                 }
    324                 p = n;
    325             }
    326         }
    327     }
    328 
    329     /*
    330     private void dumpQueue_l()
    331     {
    332         Message p = mMessages;
    333         System.out.println(this + "  queue is:");
    334         while (p != null) {
    335             System.out.println("            " + p);
    336             p = p.next;
    337         }
    338     }
    339     */
    340 }
    341