2015-08-25 8 views
6

Sto sviluppando un'applicazione Windows Phone 8.1. Ho uno schermo con una lista di titoli di notizie con miniature.Asincrono/attendi deadlock durante il download delle immagini

Prima sto facendo richiesta HTTP asincrona per ottenere raccolta notizie in JSON (soddisfare il NotifyTaskCompletion pattern)

NewsCategories = new NotifyTaskCompletion<ObservableCollection<NewsCategory>>(_newsService.GetNewsCategoriesAsync()); 

NewsCategory:

public class NewsCategory : ObservableObject 
{ 
    ... 
    public string Title { get; set; } 
    public ObservableCollection<News> Items { get; set; } 
} 

Notizie:

public class News : ObservableObject 
{ 
    ... 
    public string Title { get; set; } 
    public string ImagePath { get; set; } 
} 

Finora funziona perfettamente, ma non appena avrò la proprietà ImagePath, vorrei scaricare e visualizzare l'immagine fornita. Ho trovato una soluzione per farlo in modo asincrono qui: WP 8.1 Binding image from a http request - in modo che quando xaml ottiene il percorso dell'immagine, chiama una classe di convertitore (BinaryToImageSourceConverter), utilizzando anche il modello NotifyTaskCompletion.

Il problema si verifica nel seguente metodo:

private async Task<BitmapImage> GetImage(string path) 
{ 
    HttpClient webCLient = new HttpClient(); 
    var responseStream = await webCLient.GetStreamAsync(path); 
    var memoryStream = new MemoryStream(); 
    await responseStream.CopyToAsync(memoryStream); 
    memoryStream.Position = 0; 
    var bitmap = new BitmapImage(); 
    await bitmap.SetSourceAsync(memoryStream.AsRandomAccessStream()); 
    return bitmap; 
} 

Quando il primo await si chiama, il debugger non raggiungono mai la riga successiva e il metodo non ritorna mai. La variabile string path ha un contenuto adeguato.

Finora ho provato a utilizzare ConfigureAwait(false), ma nel mio caso non funziona.

ho trovato anche in tema: Deadlock while using async await, che:

Quando si utilizza ConfigureAwait(false), dite al vostro programma non vi dispiace sul contesto. Può risolvere alcuni problemi di stallo, ma di solito non è la soluzione giusta. La soluzione giusta probabilmente non attenderà mai le attività in modo bloccante e sarà completamente asincrona.

Non so dove potrei avere quella roba in un modo bloccante. Quale può essere la ragione per questo punto morto?

E se si tratta di un approccio errato, conosci qualche schema che sarebbe più appropriato per scaricare le miniature in una raccolta di elementi?

Grazie per il vostro aiuto.


aggiornamento: come è GetImage invocato: E 'come in argomento: WP 8.1 Binding image from a http request

public class WebPathToImage : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, string language) 
    { 
     if (value == null) return null; 
     return new NotifyTaskCompletion<BitmapImage>(GetImage((String)value)); 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, string language) 
    { throw new NotImplementedException(); } 

    private async Task<BitmapImage> GetImage(string path) 
    { 
     using (var webCLient = new HttpClient()) 
     { 
      webCLient.DefaultRequestHeaders.Add("User-Agent", "bot"); 
      var responseStream = await webCLient.GetStreamAsync(path).ConfigureAwait(false); 
      var memoryStream = new MemoryStream(); 
      await responseStream.CopyToAsync(memoryStream); 
      memoryStream.Position = 0; 
      var bitmap = new BitmapImage(); 
      await bitmap.SetSourceAsync(memoryStream.AsRandomAccessStream()); 
      return bitmap; 
     } 
    } 
} 

e in XAML:

<Image 
DataContext="{Binding ImagePath, Converter={StaticResource WebPathToImage}}" 
Source="{Binding Result}" 
Stretch="UniformToFill" 
Height="79" Width="79"/> 
+3

Come viene richiamato 'GetImage'? Oppure lo blocchi usando '.Result' o' .Wait'? –

+0

Ho modificato il mio post per mostrare il codice. –

+1

Non vedo direttamente nulla che possa essere sbagliato. Se potessi fare una piccola, minima riproduzione di questo, sarebbe di grande aiuto. –

risposta

0

Innanzitutto, se si utilizza Windows Phone 8.1, si consiglia di utilizzare HttpClient dallo spazio nomi Windows.Web.Http e non da System.Net.Http. Alcuni motivi sono spiegati su this link.

La mia risposta utilizza il nuovo Windows.Web.Http.HttpClient in modo che il metodo di GetImage può apparire come segue:

 private async Task<BitmapImage> GetImage(string path) 
     { 
      using (var webCLient = new Windows.Web.Http.HttpClient()) 
      { 
       webCLient.DefaultRequestHeaders.Add("User-Agent", "bot"); 
       var responseStream = await webCLient.GetBufferAsync(new Uri(path)); 
       var memoryStream = new MemoryStream(responseStream.ToArray()); 
       memoryStream.Position = 0; 
       var bitmap = new BitmapImage(); 
       await bitmap.SetSourceAsync(memoryStream.AsRandomAccessStream()); 
       return bitmap; 
      } 
     } 

Ho testato questo metodo su un URL di esempio ed è visualizzato correttamente in un controllo Image.

In secondo luogo, perché non lasciare che la maniglia Image di controllo scaricando il BitmapImage senza il convertitore, in modo che il codice XAML assomiglia a questo:

<Image 
    Source="{Binding ImagePath}" 
    Stretch="UniformToFill" 
    Height="79" 
    Width="79" /> 

In questo modo il ImagePath può essere un percorso a una risorsa da internet e una risorsa locale.

0

Prova forse a chiamarti getImage in Task.Factory . Eseguire per forzare l'attività da eseguire in un altro thread.

Attenzione anche a te la creazione di bitmap potrebbe avere problemi con esso perché la bitmap non viene creata nel thread dell'interfaccia utente.

Problemi correlati