Добавление простого ScrollView в галерею вызывает утечку памяти - PullRequest
7 голосов
/ 17 января 2012

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

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

Элемент 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, которая включает в себя большую фотографию, которую я использую Heap analysis - leak candidate Heap analysis - drilldown to a single ScrollView

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

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

Ответы [ 5 ]

4 голосов
/ 25 января 2012

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

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

3 голосов
/ 31 июля 2012

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

2 голосов
/ 22 января 2012

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

0 голосов
/ 11 сентября 2012

Закончено внедрение обходного пути, в котором используется 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);
    }

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

0 голосов
/ 28 января 2012

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...