From 1aaf351f6bbaa0b1dc6c08bd5a590e1930e2ef07 Mon Sep 17 00:00:00 2001 From: Owen Lin Date: Thu, 1 Apr 2010 15:42:33 +0800 Subject: Fix the ANR after switching between Camera and Camcorder. The reason of this bug is we try to queue an event in the GLThread, but the thread has already dead when the GLRootView detacched from the window. So, we are waiting a task that will never be executed. I have seen a similar ANR in GLRootView.onTouch(), so I also fix the that issue in this change. Bug: 2559472 Change-Id: I49efd9ca01f1f6cce73320c31448ebaa7687469f --- src/com/android/camera/ui/GLRootView.java | 39 ++++++++++++++++++++++---- src/com/android/camera/ui/HeadUpDisplay.java | 42 +++++++++++----------------- src/com/android/camera/ui/IndicatorBar.java | 8 ++++-- 3 files changed, 56 insertions(+), 33 deletions(-) (limited to 'src/com/android/camera/ui') diff --git a/src/com/android/camera/ui/GLRootView.java b/src/com/android/camera/ui/GLRootView.java index e3c066b..33aa736 100644 --- a/src/com/android/camera/ui/GLRootView.java +++ b/src/com/android/camera/ui/GLRootView.java @@ -1,13 +1,14 @@ package com.android.camera.ui; +import com.android.camera.Util; + import android.app.Activity; import android.content.Context; import android.graphics.Matrix; import android.graphics.PixelFormat; import android.opengl.GLSurfaceView; import android.opengl.GLU; -import android.os.Handler; -import android.os.HandlerThread; +import android.os.ConditionVariable; import android.os.Looper; import android.os.Process; import android.os.SystemClock; @@ -18,8 +19,6 @@ import android.view.MotionEvent; import android.view.animation.Animation; import android.view.animation.Transformation; -import com.android.camera.Util; - import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; @@ -75,11 +74,13 @@ public class GLRootView extends GLSurfaceView private Thread mGLThread; + private boolean mIsQueueActive = true; + // TODO: move this part (handler) into GLSurfaceView private final Looper mLooper; public GLRootView(Context context) { - this(context, null); + this(context, null); } public GLRootView(Context context, AttributeSet attrs) { @@ -437,9 +438,11 @@ public class GLRootView extends GLSurfaceView @Override public boolean dispatchTouchEvent(MotionEvent event) { + // If this has been detached from root, we don't need to handle event + if (mIsQueueActive) return false; FutureTask task = new FutureTask( new TouchEventHandler(event)); - queueEvent(task); + queueEventOrThrowException(task); try { return task.get(); } catch (Exception e) { @@ -521,6 +524,30 @@ public class GLRootView extends GLSurfaceView (float) width / newWidth, (float) height / newHeight); } + public synchronized void queueEventOrThrowException(Runnable runnable) { + if (!mIsQueueActive) { + throw new IllegalStateException("GLThread has exit"); + } + super.queueEvent(runnable); + } + + @Override + protected void onDetachedFromWindow() { + final ConditionVariable var = new ConditionVariable(); + synchronized (this) { + mIsQueueActive = false; + queueEvent(new Runnable() { + public void run() { + var.open(); + } + }); + } + + // Make sure all the runnables in the event queue is executed. + var.block(); + super.onDetachedFromWindow(); + } + protected Looper getTimerLooper() { return mLooper; } diff --git a/src/com/android/camera/ui/HeadUpDisplay.java b/src/com/android/camera/ui/HeadUpDisplay.java index 0e43ebe..ac2928f 100644 --- a/src/com/android/camera/ui/HeadUpDisplay.java +++ b/src/com/android/camera/ui/HeadUpDisplay.java @@ -1,6 +1,11 @@ package com.android.camera.ui; import static com.android.camera.ui.GLRootView.dpToPixel; + +import java.util.ArrayList; +import java.util.concurrent.Callable; +import java.util.concurrent.FutureTask; + import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; @@ -11,7 +16,6 @@ import android.os.Handler; import android.os.Message; import android.preference.PreferenceManager; import android.util.DisplayMetrics; -import android.util.Log; import android.view.MotionEvent; import android.view.View.MeasureSpec; import android.view.animation.AlphaAnimation; @@ -23,10 +27,6 @@ import com.android.camera.ListPreference; import com.android.camera.PreferenceGroup; import com.android.camera.R; -import java.util.ArrayList; -import java.util.concurrent.Callable; -import java.util.concurrent.FutureTask; - // This is the UI for the on-screen settings. It mainly run in the GLThread. It // will modify the shared-preferences. The concurrency rule is: The shared- // preference will be updated in the GLThread. And an event will be trigger in @@ -88,22 +88,16 @@ public class HeadUpDisplay extends GLView { @Override public void handleMessage(Message msg) { GLRootView root = getGLRootView(); - FutureTask task = null; + Runnable runnable = null; switch(msg.what) { case DESELECT_INDICATOR: - task = new FutureTask(mDeselectIndicator); + runnable = mDeselectIndicator; break; case DEACTIVATE_INDICATOR_BAR: - task = new FutureTask(mDeactivateIndicatorBar); + runnable = mDeactivateIndicatorBar; break; } - if (task == null) return; - try { - root.queueEvent(task); - task.get(); - } catch (Exception e) { - Log.e(TAG, "error in concurrent code", e); - } + if (runnable != null) root.queueEvent(runnable); } }; } @@ -116,17 +110,15 @@ public class HeadUpDisplay extends GLView { sPopupTriangleOffset = dpToPixel(context, POPUP_TRIANGLE_OFFSET); } - private final Callable mDeselectIndicator = new Callable () { - public Void call() throws Exception { + private final Runnable mDeselectIndicator = new Runnable () { + public void run() { mIndicatorBar.setSelectedIndex(IndicatorBar.INDEX_NONE); - return null; - } + } }; - private final Callable mDeactivateIndicatorBar = new Callable () { - public Void call() throws Exception { + private final Runnable mDeactivateIndicatorBar = new Runnable () { + public void run() { if (mIndicatorBar != null) mIndicatorBar.setActivated(false); - return null; } }; @@ -367,9 +359,9 @@ public class HeadUpDisplay extends GLView { private final Callable mCollapse = new Callable() { public Boolean call() { - if (mIndicatorBar.getSelectedIndex() == IndicatorBar.INDEX_NONE) { - return false; - } + if (!mIndicatorBar.isActivated()) return false; + mHandler.removeMessages(DESELECT_INDICATOR); + mHandler.removeMessages(DEACTIVATE_INDICATOR_BAR); mIndicatorBar.setSelectedIndex(IndicatorBar.INDEX_NONE); mIndicatorBar.setActivated(false); return true; diff --git a/src/com/android/camera/ui/IndicatorBar.java b/src/com/android/camera/ui/IndicatorBar.java index 28c4eab..e5ee82a 100644 --- a/src/com/android/camera/ui/IndicatorBar.java +++ b/src/com/android/camera/ui/IndicatorBar.java @@ -1,12 +1,12 @@ package com.android.camera.ui; +import javax.microedition.khronos.opengles.GL11; + import android.graphics.Rect; import android.view.MotionEvent; import android.view.View.MeasureSpec; import android.view.animation.AlphaAnimation; -import javax.microedition.khronos.opengles.GL11; - public class IndicatorBar extends GLView { public static final int INDEX_NONE = -1; @@ -148,6 +148,10 @@ public class IndicatorBar extends GLView { } } + public boolean isActivated() { + return mActivated; + } + @Override protected boolean dispatchTouchEvent(MotionEvent event) { // Do not pass motion events to children -- cgit v1.1