Xamarin.Forms ListView OutOfMemoryError excepción en Android

¿Alguien ha intentado alguna vez Xamarin.Forms Listview con un ItemTemplate que contiene una vista de imagen? Ahora, ¿qué sucede cuando ListView contiene aproximadamente 20 o más filas?

En cuanto a mí, tengo un archivo .png de alrededor de 4K de tamaño cargado en la vista de imagen. Consiguió un máximo de 9 – 12 filas mostradas antes de que la aplicación se estrellara con un OutOfMemoryError. Después de solicitar un montón grande en el manifiesto de Android, la aplicación se bloquea después de 60 a 70 filas.

Sé que Xamarin está promoviendo el uso de la clase BitmapFactory para escalar mapas de bits, pero esto no es aplicable (fuera de la caja) para el Xamarin Forms Image View.

Estoy tratando de jugar con una subclase de ImageRenderer para ver si puedo agregar una propiedad BitmapFactory.Options y si esto solucionará el problema.

Además, es posible que tenga que comprobar si Xamarin.Forms no dispone (reciclar) el mapa de bits contenido después de que el ViewCell se desplaza de la pantalla.

Antes de emprender este viaje, estaría muy interesado en recibir cualquier comentario que pudiera hacer que esto sea más fácil o una solución más simple que considere este proceso innecesario.

Viendo hacia adelante…

Sí, encontré una solución. Código a seguir. Pero antes de eso, permítanme explicar un poco lo que he hecho.

Por lo tanto, definitivamente hay una necesidad de tomar maters en nuestras propias manos para disponer de la imagen y sus recursos subyacentes (mapa de bits o dibujable, sin embargo desea llamar). Básicamente, se trata de disponer el objeto nativo 'ImageRenderer'.

Ahora, no hay manera de obtener una referencia a que ImageRenderer desde cualquier lugar, porque para ello, es necesario ser capaz de llamar a Platform.GetRenderer (…). El acceso a la clase 'Platform' es inaccesible ya que su ámbito es declarado como 'internal'.

Por lo tanto, me han quedado sin otra opción que subclase de la clase Image y su (Android) Renderer y destruir este Renderer desde dentro (pasando 'true' como argumento. No intente con 'false'). Dentro del Renderer me engancho a la página desaparecer (en caso de una TabbedPage). En la mayoría de las situaciones, el evento Disparar página no servirá bien el propósito, como cuando la página sigue en la pila de pantalla pero desaparece debido a que se está dibujando otra página en la parte superior de la misma. Si desecha la (s) Imagen (s), cuando la página se vuelva a descubrir (mostrada), no mostrará las imágenes. En tal caso, tenemos que conectar en la página de navegación principal 'Popped' evento.

He tratado de explicar lo mejor que pude. El resto – espero – usted podrá conseguir del código:

Esta es la Subclase de Imagen en el Proyecto PCL.

using System; using Xamarin.Forms; namespace ApplicationClient.CustomControls { public class LSImage : Image { } } 

