3

I am writing a universal app primarily targeting Windows Phone using SQLite-net.

During the course of operation, the user is presented with an option to download multiple files. At the end of each file download, I need to mark the file in the db as completed. I am using BackgroundDownloader in order to download files - the WP8.0 app used Background Transfer Service and that worked great. Files can be huge (some 200+ mbs, user content) and i am not looking forward to wrapping the downloads in the HttpClient or WebClient.

However, it seems that the progress callback doesn't work with awaits unless I actually breakpoint in the method.

The following are listings from a sample app i quickly put together that demonstrates the behaviour:

Model:

   public class Field
  {
    [PrimaryKey]
    [AutoIncrement]
    public int Id { get; set; }

    public bool Done { get; set; }
  }

MainPage codebehind (i am creating a db here only for the purposes of this example!):

 private async void Button_Click(object sender, RoutedEventArgs e)
{
  using (var db = new SQLiteConnection(Windows.Storage.ApplicationData.Current.LocalFolder.Path + "//Main.db"))
  {
    db.CreateTable<Field>();
    db.Commit();
  }

  this.DbConnection = new SQLiteAsyncConnection(Windows.Storage.ApplicationData.Current.LocalFolder.Path + "//My.db");
  var dl = new BackgroundDownloader();
  dl.CostPolicy = BackgroundTransferCostPolicy.Always;
  var transferUri = new Uri("http://192.168.1.4/hello.world", UriKind.Absolute);
  var folder = await ApplicationData.Current.LocalFolder.CreateFolderAsync(
    "Content",
    CreationCollisionOption.OpenIfExists);

  var localFile = await folder.CreateFileAsync("cheesecakes.file", CreationCollisionOption.ReplaceExisting);
  var d = dl.CreateDownload(transferUri, localFile);
  d.Priority = BackgroundTransferPriority.High;

  var progressCallback = new Progress<DownloadOperation>(this.DownloadProgress);

  await d.StartAsync().AsTask(progressCallback);
}

private async void DownloadProgress(DownloadOperation download)
{
  Debug.WriteLine("Callback");
  if (download.Progress.Status == BackgroundTransferStatus.Completed)
  {
        var f = new Field();
        f.Done = true;
        await this.DbConnection.InsertAsync(f);
        Debug.WriteLine("DONE");
   }
}

If i breakpoint inside the DownloadProgress and then press F5 i get both Debug messages, and my db gets a new record.

However, if i just let the code execute, i never see "DONE" printed to me and neither is my db updated.

I tried wrapping the code in a new task:

await Task.Run(
      async () =>
      {
        Debug.WriteLine("taskrun");
       .... OTHER CODE FROM ABOVE...
      });

But again, i only get to see 'taskrun' if i breakpoint in the callback.

UPDATE I actually think this is more related to checking the status. E.g. the statements outside of the check are executed, but only once, whereas anything inside the check is not executed.

Is there any way to force that callback to be invoked when the download is completed?

zaitsman
  • 8,984
  • 6
  • 47
  • 79

2 Answers2

2
private async void DownloadProgress(DownloadOperation download)
{
    Debug.WriteLine("Callback");

    var value = download.Progress.BytesReceived * 100 download.Progress.TotalBytesToReceive;
    new System.Threading.ManualResetEvent(false).WaitOne(1000);
    if (download.Progress.Status == BackgroundTransferStatus.Completed )
    {
        var f = new Field();
        f.Done = true;
        await this.DbConnection.InsertAsync(f);
        Debug.WriteLine("DONE");
    }
}

I had this problem too, and I solved this by sleeping for 1000 ms, which worked really well for me.

Nic
  • 12,220
  • 20
  • 77
  • 105
John Albert
  • 69
  • 1
  • 6
1

Not sure what is causing this, but I was able to get the sample app to work reliably by manually checking the bytes to download as opposed to relying on the DownloadOperation.Progress.Status:

 private async void DownloadProgress(DownloadOperation download)
{
  Debug.WriteLine("Callback");

  var value = download.Progress.BytesReceived * 100 / download.Progress.TotalBytesToReceive;

  if (download.Progress.Status == BackgroundTransferStatus.Completed || value >= 100)
  {
    var f = new Field();
    f.Done = true;
    await this.DbConnection.InsertAsync(f);
    Debug.WriteLine("DONE");
  }

This gets me to 'DONE' every time.

zaitsman
  • 8,984
  • 6
  • 47
  • 79
  • I've had to use this same approach in my app. Completed never seems to get called on it's own! Glad I'm not alone! – ozziepeeps Aug 02 '14 at 20:18
  • completed is not done but problem is GetCurrentDownloadsAsync returns previous downloads as well please see at http://stackoverflow.com/questions/27894341/backgrounddownloader-getcurrentdownloadsasync-returns-completed-downloads – Teoman shipahi Jan 12 '15 at 02:54