| 

.NET C# Java Javascript Exception

4
Hallo Leute,

so langsam weiß ich selbst nicht weiter.. In meiner App (es soll eine einfache Podcast-App werden) nutze ich den LongListMultiSelector aus dem Windows Phone Toolkit (neueste Version vom August 2013).

Iich nutze den LongListMultiSelector, um den Benutzer auswählen zu lassen, welche Episoden eines Podcast er herunterladen kann. Die Auswahl ist dabei gruppiert nach Erscheinungs-Monat der Episoden. Wählt ein Benutzer einen (oder mehrere) Episoden aus und drück auf den Button "Download", werden die ausgewählten Episoden heruntergeladen. Das Herunterladen übernimmt dabei der BackgroundTransferService. Der Haken an der Geschichte ist jetzt der: sobald man mehr als ein paar Episoden zum Herunterladen auswählt, spinnt die UI etwas herum. Wenn alle selektierten Einträge an den BackgroundTransferService geschickt wurden, solte IsSelectionEnabled beim LongListMultiSelector auf false gesetzt werden, aber irgendwie wird diese Aktion nicht direkt ausgeführt sondern scheinbar zurückgestellt. Außerdem werden beim Scrollen manche Einträge gar nicht mehr angezeigt.

Die Vermutung: Ich horche das Verändern des Fortschritts eines BackgroundTransferRequests, welches bei jeder Änderung die Progress-Eigenschaft einer Episode aktualisiert (was dank NotifyPropertyChanged auch die UI aktualisiert).

Ist es möglich, dass diese Aktualisierung dazu führt, dass sich das gesamte User Interface nicht korrekt verhält?

Anbei noch etwas Code, evtl. findet ja jemand von euch das Problem mit der Performance :)

Das ist der Code, der ausgeführt wird, wenn man auf den Download-Button drückt.
void _btnDownload_Click(object sender, EventArgs e)
{
this._vm.LoadingText = AppResources.Loading;

foreach (Episode episode in selectorEpisodes.SelectedItems)
{
App.ViewModel.StartDownload(episode);
}

this._vm.IsLoading = false;
this.selectorEpisodes.IsSelectionEnabled = false;
}


App.ViewModel hält eine Instanz des MainViewModels:
public class MainViewModel : ViewModelBase
{
/// <summary>
/// Lists containing all Subscriptions (flat list)
/// </summary>
public List<Subscription> SubscriptionsList { get; set; }

/// <summary>
/// Sorted list of subscriptions for LongListSelector
/// </summary>
public List<AlphaKeyGroup<Subscription>> Subscriptions
{
get
{
return AlphaKeyGroup<Subscription>.CreateGroups(this.SubscriptionsList,
System.Threading.Thread.CurrentThread.CurrentUICulture,
(Subscription s) => { return s.Title; }, true);
}
}

/// <summary>
/// Current Downloads
/// </summary>
public ObservableCollection<Episode> Downloads { get; private set; }

/// <summary>
/// True if downloads are pending
/// </summary>
public bool HasDownloads
{
get
{
return this.Downloads.Count > 0;
}
}

/// <summary>
/// Background transfers (maybe obsolete)
/// </summary>
private IEnumerable<BackgroundTransferRequest> transferRequests;

public MainViewModel()
{
this.Downloads = new ObservableCollection<Episode>();
this.UpdateDownloadList();

this.UpdateUI();
}

/// <summary>
/// Updates Downloads-Collection (see above)
/// </summary>
public void UpdateDownloadList()
{
if (this.transferRequests != null)
{
foreach (var request in this.transferRequests)
{
request.Dispose();
}
}

this.transferRequests = BackgroundTransferService.Requests;

this.Downloads.Clear();

foreach (var request in this.transferRequests)
{
var episode = (from Episode e in App.LocalDB.Episodes
where e.DownloadId == request.RequestId
select e).FirstOrDefault();

if (episode != null)
{
this.Downloads.Add(episode);

// Add event handlers
request.TransferStatusChanged += transfer_TransferStatusChanged;
request.TransferProgressChanged += transfer_TransferProgressChanged;

ProcessTransfer(request);

// Display progress
if (request.TotalBytesToReceive > 0)
{
episode.Progress = ((double)request.BytesReceived / request.TotalBytesToReceive) * 100;
}
}
}
}

/// <summary>
/// Handles a successful transfer
/// </summary>
/// <param name="transfer"></param>
private void ProcessTransfer(BackgroundTransferRequest transfer)
{
if (transfer.TransferStatus == TransferStatus.Completed)
{
if (transfer.StatusCode == 200 || transfer.StatusCode == 206)
{
var episode = (from Episode e in App.LocalDB.Episodes
where e.DownloadId == transfer.RequestId
select e).FirstOrDefault();

if (episode != null)
{
using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication())
{
string filename = transfer.Tag;
if (isoStore.FileExists(filename))
{
isoStore.DeleteFile(filename);
}

isoStore.MoveFile(transfer.DownloadLocation.OriginalString, episode.Location);
}
}

this.RemoveDownload(transfer);
}
}
}

