diff options
-rw-r--r-- | api/current.txt | 2 | ||||
-rw-r--r-- | core/java/android/os/Handler.java | 15 | ||||
-rw-r--r-- | core/java/android/os/HandlerThread.java | 53 | ||||
-rw-r--r-- | core/java/android/os/Looper.java | 33 | ||||
-rw-r--r-- | core/java/android/os/MessageQueue.java | 46 | ||||
-rw-r--r-- | opengl/java/android/opengl/GLSurfaceView.java | 16 |
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 { |