El código siguiente está en el proyecto Droid.

 using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using Android.App; using Android.Content; using Android.OS; using Android.Runtime; using Android.Views; using Android.Views.InputMethods; using Android.Widget; using Android.Util; using Application.Droid.CustomControls; using ApplicationClient.CustomControls; using Xamarin.Forms; using Xamarin.Forms.Platform.Android; [assembly: ExportRenderer(typeof(ApplicationClient.CustomControls.LSImage), typeof(LSImageRenderer))] namespace Application.Droid.CustomControls { public class LSImageRenderer : ImageRenderer { Page page; NavigationPage navigPage; protected override void OnElementChanged(ElementChangedEventArgs<Image> e) { base.OnElementChanged(e); if (e.OldElement == null) { if (GetContainingViewCell(e.NewElement) != null) { page = GetContainingPage(e.NewElement); if (page.Parent is TabbedPage) { page.Disappearing += PageContainedInTabbedPageDisapearing; return; } navigPage = GetContainingNavigationPage(page); if (navigPage != null) navigPage.Popped += OnPagePopped; } else if ((page = GetContainingTabbedPage(e.NewElement)) != null) { page.Disappearing += PageContainedInTabbedPageDisapearing; } } } void PageContainedInTabbedPageDisapearing (object sender, EventArgs e) { this.Dispose(true); page.Disappearing -= PageContainedInTabbedPageDisapearing; } protected override void Dispose(bool disposing) { Log.Info("**** LSImageRenderer *****", "Image got disposed"); base.Dispose(disposing); } private void OnPagePopped(object s, NavigationEventArgs e) { if (e.Page == page) { this.Dispose(true); navigPage.Popped -= OnPagePopped; } } private Page GetContainingPage(Xamarin.Forms.Element element) { Element parentElement = element.ParentView; if (typeof(Page).IsAssignableFrom(parentElement.GetType())) return (Page)parentElement; else return GetContainingPage(parentElement); } private ViewCell GetContainingViewCell(Xamarin.Forms.Element element) { Element parentElement = element.Parent; if (parentElement == null) return null; if (typeof(ViewCell).IsAssignableFrom(parentElement.GetType())) return (ViewCell)parentElement; else return GetContainingViewCell(parentElement); } private TabbedPage GetContainingTabbedPage(Element element) { Element parentElement = element.Parent; if (parentElement == null) return null; if (typeof(TabbedPage).IsAssignableFrom(parentElement.GetType())) return (TabbedPage)parentElement; else return GetContainingTabbedPage(parentElement); } private NavigationPage GetContainingNavigationPage(Element element) { Element parentElement = element.Parent; if (parentElement == null) return null; if (typeof(NavigationPage).IsAssignableFrom(parentElement.GetType())) return (NavigationPage)parentElement; else return GetContainingNavigationPage(parentElement); } } } 

Finalmente, he cambiado el Nombre de la Aplicación en el espacio de nombres a 'ApplicationClient' en el proyecto PCL ya 'Application.Droid' en el proyecto Droid. Deberías cambiarlo al nombre de tu aplicación.

Además, los pocos métodos recursivos al final de la clase Renderer, sé que podría combinarlo en un método genérico. La cosa es, que he construido uno a la vez como la necesidad surgió. Así es como lo dejé.

Codificación feliz,

Avrohom

Otro conjunto de pasos que pueden ayudar es el siguiente:

Parece que hay una pérdida de memoria en android que implica listas con celdas personalizadas. Hice algunas investigaciones y encontré que si hice lo siguiente en mis páginas:

  protected override void OnAppearing() { BindingContext = controller.Model; base.OnAppearing(); } protected override void OnDisappearing() { BindingContext = null; Content = null; base.OnDisappearing(); GC.Collect(); } 

Establezca la opción android en el manifiesto para usar un montón grande (android: largeHeap = "true"), y por último, utiliza el nuevo recolector de basura, (en el entorno.txt, MONO_GC_PARAMS = bridge-implementation = new) Esta combinación de cosas , Parece arreglar el problema de bloqueo. Sólo puedo asumir que esto es sólo porque el ajuste de las cosas a nulo, ayuda a la GC a disponer de los elementos, el gran tamaño de montón para comprar el tiempo de GC para hacerlo, y la nueva opción de GC para ayudar a acelerar la colección en sí. Espero sinceramente que esto ayude a alguien más …

En Visual Studio 2015 vaya a la opción Depuración> .droid Propiedades> Opciones de Android> Avanzadas y configure Java Max Heap Size a 1G

  • Xamarin.Forms popup "Nueva Versión Disponible"
  • Xamarin implementación de Android no utiliza el código más reciente?
  • Xamarin.Forms XAML - Agregar controles nativos
  • No se puede depurar la aplicación en Android Phone
  • Xamarin.Forms implementa AndHud y BTProgressHud
  • Línea azul claro sobre mi barra de navegación en la aplicación de Android
  • Xamarin Form Master Página de detalles que no se muestra
  • Xamarin Forms - Media Plugin - Vaciar las miniaturas en el teléfono
  • Xamarin.forms mezclando ContentPage y la actividad de Android
  • Xamarin.Forms Switch Control no es visible en Android antes de seleccionar un control de entrada
  • Arrastrar y soltar en formas Xamarin
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.