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