2014-09-12 71 views
11

任何曾經嘗試過的Xamarin.Forms ListView和包含Image視圖的ItemTemplate?現在,當ListView包含約20行或更多行時會發生什麼?Android上的Xamarin.Forms ListView OutOfMemoryError異常

至於我,我有一個大約4K大小的.png文件加載到圖像視圖。在應用程序崩潰之前顯示的最大9 - 12行OutOfMemoryError。在Android Manifest中請求一個大堆之後,該應用程序在60-70行之後崩潰。我知道Xamarin正在推廣使用BitmapFactory類來縮小位圖,但是這對於Xamarin Forms Image View並不適用(開箱即用)。

我關於試圖擺弄ImageRenderer的Sub Class,看看我是否可以添加一個BitmapFactory.Options屬性,如果這將解決問題。

此外,我可能需要檢查Xamarin.Forms是否在ViewCell滾動屏幕後處理(回收)包含的位圖。

在開始這段旅程之前,我非常希望得到任何可以使這個過程變得簡單或更簡單的解決方案,從而認爲這個過程是不必要的。

展望未來...

+0

什麼的4K PNG的位圖大小? PNG存儲在內存中而不壓縮。當轉換爲位圖時,可以創建一個超過1GB數據的4K PNG。 另外,是的,你真的需要檢查位圖是否處置。也許答案是,不,他們不是。 – Frank 2014-09-12 11:35:03

+0

我目前使用的PNG被定義爲512 x 512. – Avrohom 2014-09-12 12:02:01

+0

因此,4kB PNG需要512 x 512 x 32位= 1MB的RAM來存儲/顯示。所以你很可能不會處置它們。 – Frank 2014-09-12 12:04:33

回答

10

是的,我找到了解決方案。要遵循的代碼。但在此之前,讓我稍微解釋我所做的一切。

因此,肯定需要在我們自己的手中使用maters來處理圖像及其底層資源(位圖或可繪製,但是您想稱之爲)。基本上,它歸結爲處理原生的'ImageRenderer'對象。

現在,無法從任何地方獲取對該ImageRenderer的引用,因爲要這樣做,需要能夠調用Platform.GetRenderer(...)。由於其範圍被聲明爲「內部」,因此無法訪問「平臺」類。

因此,除了爲Image類和它的(Android)Renderer子類並從內部銷燬這個Renderer本身(作爲參數傳遞'true')之外,我沒有別的選擇。不要嘗試'false 「)。在渲染器內部,我掛鉤頁面消失(如果是TabbedPage)。在大多數情況下,頁面消失事件不會很好地發揮作用,例如當頁面仍處於屏幕堆棧中時,由於另一個頁面正在繪製在頂部之上而消失。如果您丟棄圖像,則當頁面再次被覆蓋(顯示)時,它將不會顯示圖像。在這種情況下,我們必須掛鉤主導航頁面的「彈出」事件。

我試圖盡我所能解釋。剩下的 - 我希望 - 你將能夠從代碼中獲得:

這是PCL項目中的圖像子類。

using System; 

using Xamarin.Forms; 

namespace ApplicationClient.CustomControls 
{ 
    public class LSImage : Image 
    { 
    } 
} 

以下代碼在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); 
      } 
     } 
    } 

最後,我在PCL項目改變了應用程序的名稱的命名空間「ApplicationClient」和Droid的項目爲「Application.Droid」。您應該將其更改爲您的應用程序名稱。

此外,Renderer類末尾的幾個遞歸方法,我知道我可以將它組合成一個Generic方法。事情是,當我需要時,我一次一個建立一個。所以,我就是這樣離開它的。

快樂編碼,

Avrohom

+0

感謝您分享您的代碼和解釋。只有一件事,我可以在代碼中使用ImageCell嗎?我嘗試了一個自定義ViewCell,但無法使其工作。乾杯 – user1667474 2014-09-29 13:35:20

+0

從來沒有與ImageCell嘗試過。什麼阻止你使用ViewCell?如果你堅持使用ImageCell,那麼我認爲改變'GetContainingViewCell'方法中的代碼就是一個好主意,只要你有'ViewCell'就可以用'ImageCell'取代它。我看不到它不應該做的一個理由。 – Avrohom 2014-09-29 16:12:41

+0

更重要的是,您可以將'ViewCell'改爲'Cell',這樣它們就可以同時使用。 – Avrohom 2014-09-29 16:13:43

2

另一組的步驟可以幫助如下:

似乎有在Android涉及的列表視圖中的內存泄漏與定製單元。我做了一些調查,發現如果我沒有在我的網頁如下:

protected override void OnAppearing() 
    { 
     BindingContext = controller.Model; 
     base.OnAppearing(); 
    } 

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

設置在清單中的Android選項使用大量堆(機器人:largeHeap =「真」),最後,用新的垃圾收集器,(在environment.txt中,MONO_GC_PARAMS = bridge-implementation = new) 這種組合的東西似乎可以解決崩潰問題。我只能假設這只是因爲將事情設置爲null,有助於GC處置元素,大堆大小購買GC時間,以及新的GC選項來幫助加速收集本身。我真誠地希望這可以幫助別人......

+0

我認爲你仍然有內存泄漏。用largeHeap =「true」解決事情真的是一個壞主意。它將在具有更多內存的設備上工作,但「小」設備不會有額外的內存可用。你只是推遲了崩潰。 – 2016-11-28 07:57:22

+0

你的正確,這就是爲什麼我建議,只是爲了讓垃圾收集器通過並釋放空間而花時間。我有一個列表視圖,每個單元格有多個圖像和文本,每次在一個屏幕上顯示5到10個單元格(取決於屏幕空間),包含整個列表的數百部分,具有256 MB RAM的設備,處理它沒有崩潰。這只是一個創可貼,直到Xamarin人可以修復內存泄漏。 – TChadwick 2016-11-29 15:40:45

-2

在Visual Studio 2015年進入調試選項> .droid屬性> Android的選項>高級,並設置Java的最大堆大小達到1G

相關問題