Looper.java revision df58a1bbf8d7f6608848eae4d5d48dcff137dc8c
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 17package android.os; 18 19import android.util.Config; 20import android.util.Log; 21import android.util.Printer; 22import android.util.PrefixPrinter; 23 24/** 25 * Class used to run a message loop for a thread. Threads by default do 26 * not have a message loop associated with them; to create one, call 27 * {@link #prepare} in the thread that is to run the loop, and then 28 * {@link #loop} to have it process messages until the loop is stopped. 29 * 30 * <p>Most interaction with a message loop is through the 31 * {@link Handler} class. 32 * 33 * <p>This is a typical example of the implementation of a Looper thread, 34 * using the separation of {@link #prepare} and {@link #loop} to create an 35 * initial Handler to communicate with the Looper. 36 * 37 * <pre> 38 * class LooperThread extends Thread { 39 * public Handler mHandler; 40 * 41 * public void run() { 42 * Looper.prepare(); 43 * 44 * mHandler = new Handler() { 45 * public void handleMessage(Message msg) { 46 * // process incoming messages here 47 * } 48 * }; 49 * 50 * Looper.loop(); 51 * } 52 * }</pre> 53 */ 54public class Looper { 55 private static final String TAG = "Looper"; 56 private static final boolean LOG_V = Log.isLoggable(TAG, Log.VERBOSE); 57 58 // sThreadLocal.get() will return null unless you've called prepare(). 59 private static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); 60 61 final MessageQueue mQueue; 62 final Thread mThread; 63 volatile boolean mRun; 64 65 private Printer mLogging = null; 66 private static Looper mMainLooper = null; // guarded by Looper.class 67 68 /** Initialize the current thread as a looper. 69 * This gives you a chance to create handlers that then reference 70 * this looper, before actually starting the loop. Be sure to call 71 * {@link #loop()} after calling this method, and end it by calling 72 * {@link #quit()}. 73 */ 74 public static final void prepare() { 75 if (sThreadLocal.get() != null) { 76 throw new RuntimeException("Only one Looper may be created per thread"); 77 } 78 sThreadLocal.set(new Looper()); 79 } 80 81 /** 82 * Initialize the current thread as a looper, marking it as an 83 * application's main looper. The main looper for your application 84 * is created by the Android environment, so you should never need 85 * to call this function yourself. See also: {@link #prepare()} 86 */ 87 public static final void prepareMainLooper() { 88 prepare(); 89 setMainLooper(myLooper()); 90 if (Process.supportsProcesses()) { 91 myLooper().mQueue.mQuitAllowed = false; 92 } 93 } 94 95 private synchronized static void setMainLooper(Looper looper) { 96 mMainLooper = looper; 97 } 98 99 /** Returns the application's main looper, which lives in the main thread of the application. 100 */ 101 public synchronized static final Looper getMainLooper() { 102 return mMainLooper; 103 } 104 105 /** 106 * Run the message queue in this thread. Be sure to call 107 * {@link #quit()} to end the loop. 108 */ 109 public static final void loop() { 110 Looper me = myLooper(); 111 if (me == null) { 112 throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); 113 } 114 MessageQueue queue = me.mQueue; 115 116 // Make sure the identity of this thread is that of the local process, 117 // and keep track of what that identity token actually is. 118 Binder.clearCallingIdentity(); 119 final long ident = Binder.clearCallingIdentity(); 120 121 while (true) { 122 Message msg = queue.next(); // might block 123 if (msg != null) { 124 if (msg.target == null) { 125 // No target is a magic identifier for the quit message. 126 return; 127 } 128 if (me.mLogging != null) me.mLogging.println( 129 ">>>>> Dispatching to " + msg.target + " " 130 + msg.callback + ": " + msg.what 131 ); 132 msg.target.dispatchMessage(msg); 133 if (me.mLogging != null) me.mLogging.println( 134 "<<<<< Finished to " + msg.target + " " 135 + msg.callback); 136 137 // Make sure that during the course of dispatching the 138 // identity of the thread wasn't corrupted. 139 final long newIdent = Binder.clearCallingIdentity(); 140 if (ident != newIdent) { 141 Log.wtf("Looper", "Thread identity changed from 0x" 142 + Long.toHexString(ident) + " to 0x" 143 + Long.toHexString(newIdent) + " while dispatching to " 144 + msg.target + " " + msg.callback + " what=" + msg.what); 145 } 146 147 msg.recycle(); 148 } 149 } 150 } 151 152 /** 153 * Return the Looper object associated with the current thread. Returns 154 * null if the calling thread is not associated with a Looper. 155 */ 156 public static final Looper myLooper() { 157 return sThreadLocal.get(); 158 } 159 160 /** 161 * Control logging of messages as they are processed by this Looper. If 162 * enabled, a log message will be written to <var>printer</var> 163 * at the beginning and ending of each message dispatch, identifying the 164 * target Handler and message contents. 165 * 166 * @param printer A Printer object that will receive log messages, or 167 * null to disable message logging. 168 */ 169 public void setMessageLogging(Printer printer) { 170 mLogging = printer; 171 } 172 173 /** 174 * Return the {@link MessageQueue} object associated with the current 175 * thread. This must be called from a thread running a Looper, or a 176 * NullPointerException will be thrown. 177 */ 178 public static final MessageQueue myQueue() { 179 return myLooper().mQueue; 180 } 181 182 private Looper() { 183 mQueue = new MessageQueue(); 184 mRun = true; 185 mThread = Thread.currentThread(); 186 } 187 188 public void quit() { 189 Message msg = Message.obtain(); 190 // NOTE: By enqueueing directly into the message queue, the 191 // message is left with a null target. This is how we know it is 192 // a quit message. 193 mQueue.enqueueMessage(msg, 0); 194 } 195 196 /** 197 * Return the Thread associated with this Looper. 198 */ 199 public Thread getThread() { 200 return mThread; 201 } 202 203 /** @hide */ 204 public MessageQueue getQueue() { 205 return mQueue; 206 } 207 208 public void dump(Printer pw, String prefix) { 209 pw = PrefixPrinter.create(pw, prefix); 210 pw.println(this.toString()); 211 pw.println("mRun=" + mRun); 212 pw.println("mThread=" + mThread); 213 pw.println("mQueue=" + ((mQueue != null) ? mQueue : "(null")); 214 if (mQueue != null) { 215 synchronized (mQueue) { 216 long now = SystemClock.uptimeMillis(); 217 Message msg = mQueue.mMessages; 218 int n = 0; 219 while (msg != null) { 220 pw.println(" Message " + n + ": " + msg.toString(now)); 221 n++; 222 msg = msg.next; 223 } 224 pw.println("(Total messages: " + n + ")"); 225 } 226 } 227 } 228 229 public String toString() { 230 return "Looper{" 231 + Integer.toHexString(System.identityHashCode(this)) 232 + "}"; 233 } 234 235 static class HandlerException extends Exception { 236 237 HandlerException(Message message, Throwable cause) { 238 super(createMessage(cause), cause); 239 } 240 241 static String createMessage(Throwable cause) { 242 String causeMsg = cause.getMessage(); 243 if (causeMsg == null) { 244 causeMsg = cause.toString(); 245 } 246 return causeMsg; 247 } 248 } 249} 250