Adding a simple scrollview to the gallery causes a memory leak

I came across the fact that I can only classify as a memory leak for ScrollView elements when using the Gallery component.

Short background. I have an existing application, which is a slide show application. It uses the Gallery component, but each element in the adapter is displayed in full screen. (full source is available at this link )

The adapter's View element consists of an ImageView and two TextViews for the title and description. Since the photos have a fairly high resolution, the application uses a lot of memory, but the Gallery, as a rule, processes them well.

However, when I now implement ScrollView to describe the TextView, I run into memory problems almost immediately . This is the only change I made

<ScrollView android:id="@+id/description_scroller" android:layout_width="fill_parent" android:layout_height="wrap_content" android:scrollbars="vertical" android:fillViewport="true"> <TextView android:id="@+id/slideshow_description" android:textSize="@dimen/description_font_size" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@color/white" android:layout_below="@id/slideshow_title" android:singleLine="false" android:maxLines="4"/> </ScrollView> 

I made a bunch of heaps and could clearly see that Scrollview was the root of the memory problems.

Here are two screenshots from the heap dump analysis. Please note that ScrollView maintains a link to mParent, which includes a large photo that I use Heap analysis - leak candidateHeap analysis - drilldown to a single ScrollView

PS the same problem occurs if I use TextView scrolling (android: scrollbars = "vertical" and .setMovementMethod (new ScrollingMovementMethod ());

PSS Tried to disable the constant drawing cache, but no other dreaandroid: persistentDrawingCache = "none"

+7
source share
5 answers

Just add this -> android: isScrollContainer = "false"

 <ScrollView android:id="@+id/description_scroller" android:layout_width="fill_parent" android:layout_height="wrap_content" android:scrollbars="vertical" android:fillViewport="true" android:isScrollContainer="false"> 

There is a source why this looks: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.1_r1/android/view/View.java

the problem is this:

 setScrollContainer(boolean isScrollContainer) 

default:

 boolean setScrollContainer = false; 

but in some cases, such as

 if (!setScrollContainer && (viewFlagValues&SCROLLBARS_VERTICAL) != 0) { setScrollContainer(true); } 

this may be true and when that happens

/ ** * Change whether this view is one of the many scrollable containers in * its window. This will be used to determine if the window * can resize or pan when the soft input area is open - scrollable * containers allow the window to use the resize mode, as the container * will decrease accordingly. * /

 public void setScrollContainer(boolean isScrollContainer) { if (isScrollContainer) { if (mAttachInfo != null && (mPrivateFlags&SCROLL_CONTAINER_ADDED) == 0) { mAttachInfo.mScrollContainers.add(this); mPrivateFlags |= SCROLL_CONTAINER_ADDED; } mPrivateFlags |= SCROLL_CONTAINER; } else { if ((mPrivateFlags&SCROLL_CONTAINER_ADDED) != 0) { mAttachInfo.mScrollContainers.remove(this); } mPrivateFlags &= ~(SCROLL_CONTAINER|SCROLL_CONTAINER_ADDED); } } 

mAttachInfo.mScrollContainers.add (this) - all views placed in an ArrayList sometimes lead to a memory leak

+3
source

Have you tried to remove the scroll view whenever it displays the contents of the container from the screen? I'm not sure if this works for you, but is it worth it? Also, try calling setScrollContainer (false) in the scroll view when it leaves the screen. This is similar to removing a view from the mScrollContainers collection.

In addition, this question , answered by Dianne Hackborn (android engineer), explicitly states that he should not use scrollable views inside the gallery. Maybe this is a problem?

+4
source

Yes, I noticed a problem, sorry for my previous comment, I tried to clear Drawables by setting the previous Drawable.setCallBack(null); but it didn’t work, btw I have almost the same project, I use ViewFlipper instead of Gallery, so I can control everything and I just use 2 views in it and switch between them and there is no memory leak and why you do not resize the image before displaying it, so this will reduce memory usage (SO search for resizing the image before reading it).

+2
source

Try moving "android: layout_below =" @ id / slideshow_title "to TextView for ScrollView.

0
source

Finished using a workaround that uses TextSwitcher, which is automatically replaced with the remaining substring every x seconds.

Here is the corresponding xml definition from the layout

  <TextSwitcher android:id="@+id/slideshow_description" android:textSize="@dimen/description_font_size" android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView android:id="@+id/slideshow_description_anim1" android:textSize="@dimen/description_font_size" android:layout_width="wrap_content" android:layout_height="wrap_content" android:maxLines="2" android:textColor="@color/white" android:singleLine="false"/> <TextView android:id="@+id/slideshow_description_anim2" android:textSize="@dimen/description_font_size" android:layout_width="wrap_content" android:layout_height="wrap_content" android:maxLines="2" android:textColor="@color/white" android:singleLine="false"/> </TextSwitcher> 

Here I add the transition animation to the TextSwitcher (in the adapter's getView method)

 final TextSwitcher slideshowDescription = (TextSwitcher)slideshowView.findViewById(R.id.slideshow_description); Animation outAnim = AnimationUtils.loadAnimation(context, R.anim.slide_out_down); Animation inAnim = AnimationUtils.loadAnimation(context, R.anim.slide_in_up); slideshowDescription.setInAnimation(inAnim); slideshowDescription.setOutAnimation(outAnim); 

Here's how I change the description part

  private void updateScrollingDescription(SlideshowPhoto currentSlideshowPhoto, TextSwitcher switcherDescription){ String description = currentSlideshowPhoto.getDescription(); TextView descriptionView = ((TextView)switcherDescription.getCurrentView()); //note currentDescription may contain more text that is shown (but is always a substring String currentDescription = descriptionView.getText().toString(); if(currentDescription == null || description==null){ return; } int indexEndCurrentDescription= descriptionView.getLayout().getLineEnd(1); //if we are not displaying all characters, let swap to the not displayed substring if(indexEndCurrentDescription>0 && indexEndCurrentDescription<currentDescription.length()){ String newDescription = currentDescription.substring(indexEndCurrentDescription); switcherDescription.setText(newDescription); }else if(indexEndCurrentDescription>=currentDescription.length() && indexEndCurrentDescription<description.length()){ //if we are displaying the last of the text, but the text has multiple sections. Display the first one again switcherDescription.setText(description); }else { //do nothing (ie. leave the text) } } 

And finally, here I set up a timer that forces it to update every 3.5 seconds.

  public void setUpScrollingOfDescription(){ final CustomGallery gallery = (CustomGallery) findViewById(R.id.gallery); //use the same timer. Cancel if running if(timerDescriptionScrolling!=null){ timerDescriptionScrolling.cancel(); } timerDescriptionScrolling = new Timer("TextScrolling"); final Activity activity = this; long msBetweenSwaps=3500; //schedule this to timerDescriptionScrolling.scheduleAtFixedRate( new TimerTask() { int i=0; public void run() { activity.runOnUiThread(new Runnable() { public void run() { SlideshowPhoto currentSlideshowPhoto = (SlideshowPhoto)imageAdapter.getItem(gallery.getSelectedItemPosition()); View currentRootView = gallery.getSelectedView(); TextSwitcher switcherDescription = (TextSwitcher)currentRootView.findViewById(R.id.slideshow_description); updateScrollingDescription(currentSlideshowPhoto,switcherDescription); //this is the max times we will swap (to make sure we don't create an infinite timer by mistake if(i>30){ timerDescriptionScrolling.cancel(); } i++; } }); } }, msBetweenSwaps, msBetweenSwaps); } 

Finally, I can leave this problem alone :)

0
source

All Articles