From 4dbc4a7ed3fa503e3d5bfe0cbdf344b0e2f0a0c5 Mon Sep 17 00:00:00 2001 From: Craig Stout Date: Wed, 3 Sep 2014 10:14:02 -0700 Subject: Fix memory leak when fast scrolling rows. If a presenter starts a view property animation, then the parent row view will have transient state which may cause a row to fail to be recycled during a fast scroll, because RecyclerView checks transient state and refuses to recycle a view if it or its children has running view property animation. This can cause a memory leak because ObjectAdapters have references to ItemBridgeAdapters via the registered observer mechanism. Apps should clear any view property animations in Presenter onViewDetachedFromWindow, but in case they don't we'll do it for them in the base class. b/17013302 Change-Id: Ibdf5998e81dd130128f88f85d88243ec27a70dd5 --- .../support/v17/leanback/widget/ImageCardView.java | 7 +++++++ .../v17/leanback/widget/ListRowPresenter.java | 5 +++-- .../support/v17/leanback/widget/Presenter.java | 20 ++++++++++++++++++++ .../support/v17/leanback/widget/RowPresenter.java | 1 + 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ImageCardView.java b/v17/leanback/src/android/support/v17/leanback/widget/ImageCardView.java index fb131e0aa1..aa9e3f846e 100644 --- a/v17/leanback/src/android/support/v17/leanback/widget/ImageCardView.java +++ b/v17/leanback/src/android/support/v17/leanback/widget/ImageCardView.java @@ -234,4 +234,11 @@ public class ImageCardView extends BaseCardView { mTitleView.setMaxLines(1); } } + + @Override + protected void onDetachedFromWindow() { + mImageView.animate().cancel(); + mImageView.setAlpha(1f); + super.onDetachedFromWindow(); + } } diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java index c13f48a365..8940352390 100644 --- a/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java +++ b/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java @@ -412,14 +412,15 @@ public class ListRowPresenter extends RowPresenter { super.onBindRowViewHolder(holder, item); ViewHolder vh = (ViewHolder) holder; ListRow rowItem = (ListRow) item; - vh.mItemBridgeAdapter.clear(); vh.mItemBridgeAdapter.setAdapter(rowItem.getAdapter()); vh.mGridView.setAdapter(vh.mItemBridgeAdapter); } @Override protected void onUnbindRowViewHolder(RowPresenter.ViewHolder holder) { - ((ViewHolder) holder).mGridView.setAdapter(null); + ViewHolder vh = (ViewHolder) holder; + vh.mGridView.setAdapter(null); + vh.mItemBridgeAdapter.clear(); super.onUnbindRowViewHolder(holder); } diff --git a/v17/leanback/src/android/support/v17/leanback/widget/Presenter.java b/v17/leanback/src/android/support/v17/leanback/widget/Presenter.java index ed7714d8b7..9a2b0bf7bc 100644 --- a/v17/leanback/src/android/support/v17/leanback/widget/Presenter.java +++ b/v17/leanback/src/android/support/v17/leanback/widget/Presenter.java @@ -103,9 +103,29 @@ public abstract class Presenter { * the consumer of an presenter's views may choose to cache views offscreen while they * are not visible, attaching an detaching them as appropriate.

* + * Any view property animations should be cancelled here or the view may fail + * to be recycled. + * * @param holder Holder of the view being detached */ public void onViewDetachedFromWindow(ViewHolder holder) { + // If there are view property animations running then RecyclerView won't recycle. + cancelAnimationsRecursive(holder.view); + } + + /** + * Utility method for removing all running animations on a view. + */ + protected static void cancelAnimationsRecursive(View view) { + if (view.hasTransientState()) { + view.animate().cancel(); + if (view instanceof ViewGroup) { + final int count = ((ViewGroup) view).getChildCount(); + for (int i = 0; view.hasTransientState() && i < count; i++) { + cancelAnimationsRecursive(((ViewGroup) view).getChildAt(i)); + } + } + } } /** diff --git a/v17/leanback/src/android/support/v17/leanback/widget/RowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/RowPresenter.java index 27f1975b4e..1c7ed3ddf6 100644 --- a/v17/leanback/src/android/support/v17/leanback/widget/RowPresenter.java +++ b/v17/leanback/src/android/support/v17/leanback/widget/RowPresenter.java @@ -415,6 +415,7 @@ public abstract class RowPresenter extends Presenter { if (vh.mHeaderViewHolder != null) { mHeaderPresenter.onViewDetachedFromWindow(vh.mHeaderViewHolder); } + cancelAnimationsRecursive(vh.view); } /** -- cgit v1.2.3 From b43872b3b32cf208089c8b88e67e433653084ba5 Mon Sep 17 00:00:00 2001 From: Dake Gu Date: Tue, 9 Sep 2014 14:27:27 -0700 Subject: fix grey panel on top of settings Bug introduced because changed layout of headers fragment. b/17436246 Change-Id: Ie46673099894ceb3b64513b2d0ba315fdccbb2a4 --- v17/leanback/src/android/support/v17/leanback/app/HeadersFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v17/leanback/src/android/support/v17/leanback/app/HeadersFragment.java b/v17/leanback/src/android/support/v17/leanback/app/HeadersFragment.java index 288640c1b8..6225a59c0a 100644 --- a/v17/leanback/src/android/support/v17/leanback/app/HeadersFragment.java +++ b/v17/leanback/src/android/support/v17/leanback/app/HeadersFragment.java @@ -139,7 +139,7 @@ public class HeadersFragment extends BaseRowFragment { private void updateListViewVisibility() { final VerticalGridView listView = getVerticalGridView(); if (listView != null) { - listView.setVisibility(mHeadersGone ? View.GONE : View.VISIBLE); + getView().setVisibility(mHeadersGone ? View.GONE : View.VISIBLE); if (!mHeadersGone) { if (mHeadersEnabled) { listView.setChildrenVisibility(View.VISIBLE); -- cgit v1.2.3