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.Config;
     20 import android.util.Printer;
     21 
     22 /**
     23   * Class used to run a message loop for a thread.  Threads by default do
     24   * not have a message loop associated with them; to create one, call
     25   * {@link #prepare} in the thread that is to run the loop, and then
     26   * {@link #loop} to have it process messages until the loop is stopped.
     27   *
     28   * <p>Most interaction with a message loop is through the
     29   * {@link Handler} class.
     30   *
     31   * <p>This is a typical example of the implementation of a Looper thread,
     32   * using the separation of {@link #prepare} and {@link #loop} to create an
     33   * initial Handler to communicate with the Looper.
     34   *
     35   * <pre>
     36   *  class LooperThread extends Thread {
     37   *      public Handler mHandler;
     38   *
     39   *      public void run() {
     40   *          Looper.prepare();
     41   *
     42   *          mHandler = new Handler() {
     43   *              public void handleMessage(Message msg) {
     44   *                  // process incoming messages here
     45   *              }
     46   *          };
     47   *
     48   *          Looper.loop();
     49   *      }
     50   *  }</pre>
     51   */
     52 public class Looper {
     53     private static final boolean DEBUG = false;
     54     private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
     55 
     56     // sThreadLocal.get() will return null unless you've called prepare().
     57     private static final ThreadLocal sThreadLocal = new ThreadLocal();
     58 
     59     final MessageQueue mQueue;
     60     volatile boolean mRun;
     61     Thread mThread;
     62     private Printer mLogging = null;
     63     private static Looper mMainLooper = null;
     64 
     65      /** Initialize the current thread as a looper.
     66       * This gives you a chance to create handlers that then reference
     67       * this looper, before actually starting the loop. Be sure to call
     68       * {@link #loop()} after calling this method, and end it by calling
     69       * {@link #quit()}.
     70       */
     71     public static final void prepare() {
     72         if (sThreadLocal.get() != null) {
     73             throw new RuntimeException("Only one Looper may be created per thread");
     74         }
     75         sThreadLocal.set(new Looper());
     76     }
     77 
     78     /** Initialize the current thread as a looper, marking it as an application's main
     79      *  looper. The main looper for your application is created by the Android environment,
     80      *  so you should never need to call this function yourself.
     81      * {@link #prepare()}
     82      */
     83 
     84     public static final void prepareMainLooper() {
     85         prepare();
     86         setMainLooper(myLooper());
     87         if (Process.supportsProcesses()) {
     88             myLooper().mQueue.mQuitAllowed = false;
     89         }
     90     }
     91 
     92     private synchronized static void setMainLooper(Looper looper) {
     93         mMainLooper = looper;
     94     }
     95 
     96     /** Returns the application's main looper, which lives in the main thread of the application.
     97      */
     98     public synchronized static final Looper getMainLooper() {
     99         return mMainLooper;
    100     }
    101 
    102     /**
    103      *  Run the message queue in this thread. Be sure to call
    104      * {@link #quit()} to end the loop.
    105      */
    106     public static final void loop() {
    107         Looper me = myLooper();
    108         MessageQueue queue = me.mQueue;
    109         while (true) {
    110             Message msg = queue.next(); // might block
    111             //if (!me.mRun) {
    112             //    break;
    113             //}
    114             if (msg != null) {
    115                 if (msg.target == null) {
    116                     // No target is a magic identifier for the quit message.
    117                     return;
    118                 }
    119                 if (me.mLogging!= null) me.mLogging.println(
    120                         ">>>>> Dispatching to " + msg.target + " "
    121                         + msg.callback + ": " + msg.what
    122                         );
    123                 msg.target.dispatchMessage(msg);
    124                 if (me.mLogging!= null) me.mLogging.println(
    125                         "<<<<< Finished to    " + msg.target + " "
    126                         + msg.callback);
    127                 msg.recycle();
    128             }
    129         }
    130     }
    131 
    132     /**
    133      * Return the Looper object associated with the current thread.  Returns
    134      * null if the calling thread is not associated with a Looper.
    135      */
    136     public static final Looper myLooper() {
    137         return (Looper)sThreadLocal.get();
    138     }
    139 
    140     /**
    141      * Control logging of messages as they are processed by this Looper.  If
    142      * enabled, a log message will be written to <var>printer</var>
    143      * at the beginning and ending of each message dispatch, identifying the
    144      * target Handler and message contents.
    145      *
    146      * @param printer A Printer object that will receive log messages, or
    147      * null to disable message logging.
    148      */
    149     public void setMessageLogging(Printer printer) {
    150         mLogging = printer;
    151     }
    152 
    153     /**
    154      * Return the {@link MessageQueue} object associated with the current
    155      * thread.  This must be called from a thread running a Looper, or a
    156      * NullPointerException will be thrown.
    157      */
    158     public static final MessageQueue myQueue() {
    159         return myLooper().mQueue;
    160     }
    161 
    162     private Looper() {
    163         mQueue = new MessageQueue();
    164         mRun = true;
    165         mThread = Thread.currentThread();
    166     }
    167 
    168     public void quit() {
    169         Message msg = Message.obtain();
    170         // NOTE: By enqueueing directly into the message queue, the
    171         // message is left with a null target.  This is how we know it is
    172         // a quit message.
    173         mQueue.enqueueMessage(msg, 0);
    174     }
    175 
    176     /**
    177      * Return the Thread associated with this Looper.
    178      */
    179     public Thread getThread() {
    180         return mThread;
    181     }
    182 
    183     public void dump(Printer pw, String prefix) {
    184         pw.println(prefix + this);
    185         pw.println(prefix + "mRun=" + mRun);
    186         pw.println(prefix + "mThread=" + mThread);
    187         pw.println(prefix + "mQueue=" + ((mQueue != null) ? mQueue : "(null"));
    188         if (mQueue != null) {
    189             synchronized (mQueue) {
    190                 Message msg = mQueue.mMessages;
    191                 int n = 0;
    192                 while (msg != null) {
    193                     pw.println(prefix + "  Message " + n + ": " + msg);
    194                     n++;
    195                     msg = msg.next;
    196                 }
    197                 pw.println(prefix + "(Total messages: " + n + ")");
    198             }
    199         }
    200     }
    201 
    202     public String toString() {
    203         return "Looper{"
    204             + Integer.toHexString(System.identityHashCode(this))
    205             + "}";
    206     }
    207 
    208     static class HandlerException extends Exception {
    209 
    210         HandlerException(Message message, Throwable cause) {
    211             super(createMessage(cause), cause);
    212         }
    213 
    214         static String createMessage(Throwable cause) {
    215             String causeMsg = cause.getMessage();
    216             if (causeMsg == null) {
    217                 causeMsg = cause.toString();
    218             }
    219             return causeMsg;
    220         }
    221     }
    222 }
    223 
    224