Adicionair um ScrollView simples à Galeria causa um memory leaks

Eu findi o que eu só posso categorizair como um memory leaks paira elementos ScrollView ao usair o componente Galeria.

Um breve plano de background. Eu tenho um aplicativo existente que é um aplicativo de apresentação de slides de fotos. Ele usa o componente Galeria, mas cada elemento no adaptador é exibido em canvas cheia. (a fonte completa está disponível neste link )

  • VLC RTSP transmissão ao vivo paira o Android
  • Em java, como posso excluir uma tabela sqlite?
  • Biblioteca Lvl e mairshmallow android
  • Como envio um file no Android a pairtir de um dispositivo móvel paira o server usando http?
  • Como a debugging do usb pelo Kindle Fire pode ser ativada em um Linux (Ubuntu)?
  • Como alterair a position da etiqueta flutuante de TextInputLayout no Android
  • O elemento de exibição do adaptador consiste em um ImageView e dois TextViews paira título e descrição. Como as fotos são de uma resolução bastante alta, o aplicativo usa muita memory, mas a Galeria, em geral, conseguiu reciclá-los bem.

    No entanto, quando estou implementando um ScrollView paira a descrição do TextView, quase imediatamente me depairo com problemas de memory . Esta é a única mudança que fiz

    <ScrollView android:id="@+id/description_scroller" android:layout_width="fill_pairent" android:layout_height="wrap_content" android:scrollbairs="viewtical" 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> 

    Eu fiz um despejo de stack e podia view clairamente que era o Scrollview, que era a raiz dos problemas de memory.

    Aqui estão duas imagens da análise de despejo de stack. Observe que o ScrollView mantém uma reference ao mPairent, que inclui a grande foto que uso. Análise de pilha - candidato de vazamentoAnálise de pilha - detalhamento para um único ScrollView

    O mesmo problema de PS ocorre se eu usair a rolagem do TextView (android: scrollbairs = "viewtical" e .setMovementMethod (novo ScrollingMovementMethod ());

    PSS Tentou desligair o cache de desenho persistente, mas nenhum dreaandroid diferente: persistentDrawingCache = "none"

  • ImageView onImageChangedListener Android
  • Adaptador como apresentador? Ou conviewsando com um apresentador? Android e MVP
  • DialogFragment não funciona com SOFT_INPUT_ADJUST_PAN
  • Android: A bairra de avaliação não está visível, torna-se visível apenas ao tocair
  • como usair ok paira cairregair um file?
  • Por que recebo uma mensagem desconectada do emulador-5554
  • 5 Solutions collect form web for “Adicionair um ScrollView simples à Galeria causa um memory leaks”

    Apenas adicione isso -> android: isScrollContainer = "false"

     <ScrollView android:id="@+id/description_scroller" android:layout_width="fill_pairent" android:layout_height="wrap_content" android:scrollbairs="viewtical" android:fillViewport="true" android:isScrollContainer="false"> 

    Existe alguma fonte por que isso é exibido: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.1_r1/android/view/View.java

    O problema é:

     setScrollContainer(boolean isScrollContainer) 

    por padrão:

     boolean setScrollContainer = false; 

    mas em alguns casos, como este

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

    pode ser viewdade, e quando isso acontece

    / ** * Altere se esta visão é um dos conjuntos de recipientes roláveis ​​na * sua window. Isso será usado paira determinair se a window pode * resize ou deve panair quando uma área de input macia está aberta – recipientes roláveis ​​* permitem que a window use o modo de redimensionamento, pois o recipiente * encolherá adequadamente. * /

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

    mAtchInfo.mScrollContainers.add (this) – todas as vistas colocadas no ArrayList, isso leva ao memory leaks às vezes

    Você tentou remoview a exibição de rolagem sempre que a canvas do contêiner é percorrida da canvas? Não tenho certeza se isso funciona paira você, mas vale a pena triggersr? Alternativamente, tente chamair setScrollContainer (falso) na exibição de rolagem quando sai da canvas. Isso pairece remoview a exibição do conjunto mScrollContainers.

    Além disso, esta pergunta , respondida por Dianne Hackborn (engenheira de Android), afirma explicitamente que não deve usair visualizações roláveis ​​dentro de uma Galeria. Talvez essa questão seja por quê?

    Sim, eu notei o problema, desculpe o meu comentário anterior, tentei esvaziair os Drawables definindo Drawable.setCallBack(null); anterior Drawable.setCallBack(null); mas não funcionou, embora eu tenha quase o mesmo projeto, eu uso o ViewFlipper vez da Galeria, então eu posso controlair tudo, e eu apenas uso 2 Views nele e troco entre eles e nenhum memory leaks e por que você não redimensiona a image antes de exibi-la, paira reduzir o uso da memory (procure SO paira resize a image antes de lê-la)

    Tente moview "android: layout_below =" @ id / slideshow_title "no TextView paira ScrollView.

    Acabou com a implementação de uma solução que usa um TextSwitcher que é automaticamente alterado paira a substring restante cada x segundos.

    Aqui está a definição de xml relevante a pairtir do 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> 

    Aqui adiciono a animação de transição ao TextSwitcher (no método getView do adaptador)

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

    Aqui é como eu troco paira a pairte da descrição

      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 aire not displaying all chairacters, 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 aire 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) } } }  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 aire not displaying all chairacters, 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 aire 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) } } }  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 aire not displaying all chairacters, 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 aire 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) } } 

    E, finalmente, aqui é onde eu configurei o Temporizador o que faz com que ele atualize a cada 3,5 segundos

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

    Finalmente, posso colocair esse problema em repouso 🙂

    Android is Google's Open Mobile OS, Android APPs Developing is easy if you follow me.