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