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 final 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 private static Looper sMainLooper; // guarded by Looper.class 59 60 final MessageQueue mQueue; 61 final Thread mThread; 62 volatile boolean mRun; 63 64 private Printer mLogging; 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 */ 72 public static void prepare() { 73 prepare(true); 74 } 75 76 private static void prepare(boolean quitAllowed) { 77 if (sThreadLocal.get() != null) { 78 throw new RuntimeException("Only one Looper may be created per thread"); 79 } 80 sThreadLocal.set(new Looper(quitAllowed)); 81 } 82 83 /** 84 * Initialize the current thread as a looper, marking it as an 85 * application's main looper. The main looper for your application 86 * is created by the Android environment, so you should never need 87 * to call this function yourself. See also: {@link #prepare()} 88 */ 89 public static void prepareMainLooper() { 90 prepare(false); 91 synchronized (Looper.class) { 92 if (sMainLooper != null) { 93 throw new IllegalStateException("The main Looper has already been prepared."); 94 } 95 sMainLooper = myLooper(); 96 } 97 } 98 99 /** Returns the application's main looper, which lives in the main thread of the application. 100 */ 101 public static Looper getMainLooper() { 102 synchronized (Looper.class) { 103 return sMainLooper; 104 } 105 } 106 107 /** 108 * Run the message queue in this thread. Be sure to call 109 * {@link #quit()} to end the loop. 110 */ 111 public static void loop() { 112 final Looper me = myLooper(); 113 if (me == null) { 114 throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); 115 } 116 final MessageQueue queue = me.mQueue; 117 118 // Make sure the identity of this thread is that of the local process, 119 // and keep track of what that identity token actually is. 120 Binder.clearCallingIdentity(); 121 final long ident = Binder.clearCallingIdentity(); 122 123 for (;;) { 124 Message msg = queue.next(); // might block 125 if (msg == null) { 126 // No message indicates that the message queue is quitting. 127 return; 128 } 129 130 // This must be in a local variable, in case a UI event sets the logger 131 Printer logging = me.mLogging; 132 if (logging != null) { 133 logging.println(">>>>> Dispatching to " + msg.target + " " + 134 msg.callback + ": " + msg.what); 135 } 136 137 msg.target.dispatchMessage(msg); 138 139 if (logging != null) { 140 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); 141 } 142 143 // Make sure that during the course of dispatching the 144 // identity of the thread wasn't corrupted. 145 final long newIdent = Binder.clearCallingIdentity(); 146 if (ident != newIdent) { 147 Log.wtf(TAG, "Thread identity changed from 0x" 148 + Long.toHexString(ident) + " to 0x" 149 + Long.toHexString(newIdent) + " while dispatching to " 150 + msg.target.getClass().getName() + " " 151 + msg.callback + " what=" + msg.what); 152 } 153 154 msg.recycle(); 155 } 156 } 157 158 /** 159 * Return the Looper object associated with the current thread. Returns 160 * null if the calling thread is not associated with a Looper. 161 */ 162 public static Looper myLooper() { 163 return sThreadLocal.get(); 164 } 165 166 /** 167 * Control logging of messages as they are processed by this Looper. If 168 * enabled, a log message will be written to <var>printer</var> 169 * at the beginning and ending of each message dispatch, identifying the 170 * target Handler and message contents. 171 * 172 * @param printer A Printer object that will receive log messages, or 173 * null to disable message logging. 174 */ 175 public void setMessageLogging(Printer printer) { 176 mLogging = printer; 177 } 178 179 /** 180 * Return the {@link MessageQueue} object associated with the current 181 * thread. This must be called from a thread running a Looper, or a 182 * NullPointerException will be thrown. 183 */ 184 public static MessageQueue myQueue() { 185 return myLooper().mQueue; 186 } 187 188 private Looper(boolean quitAllowed) { 189 mQueue = new MessageQueue(quitAllowed); 190 mRun = true; 191 mThread = Thread.currentThread(); 192 } 193 194 /** 195 * Returns true if the current thread is this looper's thread. 196 * @hide 197 */ 198 public boolean isCurrentThread() { 199 return Thread.currentThread() == mThread; 200 } 201 202 /** 203 * Quits the looper. 204 * <p> 205 * Causes the {@link #loop} method to terminate without processing any 206 * more messages in the message queue. 207 * </p><p> 208 * Any attempt to post messages to the queue after the looper is asked to quit will fail. 209 * For example, the {@link Handler#sendMessage(Message)} method will return false. 210 * </p><p class="note"> 211 * Using this method may be unsafe because some messages may not be delivered 212 * before the looper terminates. Consider using {@link #quitSafely} instead to ensure 213 * that all pending work is completed in an orderly manner. 214 * </p> 215 * 216 * @see #quitSafely 217 */ 218 public void quit() { 219 mQueue.quit(false); 220 } 221 222 /** 223 * Quits the looper safely. 224 * <p> 225 * Causes the {@link #loop} method to terminate as soon as all remaining messages 226 * in the message queue that are already due to be delivered have been handled. 227 * However pending delayed messages with due times in the future will not be 228 * delivered before the loop terminates. 229 * </p><p> 230 * Any attempt to post messages to the queue after the looper is asked to quit will fail. 231 * For example, the {@link Handler#sendMessage(Message)} method will return false. 232 * </p> 233 */ 234 public void quitSafely() { 235 mQueue.quit(true); 236 } 237 238 /** 239 * Posts a synchronization barrier to the Looper's message queue. 240 * 241 * Message processing occurs as usual until the message queue encounters the 242 * synchronization barrier that has been posted. When the barrier is encountered, 243 * later synchronous messages in the queue are stalled (prevented from being executed) 244 * until the barrier is released by calling {@link #removeSyncBarrier} and specifying 245 * the token that identifies the synchronization barrier. 246 * 247 * This method is used to immediately postpone execution of all subsequently posted 248 * synchronous messages until a condition is met that releases the barrier. 249 * Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier 250 * and continue to be processed as usual. 251 * 252 * This call must be always matched by a call to {@link #removeSyncBarrier} with 253 * the same token to ensure that the message queue resumes normal operation. 254 * Otherwise the application will probably hang! 255 * 256 * @return A token that uniquely identifies the barrier. This token must be 257 * passed to {@link #removeSyncBarrier} to release the barrier. 258 * 259 * @hide 260 */ 261 public int postSyncBarrier() { 262 return mQueue.enqueueSyncBarrier(SystemClock.uptimeMillis()); 263 } 264 265 266 /** 267 * Removes a synchronization barrier. 268 * 269 * @param token The synchronization barrier token that was returned by 270 * {@link #postSyncBarrier}. 271 * 272 * @throws IllegalStateException if the barrier was not found. 273 * 274 * @hide 275 */ 276 public void removeSyncBarrier(int token) { 277 mQueue.removeSyncBarrier(token); 278 } 279 280 /** 281 * Return the Thread associated with this Looper. 282 */ 283 public Thread getThread() { 284 return mThread; 285 } 286 287 /** @hide */ 288 public MessageQueue getQueue() { 289 return mQueue; 290 } 291 292 /** 293 * Return whether this looper's thread is currently idle, waiting for new work 294 * to do. This is intrinsically racy, since its state can change before you get 295 * the result back. 296 * @hide 297 */ 298 public boolean isIdling() { 299 return mQueue.isIdling(); 300 } 301 302 public void dump(Printer pw, String prefix) { 303 pw = PrefixPrinter.create(pw, prefix); 304 pw.println(this.toString()); 305 pw.println("mRun=" + mRun); 306 pw.println("mThread=" + mThread); 307 pw.println("mQueue=" + ((mQueue != null) ? mQueue : "(null")); 308 if (mQueue != null) { 309 synchronized (mQueue) { 310 long now = SystemClock.uptimeMillis(); 311 Message msg = mQueue.mMessages; 312 int n = 0; 313 while (msg != null) { 314 pw.println(" Message " + n + ": " + msg.toString(now)); 315 n++; 316 msg = msg.next; 317 } 318 pw.println("(Total messages: " + n + ")"); 319 } 320 } 321 } 322 323 public String toString() { 324 return "Looper{" + Integer.toHexString(System.identityHashCode(this)) + "}"; 325 } 326 } 327