2013-08-08 11 views
9

Testuję aplikację WP8 i jej przeglądarkę obrazów, aby wyświetlić wiele obrazów. Zauważyłem, że zużycie pamięci aplikacji rośnie i chcę się dowiedzieć, jak go rozwiązać.Zużycie pamięci BitmapImage/Kontrola obrazu w Windows Phone 8

Czytałem niektóre artykuły z internetu, jednak rozwiązania dostarczone przez te artykuły nie działają na mojej aplikacji, zapoznaj się z historią poniżej.

Po pierwsze, uważam, że artykuł „Image Tips for Windows Phone 7” i pobrać jej próbkę do badań należy czyścić cache obraz, to działa z 1 obrazek.

A potem do celów testowych, robię to aplikacja przygotowana z 15 obrazów w trybie offline wewnątrz aplikacji i ustawić jako „Content”, należy pobrać testową aplikację z here.

Moi etapy testowania są:

(1) Launch app 
(2) Go to Image Caching page 
(3) Enable checkbox "Avoid Image Caching" 
(4) Continuously tapping button Show/Clear 
(5) Keep watching the memory status textblock at the bottom 

Kiedy jestem testowania mojej aplikacji, pamięć jest drapanie, jak 16.02MB => Show (19.32MB) => clear (16.15MB) => Pokaż (20.18MB) => Wyczyść (17.03MB) ... itd. Pamięć nie zostanie zwolniona nawet po opuszczeniu strony pamięci podręcznej i ponownym przejściu do strony buforowania. Wygląda na to, że rozwiązanie artykułu "Image Tips for Windows Phone 7" działa tylko dla 1 obrazu.

Nadchodzi kod xaml i kodu źródłowego rozwiązania "Image Tips for Windows Phone 7".

[Caching.xaml]

 <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> 
      <StackPanel Orientation="Horizontal" VerticalAlignment="Top"> 
       <ToggleButton Content="Show" Width="150" Checked="ShowImageClicked" Unchecked="ClearImageClicked"/> 
       <CheckBox x:Name="cbAvoidCache" Content="Avoid Image Caching"/> 
      </StackPanel> 
      <Image x:Name="img" Grid.Row="2" Width="256" Height="192"/> 
      <TextBlock x:Name="tbMemory" Grid.Row="2" Text="Memory: " VerticalAlignment="Bottom" Style="{StaticResource PhoneTextLargeStyle}"/> 
     </Grid> 

[Caching.xaml.cs]

public partial class Caching : PhoneApplicationPage 
{ 
    public Caching() 
    { 
     InitializeComponent(); 

     DispatcherTimer timer = new DispatcherTimer(); 
     timer.Interval = TimeSpan.FromMilliseconds(500); 
     timer.Start(); 
     timer.Tick += delegate 
     { 
      GC.Collect(); 
      tbMemory.Text = string.Format("Memory: {0} bytes", DeviceExtendedProperties.GetValue("ApplicationCurrentMemoryUsage")); 
     }; 
    } 

    private int nIndex = 1; 
    BitmapImage bitmapImageFromUri = new BitmapImage(); 
    private void ShowImageClicked(object sender, RoutedEventArgs e) 
    { 
     string strImage = string.Format("../ImagesAsContent/{0:D2}.jpg", nIndex); 
     bitmapImageFromUri.UriSource = new Uri(strImage, UriKind.Relative); 
     img.Source = bitmapImageFromUri; 

     nIndex++; 
     if (nIndex > 15) 
     { 
      nIndex = 1; 
     } 

     (sender as ToggleButton).Content = "Clear"; 
    } 

    private void ClearImageClicked(object sender, RoutedEventArgs e) 
    { 
     if (cbAvoidCache.IsChecked == true) 
     { 
      // set the UriSource to null in order to delete the image cache 
      BitmapImage bitmapImageFromUri = img.Source as BitmapImage; 
      bitmapImageFromUri.UriSource = null; 
     } 
     img.Source = null; 
     (sender as ToggleButton).Content = "Show"; 
    } 
} 

że próbował również do wyszukiwania innych rozwiązań niektóre wyniki testów podano poniżej.

(1) Artykuł "[wpdev] Memory leak with BitmapImage": Dostarcza 2 rozwiązania, jednym jest DisposeImage API, innym jest ustawienie źródła BitmapImage na wartość null, jak poniżej. Również w tym artykule informujemy, że musimy uważać na obsługę dołączania/dettach zdarzeń, jednak moja aplikacja do testowania nie ma obsługi zdarzeń na stronie buforowania.

[DisposeImage]

