TransitionDrawable improper use causes memory leakage, transitiondrawable
Recently, I want to make a blur effect similar to that of Netease cloud music background. I also want to make the background changes not so stiff, that is, the following effect.
Google decided to use TransitionDrawable. Because it was used with UniversalImageLoader, you only need to implement a BitmapDisplayer as the UIL configuration item.
The original code was written in this way.
private static class DrawableFadeDisplayer implements BitmapDisplayer { private final int durationMillis; public DrawableFadeDisplayer(int durationMillis) { this.durationMillis = durationMillis; } @Override public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) { ImageView imageview = (ImageView) imageAware.getWrappedView(); Drawable oldDrawable = imageview.getDrawable(); TransitionDrawable td = new TransitionDrawable(new Drawable[] { oldDrawable==null?(new ColorDrawable(Color.TRANSPARENT)):oldDrawable, new BitmapDrawable(Resources.getSystem(), bitmap) }); imageview.setImageDrawable(td); td.startTransition(durationMillis); }}
The most important part isdisplayFirst, get the oldDrawableAnd then generate a newBitmapDrawableConstructTransitionDrawable, And finally callstartTransitionYou can.
Simple and clear. In actual use, it is found that the memory occupied by the app is getting higher and higher, but you only need to exitActivityAfter one or two GC operations, the memory will be reduced. It can be confirmed that this code has caused Memory leakage.
The problem lies in this Code.
Drawable oldDrawable = imageview.getDrawable();
At first glance, there is no problem with the code logic. Every time we change the oldDrawableAs the first layer, the newDrawableCreate as Level 2TransitionDrawableBut note that we createdTransitionDrawableAnd set itImageViewThat is, we callgetDrawableYes.TransitionDrawable, OneTransitionDrawableIt actually holds multipleDrawableHere there are two.
After the program performs the first gradient animation,ImageViewInTransitionDrawableHold twoDrawable, The second gradient animation, we willTransitionDrawableNewBitmapDrawableCreate a newTransitionDrawable.
Just give a brief lookImageViewHoldDrawable:
After the first gradient:
TransitionDrawable(drawable0, drawable1)
After the second gradient:
TransitionDrawable( TransitionDrawable(drawable0, drawable1), drawable2)
After the third gradient:
TransitionDrawable( TransitionDrawable( TransitionDrawable(drawable0, drawable1), drawable2 ), drawable3)
This wayImageViewWhich cannot be recycledDrawableThe number is increasing, and eventually OOM.
Therefore, we should not directlygetDrawableIs used as the first layer.DrawableTo determine the type of the value.TransitionDrawableThe second layer should be obtained.DrawableAs our first layer, the original first layerDrawableThe reference chain of GC Roots is lost and can be recycled.
Of course, another idea is:TransitionDrawableAfter the animation is completedBitmapDrawableSetImageViewBut there is no such listener, the simplest and most convenient is the above idea.
The final code is changed to the following, mainly to determinegetDrawableType, if it isTransitionDrawableTo obtain the second layer.Drawable.
private static class DrawableFadeDisplayer implements BitmapDisplayer { private final int durationMillis; public DrawableFadeDisplayer(int durationMillis) { this.durationMillis = durationMillis; } @Override public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) { ImageView imageview = (ImageView) imageAware.getWrappedView(); Drawable oldDrawable = imageview.getDrawable(); Drawable oldBitmapDrawable = null; if (oldDrawable == null) { oldBitmapDrawable = new ColorDrawable(Color.TRANSPARENT); } else if (oldDrawable instanceof TransitionDrawable) { oldBitmapDrawable = ((TransitionDrawable) oldDrawable).getDrawable(1); } else { oldBitmapDrawable = oldDrawable; } TransitionDrawable td = new TransitionDrawable(new Drawable[] { oldBitmapDrawable, new BitmapDrawable(Resources.getSystem(), bitmap) }); imageview.setImageDrawable(td); td.startTransition(durationMillis); }}
Copyright Disclaimer: This article is an original article by the blogger and cannot be reproduced without the permission of the blogger.