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.Log;
     20 import android.util.Printer;
     21 import android.util.PrefixPrinter;
     22 
     23 /**
     24   * Class used to run a message loop for a thread.  Threads by default do
     25   * not have a message loop associated with them; to create one, call
     26   * {@link #prepare} in the thread that is to run the loop, and then
     27   * {@link #loop} to have it process messages until the loop is stopped.
     28   *
     29   * <p>Most interaction with a message loop is through the
     30   * {@link Handler} class.
     31   *
     32   * <p>This is a typical example of the implementation of a Looper thread,
     33   * using the separation of {@link #prepare} and {@link #loop} to create an
     34   * initial Handler to communicate with the Looper.
     35   *
     36   * <pre>
     37   *  class LooperThread extends Thread {
     38   *      public Handler mHandler;
     39   *
     40   *      public void run() {
     41   *          Looper.prepare();
     42   *
     43   *          mHandler = new Handler() {
     44   *              public void handleMessage(Message msg) {
     45   *                  // process incoming messages here
     46   *              }
     47   *          };
     48   *
     49   *          Looper.loop();
     50   *      }
     51   *  }</pre>
     52   */
     53 public class Looper {
     54     private static final String TAG = "Looper";
     55 
     56     // sThreadLocal.get() will return null unless you've called prepare().
     57     static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
     58     private static Looper sMainLooper;  // guarded by Looper.class
     59 
     60     final MessageQueue mQueue;
     61     final Thread mThread;
     62     volatile boolean mRun;
     63 
     64     private Printer mLogging;
     65 
     66      /** Initialize the current thread as a looper.
     67       * This gives you a chance to create handlers that then reference
     68       * this looper, before actually starting the loop. Be sure to call
     69       * {@link #loop()} after calling this method, and end it by calling
     70       * {@link #quit()}.
     71       */
     72     public static void prepare() {
     73         prepare(true);
     74     }
     75 
     76     private static void prepare(boolean quitAllowed) {
     77         if (sThreadLocal.get() != null) {
     78             throw new RuntimeException("Only one Looper may be created per thread");
     79         }
     80         sThreadLocal.set(new Looper(quitAllowed));
     81     }
     82 
     83     /**
     84      * Initialize the current thread as a looper, marking it as an
     85      * application's main looper. The main looper for your application
     86      * is created by the Android environment, so you should never need
     87      * to call this function yourself.  See also: {@link #prepare()}
     88      */
     89     public static void prepareMainLooper() {
     90         prepare(false);
     91         synchronized (Looper.class) {
     92             if (sMainLooper != null) {
     93                 throw new IllegalStateException("The main Looper has already been prepared.");
     94             }
     95             sMainLooper = myLooper();
     96         }
     97     }
     98 
     99     /** Returns the application's main looper, which lives in the main thread of the application.
    100      */
    101     public static Looper getMainLooper() {
    102         synchronized (Looper.class) {
    103             return sMainLooper;
    104         }
    105     }
    106 
    107     /**
    108      * Run the message queue in this thread. Be sure to call
    109      * {@link #quit()} to end the loop.
    110      */
    111     public static void loop() {
    112         final Looper me = myLooper();
    113         if (me == null) {
    114             throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    115         }
    116         final MessageQueue queue = me.mQueue;
    117 
    118         // Make sure the identity of this thread is that of the local process,
    119         // and keep track of what that identity token actually is.
    120         Binder.clearCallingIdentity();
    121         final long ident = Binder.clearCallingIdentity();
    122 
    123         for (;;) {
    124             Message msg = queue.next(); // might block
    125             if (msg == null) {
    126                 // No message indicates that the message queue is quitting.
    127                 return;
    128             }
    129 
    130             // This must be in a local variable, in case a UI event sets the logger
    131             Printer logging = me.mLogging;
    132             if (logging != null) {
    133                 logging.println(">>>>> Dispatching to " + msg.target + " " +
    134                         msg.callback + ": " + msg.what);
    135             }
    136 
    137             msg.target.dispatchMessage(msg);
    138 
    139             if (logging != null) {
    140                 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
    141             }
    142 
    143             // Make sure that during the course of dispatching the
    144             // identity of the thread wasn't corrupted.
    145             final long newIdent = Binder.clearCallingIdentity();
    146             if (ident != newIdent) {
    147                 Log.wtf(TAG, "Thread identity changed from 0x"
    148                         + Long.toHexString(ident) + " to 0x"
    149                         + Long.toHexString(newIdent) + " while dispatching to "
    150                         + msg.target.getClass().getName() + " "
    151                         + msg.callback + " what=" + msg.what);
    152             }
    153 
    154             msg.recycle();
    155         }
    156     }
    157 
    158     /**
    159      * Return the Looper object associated with the current thread.  Returns
    160      * null if the calling thread is not associated with a Looper.
    161      */
    162     public static Looper myLooper() {
    163         return sThreadLocal.get();
    164     }
    165 
    166     /**
    167      * Control logging of messages as they are processed by this Looper.  If
    168      * enabled, a log message will be written to <var>printer</var>
    169      * at the beginning and ending of each message dispatch, identifying the
    170      * target Handler and message contents.
    171      *
    172      * @param printer A Printer object that will receive log messages, or
    173      * null to disable message logging.
    174      */
    175     public void setMessageLogging(Printer printer) {
    176         mLogging = printer;
    177     }
    178 
    179     /**
    180      * Return the {@link MessageQueue} object associated with the current
    181      * thread.  This must be called from a thread running a Looper, or a
    182      * NullPointerException will be thrown.
    183      */
    184     public static MessageQueue myQueue() {
    185         return myLooper().mQueue;
    186     }
    187 
    188     private Looper(boolean quitAllowed) {
    189         mQueue = new MessageQueue(quitAllowed);
    190         mRun = true;
    191         mThread = Thread.currentThread();
    192     }
    193 
    194     /**
    195      * Quits the looper.
    196      *
    197      * Causes the {@link #loop} method to terminate as soon as possible.
    198      */
    199     public void quit() {
    200         mQueue.quit();
    201     }
    202 
    203     /**
    204      * Posts a synchronization barrier to the Looper's message queue.
    205      *
    206      * Message processing occurs as usual until the message queue encounters the
    207      * synchronization barrier that has been posted.  When the barrier is encountered,
    208      * later synchronous messages in the queue are stalled (prevented from being executed)
    209      * until the barrier is released by calling {@link #removeSyncBarrier} and specifying
    210      * the token that identifies the synchronization barrier.
    211      *
    212      * This method is used to immediately postpone execution of all subsequently posted
    213      * synchronous messages until a condition is met that releases the barrier.
    214      * Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier
    215      * and continue to be processed as usual.
    216      *
    217      * This call must be always matched by a call to {@link #removeSyncBarrier} with
    218      * the same token to ensure that the message queue resumes normal operation.
    219      * Otherwise the application will probably hang!
    220      *
    221      * @return A token that uniquely identifies the barrier.  This token must be
    222      * passed to {@link #removeSyncBarrier} to release the barrier.
    223      *
    224      * @hide
    225      */
    226     public final int postSyncBarrier() {
    227         return mQueue.enqueueSyncBarrier(SystemClock.uptimeMillis());
    228     }
    229 
    230 
    231     /**
    232      * Removes a synchronization barrier.
    233      *
    234      * @param token The synchronization barrier token that was returned by
    235      * {@link #postSyncBarrier}.
    236      *
    237      * @throws IllegalStateException if the barrier was not found.
    238      *
    239      * @hide
    240      */
    241     public final void removeSyncBarrier(int token) {
    242         mQueue.removeSyncBarrier(token);
    243     }
    244 
    245     /**
    246      * Return the Thread associated with this Looper.
    247      */
    248     public Thread getThread() {
    249         return mThread;
    250     }
    251 
    252     /** @hide */
    253     public MessageQueue getQueue() {
    254         return mQueue;
    255     }
    256 
    257     public void dump(Printer pw, String prefix) {
    258         pw = PrefixPrinter.create(pw, prefix);
    259         pw.println(this.toString());
    260         pw.println("mRun=" + mRun);
    261         pw.println("mThread=" + mThread);
    262         pw.println("mQueue=" + ((mQueue != null) ? mQueue : "(null"));
    263         if (mQueue != null) {
    264             synchronized (mQueue) {
    265                 long now = SystemClock.uptimeMillis();
    266                 Message msg = mQueue.mMessages;
    267                 int n = 0;
    268                 while (msg != null) {
    269                     pw.println("  Message " + n + ": " + msg.toString(now));
    270                     n++;
    271                     msg = msg.next;
    272                 }
    273                 pw.println("(Total messages: " + n + ")");
    274             }
    275         }
    276     }
    277 
    278     public String toString() {
    279         return "Looper{" + Integer.toHexString(System.identityHashCode(this)) + "}";
    280     }
    281 }
    282