/// <summary>
/// TransferProgressChanged-Event
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void transfer_TransferProgressChanged(object sender, BackgroundTransferEventArgs e)
{
var episode = (from Episode ep in App.LocalDB.Episodes
where ep.DownloadId == e.Request.RequestId
select ep).FirstOrDefault();

if (episode == null)
{
this.RemoveDownload(e.Request);
}
else
{
if (e.Request.TotalBytesToReceive > 0)
{
// Display progress
episode.Progress = ((double)e.Request.BytesReceived / e.Request.TotalBytesToReceive) * 100;
}
}
}

/// <summary>
/// TransferStatusChanged-Event
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void transfer_TransferStatusChanged(object sender, BackgroundTransferEventArgs e)
{
this.ProcessTransfer(e.Request);
}

/// <summary>
/// Downloads an episode
/// </summary>
/// <param name="e">Episode to download</param>
public void StartDownload(Episode e)
{
if (BackgroundTransferService.Requests.Count() >= 25)
{
e.InQueue = true;

App.LocalDB.SubmitChanges();

return;
}

Uri transferUri = new Uri(Uri.EscapeUriString(e.Link), UriKind.RelativeOrAbsolute);

BackgroundTransferRequest req = new BackgroundTransferRequest(transferUri);
req.Method = "GET";

string downloadFile = e.Link.Substring(e.Link.LastIndexOf("/") + 1);

using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication())
{
if (!isoStore.DirectoryExists("/shared/transfers/" + e.Subscription.ID.ToString()))
{
isoStore.CreateDirectory("/shared/transfers/" + e.Subscription.ID.ToString());
}

if (!isoStore.DirectoryExists("/shared/audio/" + e.Subscription.ID.ToString()))
{
isoStore.CreateDirectory("/shared/audio/" + e.Subscription.ID.ToString());
}
}

Uri downloadUri = new Uri("shared/transfers/" + e.Subscription.ID.ToString() + "/" + downloadFile, UriKind.RelativeOrAbsolute);

req.DownloadLocation = downloadUri;
req.Tag = downloadFile;

try
{
BackgroundTransferService.Add(req);
e.DownloadId = req.RequestId;
e.Location = "shared/audio/" + e.Subscription.ID.ToString() + "/" + downloadFile;

req.TransferProgressChanged += this.transfer_TransferProgressChanged;
req.TransferStatusChanged += this.transfer_TransferStatusChanged;

App.LocalDB.SubmitChanges();

this.Downloads.Add(e);
this.NotifyPropertyChanged("HasDownloads");
}
catch { }
}

/// <summary>
/// Removes a download from download list
/// </summary>
/// <param name="req">Request</param>
private void RemoveDownload(BackgroundTransferRequest req)
{
BackgroundTransferService.Remove(req);

var episode = (from Episode e in App.LocalDB.Episodes
where e.DownloadId == req.RequestId
select e).FirstOrDefault();

if (episode != null)
{
episode.IsDownloaded = true;
episode.DownloadId = string.Empty;

App.LocalDB.SubmitChanges();
this.Downloads.Remove(episode);
this.NotifyPropertyChanged("HasDownloads");
}

var next = (from Episode e in App.LocalDB.Episodes
where e.InQueue == true
select e).FirstOrDefault();

if (next != null)
{
next.InQueue = false;
App.LocalDB.SubmitChanges();

this.StartDownload(next);
}
}

/// <summary>
/// Cancels download of an episode
/// </summary>
/// <param name="e">Episode</param>
public void CancelDownload(Episode e)
{
var req = BackgroundTransferService.Find(e.DownloadId);

try
{
e.IsDownloaded = false;
e.Location = string.Empty;
e.DownloadId = string.Empty;

App.LocalDB.SubmitChanges();

this.RemoveDownload(req);
}
catch { }
}

/// <summary>
/// Updates UI of MainPage.xaml
/// </summary>
public void UpdateUI()
{
var subscriptions = from Subscription s in App.LocalDB.Subscriptions
select s;

this.SubscriptionsList = new List<Subscription>(subscriptions);

this.NotifyPropertyChanged("Subscriptions");
this.NotifyPropertyChanged("Downloads");
this.NotifyPropertyChanged("HasDownloads");
}
}


Den meisten Code habe ich mir von hier abgeguckt: http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh202959(v=vs.105).aspx

Sieht vielleicht jemand, wo hier die Performance der UI in die Knie geht?

LG Marcel

Update: Also entweder ich habe etwas beim BackgroundTransferService übersehen oder dessen Implementierung ist Schuld. In der Ausgabe in Visual Studio sieht man, wie in kürzester Zeit mehrere (viele!!) Thread beendet werden. Etwas rumprobieren ergab, dass diese Threads vom BackgroundTransferService gestartet werden.

Habe also entsprechend den Titel meiner Frage angepasst.

Hat evtl. jemand Erfahrung mit dem BackgroundTransferService in Zusammenhang mit mehreren Dateien?
27.08.2013
m.marnitz 216 4