diff options
| author | 2011-06-27 19:33:27 -0700 | |
|---|---|---|
| committer | 2011-06-28 12:55:38 -0700 | |
| commit | bf591ff682f14db7ba7b3554897e9cdcf245da59 (patch) | |
| tree | 247d82d0062b085a2aafe713d97413250ec036f2 | |
| parent | abb04eec06bce358826e4cb6b5f852424ece8005 (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.java | 5 | ||||
| -rw-r--r-- | core/java/com/android/internal/widget/multiwaveview/Tweener.java | 90 |
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); } } } |