• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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       */
prepare()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 
prepareMainLooper()84     public static final void prepareMainLooper() {
85         prepare();
86         setMainLooper(myLooper());
87         if (Process.supportsProcesses()) {
88             myLooper().mQueue.mQuitAllowed = false;
89         }
90     }
91 
setMainLooper(Looper looper)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      */
getMainLooper()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      */
loop()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      */
myLooper()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      */
setMessageLogging(Printer printer)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      */
myQueue()158     public static final MessageQueue myQueue() {
159         return myLooper().mQueue;
160     }
161 
Looper()162     private Looper() {
163         mQueue = new MessageQueue();
164         mRun = true;
165         mThread = Thread.currentThread();
166     }
167 
quit()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      */
getThread()179     public Thread getThread() {
180         return mThread;
181     }
182 
dump(Printer pw, String prefix)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 
toString()202     public String toString() {
203         return "Looper{"
204             + Integer.toHexString(System.identityHashCode(this))
205             + "}";
206     }
207 
208     static class HandlerException extends Exception {
209 
HandlerException(Message message, Throwable cause)210         HandlerException(Message message, Throwable cause) {
211             super(createMessage(cause), cause);
212         }
213 
createMessage(Throwable cause)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