summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.txt2
-rw-r--r--core/java/android/os/Handler.java15
-rw-r--r--core/java/android/os/HandlerThread.java53
-rw-r--r--core/java/android/os/Looper.java33
-rw-r--r--core/java/android/os/MessageQueue.java46
-rw-r--r--opengl/java/android/opengl/GLSurfaceView.java16
6 files changed, 136 insertions, 29 deletions
diff --git a/api/current.txt b/api/current.txt
index 803bb02..abf767a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -17116,6 +17116,7 @@ package android.os {
method public int getThreadId();
method protected void onLooperPrepared();
method public boolean quit();
+ method public boolean quitSafely();
}
public abstract interface IBinder {
@@ -17156,6 +17157,7 @@ package android.os {
method public static void prepare();
method public static void prepareMainLooper();
method public void quit();
+ method public void quitSafely();
method public void setMessageLogging(android.util.Printer);
}
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index 94de448..14d8f07 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -413,27 +413,32 @@ public class Handler {
/**
* Runs the specified task synchronously.
- *
+ * <p>
* If the current thread is the same as the handler thread, then the runnable
* runs immediately without being enqueued. Otherwise, posts the runnable
* to the handler and waits for it to complete before returning.
- *
+ * </p><p>
* This method is dangerous! Improper use can result in deadlocks.
* Never call this method while any locks are held or use it in a
* possibly re-entrant manner.
- *
+ * </p><p>
* This method is occasionally useful in situations where a background thread
* must synchronously await completion of a task that must run on the
* handler's thread. However, this problem is often a symptom of bad design.
* Consider improving the design (if possible) before resorting to this method.
- *
+ * </p><p>
* One example of where you might want to use this method is when you just
* set up a Handler thread and need to perform some initialization steps on
* it before continuing execution.
- *
+ * </p><p>
* If timeout occurs then this method returns <code>false</code> but the runnable
* will remain posted on the handler and may already be in progress or
* complete at a later time.
+ * </p><p>
+ * When using this method, be sure to use {@link Looper#quitSafely} when
+ * quitting the looper. Otherwise {@link #runWithScissors} may hang indefinitely.
+ * (TODO: We should fix this by making MessageQueue aware of blocking runnables.)
+ * </p>
*
* @param r The Runnable that will be executed synchronously.
* @param timeout The timeout in milliseconds, or 0 to wait indefinitely.
diff --git a/core/java/android/os/HandlerThread.java b/core/java/android/os/HandlerThread.java
index daf1f59..2904105 100644
--- a/core/java/android/os/HandlerThread.java
+++ b/core/java/android/os/HandlerThread.java
@@ -48,6 +48,7 @@ public class HandlerThread extends Thread {
protected void onLooperPrepared() {
}
+ @Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
@@ -83,12 +84,25 @@ public class HandlerThread extends Thread {
}
return mLooper;
}
-
+
/**
- * Ask the currently running looper to quit. If the thread has not
- * been started or has finished (that is if {@link #getLooper} returns
- * null), then false is returned. Otherwise the looper is asked to
- * quit and true is returned.
+ * Quits the handler thread's looper.
+ * <p>
+ * Causes the handler thread's looper to terminate without processing any
+ * more messages in the message queue.
+ * </p><p>
+ * Any attempt to post messages to the queue after the looper is asked to quit will fail.
+ * For example, the {@link Handler#sendMessage(Message)} method will return false.
+ * </p><p class="note">
+ * Using this method may be unsafe because some messages may not be delivered
+ * before the looper terminates. Consider using {@link #quitSafely} instead to ensure
+ * that all pending work is completed in an orderly manner.
+ * </p>
+ *
+ * @return True if the looper looper has been asked to quit or false if the
+ * thread had not yet started running.
+ *
+ * @see #quitSafely
*/
public boolean quit() {
Looper looper = getLooper();
@@ -98,7 +112,34 @@ public class HandlerThread extends Thread {
}
return false;
}
-
+
+ /**
+ * Quits the handler thread's looper safely.
+ * <p>
+ * Causes the handler thread's looper to terminate as soon as all remaining messages
+ * in the message queue that are already due to be delivered have been handled.
+ * Pending delayed messages with due times in the future will not be delivered.
+ * </p><p>
+ * Any attempt to post messages to the queue after the looper is asked to quit will fail.
+ * For example, the {@link Handler#sendMessage(Message)} method will return false.
+ * </p><p>
+ * If the thread has not been started or has finished (that is if
+ * {@link #getLooper} returns null), then false is returned.
+ * Otherwise the looper is asked to quit and true is returned.
+ * </p>
+ *
+ * @return True if the looper looper has been asked to quit or false if the
+ * thread had not yet started running.
+ */
+ public boolean quitSafely() {
+ Looper looper = getLooper();
+ if (looper != null) {
+ looper.quitSafely();
+ return true;
+ }
+ return false;
+ }
+
/**
* Returns the identifier of this thread. See Process.myTid().
*/
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index fa28765..d5cf771 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -202,18 +202,37 @@ public final class Looper {
/**
* Quits the looper.
* <p>
+ * Causes the {@link #loop} method to terminate without processing any
+ * more messages in the message queue.
+ * </p><p>
+ * Any attempt to post messages to the queue after the looper is asked to quit will fail.
+ * For example, the {@link Handler#sendMessage(Message)} method will return false.
+ * </p><p class="note">
+ * Using this method may be unsafe because some messages may not be delivered
+ * before the looper terminates. Consider using {@link #quitSafely} instead to ensure
+ * that all pending work is completed in an orderly manner.
+ * </p>
+ *
+ * @see #quitSafely
+ */
+ public void quit() {
+ mQueue.quit(false);
+ }
+
+ /**
+ * Quits the looper safely.
+ * <p>
* Causes the {@link #loop} method to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
- * However delayed messages with due times in the future may not be handled before
- * the loop terminates.
+ * However pending delayed messages with due times in the future will not be
+ * delivered before the loop terminates.
* </p><p>
- * Any attempt to post messages to the queue after {@link #quit} has been called
- * will fail. For example, the {@link Handler#sendMessage(Message)} method will
- * return false when the looper is being terminated.
+ * Any attempt to post messages to the queue after the looper is asked to quit will fail.
+ * For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p>
*/
- public void quit() {
- mQueue.quit();
+ public void quitSafely() {
+ mQueue.quit(true);
}
/**
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index c058bfc..bf7e5ca 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -219,7 +219,7 @@ public final class MessageQueue {
}
}
- void quit() {
+ void quit(boolean safe) {
if (!mQuitAllowed) {
throw new RuntimeException("Main thread not allowed to quit.");
}
@@ -229,6 +229,12 @@ public final class MessageQueue {
return;
}
mQuiting = true;
+
+ if (safe) {
+ removeAllFutureMessagesLocked();
+ } else {
+ removeAllMessagesLocked();
+ }
}
nativeWake(mPtr);
}
@@ -473,4 +479,42 @@ public final class MessageQueue {
}
}
}
+
+ private void removeAllMessagesLocked() {
+ Message p = mMessages;
+ while (p != null) {
+ Message n = p.next;
+ p.recycle();
+ p = n;
+ }
+ mMessages = null;
+ }
+
+ private void removeAllFutureMessagesLocked() {
+ final long now = SystemClock.uptimeMillis();
+ Message p = mMessages;
+ if (p != null) {
+ if (p.when > now) {
+ removeAllMessagesLocked();
+ } else {
+ Message n;
+ for (;;) {
+ n = p.next;
+ if (n == null) {
+ return;
+ }
+ if (n.when > now) {
+ break;
+ }
+ p = n;
+ }
+ p.next = null;
+ do {
+ p = n;
+ n = p.next;
+ p.recycle();
+ } while (n != null);
+ }
+ }
+ }
}
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index 235829e..dac3506 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -245,7 +245,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
if (mGLThread != null) {
// GLThread may still be running if this view was never
// attached to a window.
- mGLThread.requestExitAndWait();
+ mGLThread.quitSafely();
}
} finally {
super.finalize();
@@ -628,7 +628,11 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
Log.d(TAG, "onDetachedFromWindow");
}
if (mGLThread != null) {
- mGLThread.requestExitAndWait();
+ mGLThread.quitSafely();
+ try {
+ mGLThread.join();
+ } catch (InterruptedException ex) {
+ }
}
mDetached = true;
super.onDetachedFromWindow();
@@ -1627,14 +1631,6 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
mGLHandler.runWithScissors(mGetRenderModeRunnable, 0);
return mGetRenderModeRunnable.renderMode;
}
-
- public void requestExitAndWait() {
- getLooper().quit();
- try {
- this.join();
- } catch (InterruptedException e) {
- }
- }
} // class GLThread
static class LogWriter extends Writer {