From ec84c3a189e4aa70aa6ea8ba712e5a4f260a153b Mon Sep 17 00:00:00 2001 From: Patrick Dubroy Date: Thu, 13 Jan 2011 17:55:37 -0800 Subject: Allow old view hierarchy to be GC'ed more quickly during rotation. --- api/current.xml | 11 +++ core/java/android/animation/ObjectAnimator.java | 90 +++++++++++++++-------- core/java/android/appwidget/AppWidgetHost.java | 11 +++ core/java/android/view/View.java | 3 + core/java/android/widget/AdapterViewAnimator.java | 6 ++ core/java/android/widget/ProgressBar.java | 4 +- core/java/android/widget/RemoteViewsAdapter.java | 9 +-- 7 files changed, 96 insertions(+), 38 deletions(-) diff --git a/api/current.xml b/api/current.xml index 22f5c7d..c7251b7 100644 --- a/api/current.xml +++ b/api/current.xml @@ -38983,6 +38983,17 @@ visibility="public" > + + mTargetRef; private String mPropertyName; @@ -102,6 +104,9 @@ public final class ObjectAnimator extends ValueAnimator { * @return Method the method associated with mPropertyName. */ private Method getPropertyFunction(String prefix, Class valueType) { + final Object target = mTargetRef == null ? null : mTargetRef.get(); + if (target == null) return null; + // TODO: faster implementation... Method returnVal = null; String firstLetter = mPropertyName.substring(0, 1); @@ -114,7 +119,7 @@ public final class ObjectAnimator extends ValueAnimator { args[0] = valueType; } try { - returnVal = mTarget.getClass().getMethod(setterName, args); + returnVal = target.getClass().getMethod(setterName, args); } catch (NoSuchMethodException e) { Log.e("ObjectAnimator", "Couldn't find setter/getter for property " + mPropertyName + ": " + e); @@ -134,13 +139,14 @@ public final class ObjectAnimator extends ValueAnimator { * A constructor that takes a single property name and set of values. This constructor is * used in the simple case of animating a single property. * - * @param target The object whose property is to be animated. This object should - * have a public method on it called setName(), where name is - * the value of the propertyName parameter. + * @param target The object whose property is to be animated. It will be weakly referenced + * from the newly-created ObjectAnimator. This object should have a public method on it called + * setName(), where name is the value of the propertyName + * parameter. * @param propertyName The name of the property being animated. */ private ObjectAnimator(Object target, String propertyName) { - mTarget = target; + mTargetRef = new WeakReference(target); setPropertyName(propertyName); } @@ -152,9 +158,10 @@ public final class ObjectAnimator extends ValueAnimator { * from the target object and property being animated). Therefore, there should typically * be two or more values. * - * @param target The object whose property is to be animated. This object should - * have a public method on it called setName(), where name is - * the value of the propertyName parameter. + * @param target The object whose property is to be animated. It will be weakly referenced + * from the newly-created ObjectAnimator. This object should have a public method on it called + * setName(), where name is the value of the propertyName + * parameter. * @param propertyName The name of the property being animated. * @param values A set of values that the animation will animate between over time. * @return A ValueAnimator object that is set up to animate between the given values. @@ -173,9 +180,10 @@ public final class ObjectAnimator extends ValueAnimator { * from the target object and property being animated). Therefore, there should typically * be two or more values. * - * @param target The object whose property is to be animated. This object should - * have a public method on it called setName(), where name is - * the value of the propertyName parameter. + * @param target The object whose property is to be animated. It will be weakly referenced + * from the newly-created ObjectAnimator. This object should have a public method on it called + * setName(), where name is the value of the propertyName + * parameter. * @param propertyName The name of the property being animated. * @param values A set of values that the animation will animate between over time. * @return A ValueAnimator object that is set up to animate between the given values. @@ -192,10 +200,10 @@ public final class ObjectAnimator extends ValueAnimator { * PropertyValuesHolder allows you to associate a set of animation values with a property * name. * - * @param target The object whose property is to be animated. This object should - * have public methods on it called setName(), where name is - * the name of the property passed in as the propertyName parameter for - * each of the PropertyValuesHolder objects. + * @param target The object whose property is to be animated. It will be weakly referenced + * from the newly-created ObjectAnimator. This object should have public methods on it called + * setName(), where name is the name of the property passed in as the + * propertyName parameter for each of the PropertyValuesHolder objects. * @param propertyName The name of the property being animated. * @param evaluator A TypeEvaluator that will be called on each animation frame to * provide the ncessry interpolation between the Object values to derive the animated @@ -218,10 +226,10 @@ public final class ObjectAnimator extends ValueAnimator { * PropertyValuesHolder allows you to associate a set of animation values with a property * name. * - * @param target The object whose property is to be animated. This object should - * have public methods on it called setName(), where name is - * the name of the property passed in as the propertyName parameter for - * each of the PropertyValuesHolder objects. + * @param target The object whose property is to be animated. It will be weakly referenced + * from the newly-created ObjectAnimator. This object should have public methods on it called + * setName(), where name is the name of the property passed in as the + * propertyName parameter for each of the PropertyValuesHolder objects. * @param values A set of PropertyValuesHolder objects whose values will be animated * between over time. * @return A ValueAnimator object that is set up to animate between the given values. @@ -229,7 +237,7 @@ public final class ObjectAnimator extends ValueAnimator { public static ObjectAnimator ofPropertyValuesHolder(Object target, PropertyValuesHolder... values) { ObjectAnimator anim = new ObjectAnimator(); - anim.mTarget = target; + anim.mTargetRef = new WeakReference(target); anim.setValues(values); return anim; } @@ -270,7 +278,8 @@ public final class ObjectAnimator extends ValueAnimator { @Override public void start() { if (DBG) { - Log.d("ObjectAnimator", "Anim target, duration" + mTarget + ", " + getDuration()); + final Object target = mTargetRef == null ? null : mTargetRef.get(); + Log.d("ObjectAnimator", "Anim target, duration" + target + ", " + getDuration()); for (int i = 0; i < mValues.length; ++i) { PropertyValuesHolder pvh = mValues[i]; ArrayList keyframes = pvh.mKeyframeSet.mKeyframes; @@ -297,11 +306,14 @@ public final class ObjectAnimator extends ValueAnimator { @Override void initAnimation() { if (!mInitialized) { + final Object target = mTargetRef == null ? null : mTargetRef.get(); + if (target == null) return; + // mValueType may change due to setter/getter setup; do this before calling super.init(), // which uses mValueType to set up the default type evaluator. int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { - mValues[i].setupSetterAndGetter(mTarget); + mValues[i].setupSetterAndGetter(target); } super.initAnimation(); } @@ -326,22 +338,26 @@ public final class ObjectAnimator extends ValueAnimator { /** * The target object whose property will be animated by this animation * - * @return The object being animated + * @return The object being animated, or null if the object has been garbage collected. */ public Object getTarget() { - return mTarget; + return mTargetRef == null ? null : mTargetRef.get(); } /** - * Sets the target object whose property will be animated by this animation + * Sets the target object whose property will be animated by this animation. The target + * will be weakly referenced from this object. * * @param target The object being animated */ @Override public void setTarget(Object target) { - if (mTarget != target) { - mTarget = target; - if (mTarget != null && target != null && mTarget.getClass() == target.getClass()) { + final Object currentTarget = mTargetRef == null ? null : mTargetRef.get(); + + if (currentTarget != target) { + mTargetRef = new WeakReference(target); + if (currentTarget != null && target != null + && currentTarget.getClass() == target.getClass()) { return; } // New target type should cause re-initialization prior to starting @@ -351,19 +367,25 @@ public final class ObjectAnimator extends ValueAnimator { @Override public void setupStartValues() { + final Object target = mTargetRef == null ? null : mTargetRef.get(); + if (target == null) return; + initAnimation(); int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { - mValues[i].setupStartValue(mTarget); + mValues[i].setupStartValue(target); } } @Override public void setupEndValues() { + final Object target = mTargetRef == null ? null : mTargetRef.get(); + if (target == null) return; + initAnimation(); int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { - mValues[i].setupEndValue(mTarget); + mValues[i].setupEndValue(target); } } @@ -382,9 +404,13 @@ public final class ObjectAnimator extends ValueAnimator { @Override void animateValue(float fraction) { super.animateValue(fraction); + + final Object target = mTargetRef == null ? null : mTargetRef.get(); + if (target == null) return; + int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { - mValues[i].setAnimatedValue(mTarget); + mValues[i].setAnimatedValue(target); } } diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java index 7730942..9835484 100644 --- a/core/java/android/appwidget/AppWidgetHost.java +++ b/core/java/android/appwidget/AppWidgetHost.java @@ -214,6 +214,10 @@ public class AppWidgetHost { } } + /** + * Create the AppWidgetHostView for the given widget. + * The AppWidgetHost retains a pointer to the newly-created View. + */ public final AppWidgetHostView createView(Context context, int appWidgetId, AppWidgetProviderInfo appWidget) { AppWidgetHostView view = onCreateView(context, appWidgetId, appWidget); @@ -272,6 +276,13 @@ public class AppWidgetHost { v.viewDataChanged(viewId); } } + + /** + * Clear the list of Views that have been created by this AppWidgetHost. + */ + protected void clearViews() { + mViews.clear(); + } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index c4bd1de..63a3ff0 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -7580,6 +7580,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mHardwareLayer.destroy(); mHardwareLayer = null; } + mAttachInfo.mHandler.removeMessages(AttachInfo.INVALIDATE_MSG, this); + mAttachInfo.mHandler.removeMessages(AttachInfo.INVALIDATE_RECT_MSG, this); + mCurrentAnimation = null; } /** diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java index ec8e93c..1b39371 100644 --- a/core/java/android/widget/AdapterViewAnimator.java +++ b/core/java/android/widget/AdapterViewAnimator.java @@ -986,4 +986,10 @@ public abstract class AdapterViewAnimator extends AdapterView public void willBeAdvancedByHost() { } + + @Override + protected void onDetachedFromWindow() { + mAdapter = null; + super.onDetachedFromWindow(); + } } diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 5b143fe..ef4e4e0 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -1004,9 +1004,11 @@ public class ProgressBar extends View { @Override protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); if (mIndeterminate) { stopAnimation(); } + // This should come after stopAnimation(), otherwise an invalidate message remains in the + // queue, which can prevent the entire view hierarchy from being GC'ed during a rotation + super.onDetachedFromWindow(); } } diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java index a7bff62..df1f4bf 100644 --- a/core/java/android/widget/RemoteViewsAdapter.java +++ b/core/java/android/widget/RemoteViewsAdapter.java @@ -152,6 +152,7 @@ public class RemoteViewsAdapter extends BaseAdapter { if (callback != null) { callback.onRemoteAdapterDisconnected(); } + adapter.mCache.reset(); } public IRemoteViewsFactory getRemoteViewsFactory() { @@ -657,11 +658,9 @@ public class RemoteViewsAdapter extends BaseAdapter { try { remoteViews = factory.getViewAt(position); itemId = factory.getItemId(position); - } catch (Exception e) { - // Print the error - Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + - e.getMessage()); - e.printStackTrace(); + } catch (Throwable t) { + Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + t.getMessage()); + t.printStackTrace(); // Return early to prevent additional work in re-centering the view cache, and // swapping from the loading view -- cgit v1.1