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