Добавление простого ScrollView в галерею вызывает утечку памяти

Я столкнулся с тем, что я могу только классифицировать как утечку памяти для элементов ScrollView при использовании компонента Gallery.

Короткий фон. У меня есть существующее приложение, которое представляет собой приложение для слайд-шоу. Он использует компонент Gallery, но каждый элемент в адаптере отображается в полноэкранном режиме. (Полный источник доступен по этой ссылке )

Элемент View адаптера состоит из ImageView и двух TextView для заголовка и описания. Поскольку фотографии имеют довольно высокое разрешение, приложение использует довольно много памяти, но Галерея, как правило, хорошо их перерабатывает.

Однако, когда я сейчас внедряю ScrollView для описания TextView, я почти сразу сталкиваюсь с проблемами памяти . Это единственное изменение, которое я сделал

<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> 

Я сделал кучу кучи и мог ясно видеть, что именно Scrollview является корнем проблем с памятью.

Вот два скриншота из анализа дампа кучи. Обратите внимание, что ScrollView сохраняет ссылку на mParent, которая включает в себя большую фотографию, которую я использую Анализ кучи - кандидат на утечкуАнализ кучи - развертка до одного ScrollView

PS такая же проблема возникает, если я использую прокрутку TextView (android: scrollbars = "vertical" и .setMovementMethod (новый ScrollingMovementMethod ());

PSS Пробовал отключить постоянный кэш чертежа, но никакого другого dreaandroid: persistentDrawingCache = "none"

Просто добавьте это -> 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"> 

Существует некоторый источник, почему это выглядит: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.1_r1/android/view/View.java

проблема в:

 setScrollContainer(boolean isScrollContainer) 

по умолчанию:

 boolean setScrollContainer = false; 

Но в некоторых случаях

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

Это может быть правдой, и когда это происходит

/ ** * Измените, является ли это представление одним из множества прокручиваемых контейнеров в * его окне. Это будет использоваться, чтобы определить, может ли окно изменять размер или панорамировать, когда открыта область мягкого ввода – прокручиваемые * контейнеры позволяют окну использовать режим изменения размера, так как контейнер * будет соответствующим образом уменьшаться. * /

 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 (это) – все представления, помещенные в ArrayList, приводят к иногда утечке памяти

Вы пытались удалить просмотр прокрутки всякий раз, когда просматривается окно контейнера с экрана? Я не уверен, что это работает для вас, но это стоит того? Кроме того, попробуйте вызвать setScrollContainer (false) в представлении прокрутки, когда он покидает экран. Это похоже на удаление представления из набора mScrollContainers.

Кроме того, этот вопрос , на который ответил Dianne Hackborn (инженер-андроид), явно заявляет, что не должен использовать прокручиваемые представления внутри Галереи. Может быть, это проблема?

Да, я заметил проблему, извините за свой предыдущий комментарий, я попытался очистить Drawables, установив предыдущий Drawable.setCallBack(null); Но did not work, btw У меня почти такой же проект, я использую ViewFlipper вместо Gallery, поэтому я могу контролировать все, и я просто использую 2 Представления в нем и переключаюсь между ними, без утечки памяти, и почему бы вам не изменять размер Изображение перед его отображением, поэтому это уменьшит использование памяти (поиск SO для изменения размера изображения перед его чтением)

Попробуйте переместить "android: layout_below =" @ id / slideshow_title "в TextView для ScrollView.

Закончилось использование обходного пути, в котором используется TextSwitcher, который автоматически изменяется на оставшуюся подстроку каждые x секунд.

Вот соответствующее определение xml из макета

  <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> 

Здесь я добавляю анимацию перехода к TextSwitcher (в методе getView адаптера)

 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); 

Вот как я подменяю часть описания

  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) } } 

И, наконец, здесь я настраиваю таймер, который заставляет его обновлять каждые 3,5 секунды

  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); } 

Наконец, я могу поставить эту проблему на отдых 🙂