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