private void DisposeImage(BitmapImage image) 
{ 
    if (image != null) 
    { 
     try 
     { 
      using (var ms = new MemoryStream(new byte[] { 0x0 })) 
      { 
       image.SetSource(ms); 
      } 
     } 
     catch (Exception) 
     { 
     } 
    } 
} 

[Set null]

BitmapImage bitmapImage = image.Source as BitmapImage; 
bitmapImage.UriSource = null; 
image.Source = null; 

(2) Artykuł "Windows phone: listbox with images out-of-memory": To zapewnia API "DisposeImage" z małą różnicą niż (1) jak poniżej, ale to też nie działa, wciąż mam symptomy powodujące wzrost pamięci.

public static void DisposeImage(BitmapImage image) 
{ 
    Uri uri= new Uri("oneXone.png", UriKind.Relative); 
    StreamResourceInfo sr=Application.GetResourceStream(uri); 
    try 
    { 
    using (Stream stream=sr.Stream) 
    { 
     image.DecodePixelWidth=1; //This is essential! 
     image.SetSource(stream); 
    } 
    } 
    catch 
    {} 
} 

(3) Artykuł „Cannot find the memory leak”: Zapewnia te same 2 rozwiązania, jak wyżej wspomniano, również on wymieniony problem nie może powtórzenia błędu dla obrazów samodzielnie magazynowania, jednak obrazy moich testów aplikacji są z izolowanym przechowywania.

(4) Próbowałem również na 1000 zdjęć, wynikiem testu jest awaria aplikacji, gdy aplikacja wyświetla sekwencyjnie około 190 obrazów, zapoznaj się z grafiką analizy aplikacji systemu Windows Phone dla pamięci poniżej. enter image description here

Wreszcie, dziękuję za cierpliwość, aby przeczytać moje pytanie i historię, pracuję nad tym, aby znaleźć rozwiązanie na wiele dni. Jeśli masz jakąkolwiek wskazówkę lub rozwiązanie, uprzejmie proszę dać mi znać.

Dzięki.

+1

Dobre pytanie, mają te same problemy. Jakieś wieści tutaj? –

+0

Ciągle szukam rozwiązania. Wygląda na to, że jest to błąd WP7.5, co oznacza, że ​​dzieje się tak również na WP8, ale nie atakujesz tylko WP8. –

Odpowiedz

3

miałem do czynienia z tym samym problemem i myślę, że w końcu, że rzeczywiście znalazłem obejście, nie jestem pro programista, ale tutaj jest moje rozwiązanie:

public Task ReleaseSingleImageMemoryTask(MyImage myImage, object control) 
    { 
     Pivot myPivot = control as Pivot; 
     Task t = Task.Factory.StartNew(() => 
     { 
      Deployment.Current.Dispatcher.BeginInvoke(() => 
      { 
       if (myImage.img.UriSource != null) 
       { 
        myImage.img.UriSource = null; 
        DisposeImage(myImage.img); 
       } 
       PivotItem it = (PivotItem)(myPivot.ItemContainerGenerator.ContainerFromIndex(myImage.number % 10)); 
       Image img = FindFirstElementInVisualTree<Image>(it); 
       if (img != null) 
       { 
        img.Source = null; 
        GC.Collect(); 
       } 
      }); 
      myImage.released = true; 
     }); 
     return t; 
    } 


private T FindFirstElementInVisualTree<T>(DependencyObject parentElement) where T : DependencyObject 
    { 
     var count = VisualTreeHelper.GetChildrenCount(parentElement); 
     if (count == 0) 
      return null; 

     for (int i = 0; i < count; i++) 
     { 
      var child = VisualTreeHelper.GetChild(parentElement, i); 

      if (child != null && child is T) 
      { 
       return (T)child; 
      } 
      else 
      { 
       var result = FindFirstElementInVisualTree<T>(child); 
       if (result != null) 
        return result; 
      } 
     } 
     return null; 
    } 

    private void DisposeImage(BitmapImage img) 
    { 
     if (img != null) 
     { 
      try 
      { 
       using (var ms = new MemoryStream(new byte[] { 0x0 })) 
       { 
        img = new BitmapImage(); 
        img.SetSource(ms); 
       } 
      } 
      catch (Exception e) 
      { 
       System.Diagnostics.Debug.WriteLine("ImageDispose FAILED " + e.Message); 
      } 
     } 
    } 

Nadzieja ta pomoc :)

+0

Cześć Damian, dziękuję za odpowiedź, chociaż nie testuję tego problemu, wciąż dziękuję za odpowiedź. –

+0

Proszę, pomóżcie mi tutaj http://stackoverflow.com/questions/24161008/coverflow-with-out-of-memory – user2056563

+1

przy próbie usunięcia obrazu, który nazywacie 'img = new BitmapImage();'. czy to nie jest celem tego wszystkiego? – thumbmunkeys