summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jim Miller <jaggies@google.com> 2011-06-27 19:33:27 -0700
committer Jim Miller <jaggies@google.com> 2011-06-28 12:55:38 -0700
commitbf591ff682f14db7ba7b3554897e9cdcf245da59 (patch)
tree247d82d0062b085a2aafe713d97413250ec036f2
parentabb04eec06bce358826e4cb6b5f852424ece8005 (diff)
Fix 4691563: Fix memory leak caused by Tweeners hanging onto references.
This fixes a bug where the animations in MultiWaveView were keeping references to bitmaps and preventing them from being reclaimed during GC. The solution is two-fold: 1. When any given animation completes, it is now removed from the list of running animators objects. 2. When the client explicitly calls reset(), we release all references to animators and objects. Change-Id: Ice434ed1720fe4c253b9607ef61699d41f87f777
-rw-r--r--core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java5
-rw-r--r--core/java/com/android/internal/widget/multiwaveview/Tweener.java90
2 files changed, 69 insertions, 26 deletions
diff --git a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
index 3e7b976aedb7..5b3510428f7b 100644
--- a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
+++ b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
@@ -224,8 +224,8 @@ public class MultiWaveView extends View implements AnimatorUpdateListener {
/**
* Animation used to attract user's attention to the target button.
- * Assumes mChevronDrawables is an a list with an even number of chevrons filled with left
- * followed by right chevrons.
+ * Assumes mChevronDrawables is an a list with an even number of chevrons filled with
+ * mFeedbackCount items in the order: left, right, top, bottom.
*/
private void startChevronAnimation() {
final float r = mHandleDrawable.getWidth() / 2;
@@ -442,6 +442,7 @@ public class MultiWaveView extends View implements AnimatorUpdateListener {
mHandleDrawable.setX(mWaveCenterX);
mHandleDrawable.setY(mWaveCenterY);
mHandleDrawable.setState(TargetDrawable.STATE_INACTIVE);
+ Tweener.reset();
}
@Override
diff --git a/core/java/com/android/internal/widget/multiwaveview/Tweener.java b/core/java/com/android/internal/widget/multiwaveview/Tweener.java
index 0cff00a514ab..bc8a62f2ca0b 100644
--- a/core/java/com/android/internal/widget/multiwaveview/Tweener.java
+++ b/core/java/com/android/internal/widget/multiwaveview/Tweener.java
@@ -18,25 +18,42 @@ package com.android.internal.widget.multiwaveview;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map.Entry;
import android.animation.Animator.AnimatorListener;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.util.Log;
class Tweener {
private static final String TAG = "Tweener";
+ private static final boolean DEBUG = false;
- private Object object;
ObjectAnimator animator;
private static HashMap<Object, Tweener> sTweens = new HashMap<Object, Tweener>();
- public Tweener(Object obj, ObjectAnimator anim) {
- object = obj;
+ public Tweener(ObjectAnimator anim) {
animator = anim;
}
+ private static void remove(Animator animator) {
+ Iterator<Entry<Object, Tweener>> iter = sTweens.entrySet().iterator();
+ while (iter.hasNext()) {
+ Entry<Object, Tweener> entry = iter.next();
+ if (entry.getValue().animator == animator) {
+ if (DEBUG) Log.v(TAG, "Removing tweener " + sTweens.get(entry.getKey())
+ + " sTweens.size() = " + sTweens.size());
+ iter.remove();
+ break; // an animator can only be attached to one object
+ }
+ }
+ }
+
public static Tweener to(Object object, long duration, Object... vars) {
long delay = 0;
AnimatorUpdateListener updateListener = null;
@@ -77,32 +94,35 @@ class Tweener {
// Re-use existing tween, if present
Tweener tween = sTweens.get(object);
+ ObjectAnimator anim = null;
if (tween == null) {
- ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(object,
+ anim = ObjectAnimator.ofPropertyValuesHolder(object,
props.toArray(new PropertyValuesHolder[props.size()]));
- tween = new Tweener(object, anim);
+ tween = new Tweener(anim);
sTweens.put(object, tween);
+ if (DEBUG) Log.v(TAG, "Added new Tweener " + tween);
} else {
- tween.animator.cancel();
- replace(props, object);
+ anim = sTweens.get(object).animator;
+ replace(props, object); // Cancel all animators for given object
}
if (interpolator != null) {
- tween.animator.setInterpolator(interpolator);
+ anim.setInterpolator(interpolator);
}
// Update animation with properties discovered in loop above
- tween.animator.setStartDelay(delay);
- tween.animator.setDuration(duration);
+ anim.setStartDelay(delay);
+ anim.setDuration(duration);
if (updateListener != null) {
- tween.animator.removeAllUpdateListeners(); // There should be only one
- tween.animator.addUpdateListener(updateListener);
+ anim.removeAllUpdateListeners(); // There should be only one
+ anim.addUpdateListener(updateListener);
}
if (listener != null) {
- tween.animator.removeAllListeners(); // There should be only one.
- tween.animator.addListener(listener);
+ anim.removeAllListeners(); // There should be only one.
+ anim.addListener(listener);
}
- tween.animator.start();
+ anim.addListener(mCleanupListener);
+ anim.start();
return tween;
}
@@ -114,18 +134,40 @@ class Tweener {
return Tweener.to(object, duration, vars);
}
- static void replace(ArrayList<PropertyValuesHolder> props, Object... args) {
+ // Listener to watch for completed animations and remove them.
+ private static AnimatorListener mCleanupListener = new AnimatorListenerAdapter() {
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ remove(animation);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ remove(animation);
+ }
+ };
+
+ public static void reset() {
+ if (DEBUG) {
+ Log.v(TAG, "Reset()");
+ if (sTweens.size() > 0) {
+ Log.v(TAG, "Cleaning up " + sTweens.size() + " animations");
+ }
+ }
+ sTweens.clear();
+ }
+
+ private static void replace(ArrayList<PropertyValuesHolder> props, Object... args) {
for (final Object killobject : args) {
Tweener tween = sTweens.get(killobject);
if (tween != null) {
- if (killobject == tween.object) {
- tween.animator.cancel();
- if (props != null) {
- tween.animator.setValues(
- props.toArray(new PropertyValuesHolder[props.size()]));
- } else {
- sTweens.remove(tween);
- }
+ tween.animator.cancel();
+ if (props != null) {
+ tween.animator.setValues(
+ props.toArray(new PropertyValuesHolder[props.size()]));
+ } else {
+ sTweens.remove(tween);
}
}
}