• 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.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 
59     final MessageQueue mQueue;
60     final Thread mThread;
61     volatile boolean mRun;
62 
63     private Printer mLogging = null;
64     private static Looper mMainLooper = null;  // guarded by Looper.class
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       */
prepare()72     public static void prepare() {
73         if (sThreadLocal.get() != null) {
74             throw new RuntimeException("Only one Looper may be created per thread");
75         }
76         sThreadLocal.set(new Looper());
77     }
78 
79     /**
80      * Initialize the current thread as a looper, marking it as an
81      * application's main looper. The main looper for your application
82      * is created by the Android environment, so you should never need
83      * to call this function yourself.  See also: {@link #prepare()}
84      */
prepareMainLooper()85     public static void prepareMainLooper() {
86         prepare();
87         setMainLooper(myLooper());
88         myLooper().mQueue.mQuitAllowed = false;
89     }
90 
setMainLooper(Looper looper)91     private synchronized static void setMainLooper(Looper looper) {
92         mMainLooper = looper;
93     }
94 
95     /** Returns the application's main looper, which lives in the main thread of the application.
96      */
getMainLooper()97     public synchronized static Looper getMainLooper() {
98         return mMainLooper;
99     }
100 
101     /**
102      * Run the message queue in this thread. Be sure to call
103      * {@link #quit()} to end the loop.
104      */
loop()105     public static void loop() {
106         Looper me = myLooper();
107         if (me == null) {
108             throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
109         }
110         MessageQueue queue = me.mQueue;
111 
112         // Make sure the identity of this thread is that of the local process,
113         // and keep track of what that identity token actually is.
114         Binder.clearCallingIdentity();
115         final long ident = Binder.clearCallingIdentity();
116 
117         while (true) {
118             Message msg = queue.next(); // might block
119             if (msg != null) {
120                 if (msg.target == null) {
121                     // No target is a magic identifier for the quit message.
122                     return;
123                 }
124 
125                 long wallStart = 0;
126                 long threadStart = 0;
127 
128                 // This must be in a local variable, in case a UI event sets the logger
129                 Printer logging = me.mLogging;
130                 if (logging != null) {
131                     logging.println(">>>>> Dispatching to " + msg.target + " " +
132                             msg.callback + ": " + msg.what);
133                     wallStart = SystemClock.currentTimeMicro();
134                     threadStart = SystemClock.currentThreadTimeMicro();
135                 }
136 
137                 msg.target.dispatchMessage(msg);
138 
139                 if (logging != null) {
140                     long wallTime = SystemClock.currentTimeMicro() - wallStart;
141                     long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;
142 
143                     logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
144                     if (logging instanceof Profiler) {
145                         ((Profiler) logging).profile(msg, wallStart, wallTime,
146                                 threadStart, threadTime);
147                     }
148                 }
149 
150                 // Make sure that during the course of dispatching the
151                 // identity of the thread wasn't corrupted.
152                 final long newIdent = Binder.clearCallingIdentity();
153                 if (ident != newIdent) {
154                     Log.wtf(TAG, "Thread identity changed from 0x"
155                             + Long.toHexString(ident) + " to 0x"
156                             + Long.toHexString(newIdent) + " while dispatching to "
157                             + msg.target.getClass().getName() + " "
158                             + msg.callback + " what=" + msg.what);
159                 }
160 
161                 msg.recycle();
162             }
163         }
164     }
165 
166     /**
167      * Return the Looper object associated with the current thread.  Returns
168      * null if the calling thread is not associated with a Looper.
169      */
myLooper()170     public static Looper myLooper() {
171         return sThreadLocal.get();
172     }
173 
174     /**
175      * Control logging of messages as they are processed by this Looper.  If
176      * enabled, a log message will be written to <var>printer</var>
177      * at the beginning and ending of each message dispatch, identifying the
178      * target Handler and message contents.
179      *
180      * @param printer A Printer object that will receive log messages, or
181      * null to disable message logging.
182      */
setMessageLogging(Printer printer)183     public void setMessageLogging(Printer printer) {
184         mLogging = printer;
185     }
186 
187     /**
188      * Return the {@link MessageQueue} object associated with the current
189      * thread.  This must be called from a thread running a Looper, or a
190      * NullPointerException will be thrown.
191      */
myQueue()192     public static MessageQueue myQueue() {
193         return myLooper().mQueue;
194     }
195 
Looper()196     private Looper() {
197         mQueue = new MessageQueue();
198         mRun = true;
199         mThread = Thread.currentThread();
200     }
201 
quit()202     public void quit() {
203         Message msg = Message.obtain();
204         // NOTE: By enqueueing directly into the message queue, the
205         // message is left with a null target.  This is how we know it is
206         // a quit message.
207         mQueue.enqueueMessage(msg, 0);
208     }
209 
210     /**
211      * Return the Thread associated with this Looper.
212      */
getThread()213     public Thread getThread() {
214         return mThread;
215     }
216 
217     /** @hide */
getQueue()218     public MessageQueue getQueue() {
219         return mQueue;
220     }
221 
dump(Printer pw, String prefix)222     public void dump(Printer pw, String prefix) {
223         pw = PrefixPrinter.create(pw, prefix);
224         pw.println(this.toString());
225         pw.println("mRun=" + mRun);
226         pw.println("mThread=" + mThread);
227         pw.println("mQueue=" + ((mQueue != null) ? mQueue : "(null"));
228         if (mQueue != null) {
229             synchronized (mQueue) {
230                 long now = SystemClock.uptimeMillis();
231                 Message msg = mQueue.mMessages;
232                 int n = 0;
233                 while (msg != null) {
234                     pw.println("  Message " + n + ": " + msg.toString(now));
235                     n++;
236                     msg = msg.next;
237                 }
238                 pw.println("(Total messages: " + n + ")");
239             }
240         }
241     }
242 
toString()243     public String toString() {
244         return "Looper{" + Integer.toHexString(System.identityHashCode(this)) + "}";
245     }
246 
247     /**
248      * @hide
249      */
250     public static interface Profiler {
profile(Message message, long wallStart, long wallTime, long threadStart, long threadTime)251         void profile(Message message, long wallStart, long wallTime,
252                 long threadStart, long threadTime);
253     }
254 }
255