blob: 95818938d5eabf1d43dd4cdc394794749429bcfa [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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.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 */
52public 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 * @since CURRENT
180 * {@hide pending API Council approval}
181 */
182 public Thread getThread() {
183 return mThread;
184 }
185
186 public void dump(Printer pw, String prefix) {
187 pw.println(prefix + this);
188 pw.println(prefix + "mRun=" + mRun);
189 pw.println(prefix + "mThread=" + mThread);
190 pw.println(prefix + "mQueue=" + ((mQueue != null) ? mQueue : "(null"));
191 if (mQueue != null) {
192 synchronized (mQueue) {
193 Message msg = mQueue.mMessages;
194 int n = 0;
195 while (msg != null) {
196 pw.println(prefix + " Message " + n + ": " + msg);
197 n++;
198 msg = msg.next;
199 }
200 pw.println(prefix + "(Total messages: " + n + ")");
201 }
202 }
203 }
204
205 public String toString() {
206 return "Looper{"
207 + Integer.toHexString(System.identityHashCode(this))
208 + "}";
209 }
210
211 static class HandlerException extends Exception {
212
213 HandlerException(Message message, Throwable cause) {
214 super(createMessage(cause), cause);
215 }
216
217 static String createMessage(Throwable cause) {
218 String causeMsg = cause.getMessage();
219 if (causeMsg == null) {
220 causeMsg = cause.toString();
221 }
222 return causeMsg;
223 }
224 }
225}
226