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.annotation.NonNull;
     20 import android.annotation.Nullable;
     21 import android.os.LooperProto;
     22 import android.util.Log;
     23 import android.util.Printer;
     24 import android.util.Slog;
     25 import android.util.proto.ProtoOutputStream;
     26 
     27 /**
     28   * Class used to run a message loop for a thread.  Threads by default do
     29   * not have a message loop associated with them; to create one, call
     30   * {@link #prepare} in the thread that is to run the loop, and then
     31   * {@link #loop} to have it process messages until the loop is stopped.
     32   *
     33   * <p>Most interaction with a message loop is through the
     34   * {@link Handler} class.
     35   *
     36   * <p>This is a typical example of the implementation of a Looper thread,
     37   * using the separation of {@link #prepare} and {@link #loop} to create an
     38   * initial Handler to communicate with the Looper.
     39   *
     40   * <pre>
     41   *  class LooperThread extends Thread {
     42   *      public Handler mHandler;
     43   *
     44   *      public void run() {
     45   *          Looper.prepare();
     46   *
     47   *          mHandler = new Handler() {
     48   *              public void handleMessage(Message msg) {
     49   *                  // process incoming messages here
     50   *              }
     51   *          };
     52   *
     53   *          Looper.loop();
     54   *      }
     55   *  }</pre>
     56   */
     57 public final class Looper {
     58     /*
     59      * API Implementation Note:
     60      *
     61      * This class contains the code required to set up and manage an event loop
     62      * based on MessageQueue.  APIs that affect the state of the queue should be
     63      * defined on MessageQueue or Handler rather than on Looper itself.  For example,
     64      * idle handlers and sync barriers are defined on the queue whereas preparing the
     65      * thread, looping, and quitting are defined on the looper.
     66      */
     67 
     68     private static final String TAG = "Looper";
     69 
     70     // sThreadLocal.get() will return null unless you've called prepare().
     71     static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
     72     private static Looper sMainLooper;  // guarded by Looper.class
     73 
     74     final MessageQueue mQueue;
     75     final Thread mThread;
     76 
     77     private Printer mLogging;
     78     private long mTraceTag;
     79 
     80     /* If set, the looper will show a warning log if a message dispatch takes longer than time. */
     81     private long mSlowDispatchThresholdMs;
     82 
     83      /** Initialize the current thread as a looper.
     84       * This gives you a chance to create handlers that then reference
     85       * this looper, before actually starting the loop. Be sure to call
     86       * {@link #loop()} after calling this method, and end it by calling
     87       * {@link #quit()}.
     88       */
     89     public static void prepare() {
     90         prepare(true);
     91     }
     92 
     93     private static void prepare(boolean quitAllowed) {
     94         if (sThreadLocal.get() != null) {
     95             throw new RuntimeException("Only one Looper may be created per thread");
     96         }
     97         sThreadLocal.set(new Looper(quitAllowed));
     98     }
     99 
    100     /**
    101      * Initialize the current thread as a looper, marking it as an
    102      * application's main looper. The main looper for your application
    103      * is created by the Android environment, so you should never need
    104      * to call this function yourself.  See also: {@link #prepare()}
    105      */
    106     public static void prepareMainLooper() {
    107         prepare(false);
    108         synchronized (Looper.class) {
    109             if (sMainLooper != null) {
    110                 throw new IllegalStateException("The main Looper has already been prepared.");
    111             }
    112             sMainLooper = myLooper();
    113         }
    114     }
    115 
    116     /**
    117      * Returns the application's main looper, which lives in the main thread of the application.
    118      */
    119     public static Looper getMainLooper() {
    120         synchronized (Looper.class) {
    121             return sMainLooper;
    122         }
    123     }
    124 
    125     /**
    126      * Run the message queue in this thread. Be sure to call
    127      * {@link #quit()} to end the loop.
    128      */
    129     public static void loop() {
    130         final Looper me = myLooper();
    131         if (me == null) {
    132             throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    133         }
    134         final MessageQueue queue = me.mQueue;
    135 
    136         // Make sure the identity of this thread is that of the local process,
    137         // and keep track of what that identity token actually is.
    138         Binder.clearCallingIdentity();
    139         final long ident = Binder.clearCallingIdentity();
    140 
    141         for (;;) {
    142             Message msg = queue.next(); // might block
    143             if (msg == null) {
    144                 // No message indicates that the message queue is quitting.
    145                 return;
    146             }
    147 
    148             // This must be in a local variable, in case a UI event sets the logger
    149             final Printer logging = me.mLogging;
    150             if (logging != null) {
    151                 logging.println(">>>>> Dispatching to " + msg.target + " " +
    152                         msg.callback + ": " + msg.what);
    153             }
    154 
    155             final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
    156 
    157             final long traceTag = me.mTraceTag;
    158             if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
    159                 Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
    160             }
    161             final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
    162             final long end;
    163             try {
    164                 msg.target.dispatchMessage(msg);
    165                 end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
    166             } finally {
    167                 if (traceTag != 0) {
    168                     Trace.traceEnd(traceTag);
    169                 }
    170             }
    171             if (slowDispatchThresholdMs > 0) {
    172                 final long time = end - start;
    173                 if (time > slowDispatchThresholdMs) {
    174                     Slog.w(TAG, "Dispatch took " + time + "ms on "
    175                             + Thread.currentThread().getName() + ", h=" +
    176                             msg.target + " cb=" + msg.callback + " msg=" + msg.what);
    177                 }
    178             }
    179 
    180             if (logging != null) {
    181                 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
    182             }
    183 
    184             // Make sure that during the course of dispatching the
    185             // identity of the thread wasn't corrupted.
    186             final long newIdent = Binder.clearCallingIdentity();
    187             if (ident != newIdent) {
    188                 Log.wtf(TAG, "Thread identity changed from 0x"
    189                         + Long.toHexString(ident) + " to 0x"
    190                         + Long.toHexString(newIdent) + " while dispatching to "
    191                         + msg.target.getClass().getName() + " "
    192                         + msg.callback + " what=" + msg.what);
    193             }
    194 
    195             msg.recycleUnchecked();
    196         }
    197     }
    198 
    199     /**
    200      * Return the Looper object associated with the current thread.  Returns
    201      * null if the calling thread is not associated with a Looper.
    202      */
    203     public static @Nullable Looper myLooper() {
    204         return sThreadLocal.get();
    205     }
    206 
    207     /**
    208      * Return the {@link MessageQueue} object associated with the current
    209      * thread.  This must be called from a thread running a Looper, or a
    210      * NullPointerException will be thrown.
    211      */
    212     public static @NonNull MessageQueue myQueue() {
    213         return myLooper().mQueue;
    214     }
    215 
    216     private Looper(boolean quitAllowed) {
    217         mQueue = new MessageQueue(quitAllowed);
    218         mThread = Thread.currentThread();
    219     }
    220 
    221     /**
    222      * Returns true if the current thread is this looper's thread.
    223      */
    224     public boolean isCurrentThread() {
    225         return Thread.currentThread() == mThread;
    226     }
    227 
    228     /**
    229      * Control logging of messages as they are processed by this Looper.  If
    230      * enabled, a log message will be written to <var>printer</var>
    231      * at the beginning and ending of each message dispatch, identifying the
    232      * target Handler and message contents.
    233      *
    234      * @param printer A Printer object that will receive log messages, or
    235      * null to disable message logging.
    236      */
    237     public void setMessageLogging(@Nullable Printer printer) {
    238         mLogging = printer;
    239     }
    240 
    241     /** {@hide} */
    242     public void setTraceTag(long traceTag) {
    243         mTraceTag = traceTag;
    244     }
    245 
    246     /** {@hide} */
    247     public void setSlowDispatchThresholdMs(long slowDispatchThresholdMs) {
    248         mSlowDispatchThresholdMs = slowDispatchThresholdMs;
    249     }
    250 
    251     /**
    252      * Quits the looper.
    253      * <p>
    254      * Causes the {@link #loop} method to terminate without processing any
    255      * more messages in the message queue.
    256      * </p><p>
    257      * Any attempt to post messages to the queue after the looper is asked to quit will fail.
    258      * For example, the {@link Handler#sendMessage(Message)} method will return false.
    259      * </p><p class="note">
    260      * Using this method may be unsafe because some messages may not be delivered
    261      * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
    262      * that all pending work is completed in an orderly manner.
    263      * </p>
    264      *
    265      * @see #quitSafely
    266      */
    267     public void quit() {
    268         mQueue.quit(false);
    269     }
    270 
    271     /**
    272      * Quits the looper safely.
    273      * <p>
    274      * Causes the {@link #loop} method to terminate as soon as all remaining messages
    275      * in the message queue that are already due to be delivered have been handled.
    276      * However pending delayed messages with due times in the future will not be
    277      * delivered before the loop terminates.
    278      * </p><p>
    279      * Any attempt to post messages to the queue after the looper is asked to quit will fail.
    280      * For example, the {@link Handler#sendMessage(Message)} method will return false.
    281      * </p>
    282      */
    283     public void quitSafely() {
    284         mQueue.quit(true);
    285     }
    286 
    287     /**
    288      * Gets the Thread associated with this Looper.
    289      *
    290      * @return The looper's thread.
    291      */
    292     public @NonNull Thread getThread() {
    293         return mThread;
    294     }
    295 
    296     /**
    297      * Gets this looper's message queue.
    298      *
    299      * @return The looper's message queue.
    300      */
    301     public @NonNull MessageQueue getQueue() {
    302         return mQueue;
    303     }
    304 
    305     /**
    306      * Dumps the state of the looper for debugging purposes.
    307      *
    308      * @param pw A printer to receive the contents of the dump.
    309      * @param prefix A prefix to prepend to each line which is printed.
    310      */
    311     public void dump(@NonNull Printer pw, @NonNull String prefix) {
    312         pw.println(prefix + toString());
    313         mQueue.dump(pw, prefix + "  ", null);
    314     }
    315 
    316     /**
    317      * Dumps the state of the looper for debugging purposes.
    318      *
    319      * @param pw A printer to receive the contents of the dump.
    320      * @param prefix A prefix to prepend to each line which is printed.
    321      * @param handler Only dump messages for this Handler.
    322      * @hide
    323      */
    324     public void dump(@NonNull Printer pw, @NonNull String prefix, Handler handler) {
    325         pw.println(prefix + toString());
    326         mQueue.dump(pw, prefix + "  ", handler);
    327     }
    328 
    329     /** @hide */
    330     public void writeToProto(ProtoOutputStream proto, long fieldId) {
    331         final long looperToken = proto.start(fieldId);
    332         proto.write(LooperProto.THREAD_NAME, mThread.getName());
    333         proto.write(LooperProto.THREAD_ID, mThread.getId());
    334         proto.write(LooperProto.IDENTITY_HASH_CODE, System.identityHashCode(this));
    335         mQueue.writeToProto(proto, LooperProto.QUEUE);
    336         proto.end(looperToken);
    337     }
    338 
    339     @Override
    340     public String toString() {
    341         return "Looper (" + mThread.getName() + ", tid " + mThread.getId()
    342                 + ") {" + Integer.toHexString(System.identityHashCode(this)) + "}";
    343     }
    344 }
    345