如何解决DownloadOperation的AttachAsync不会立即返回?

时间:2012-12-19 00:58:56

标签: windows-runtime windows-store-apps

当使用Background Transfer API时,我们必须迭代当前数据传输以在应用程序重新启动(即系统关闭)后重新启动它们。要获取进度信息并能够取消数据传输,必须使用AttachAsync附加它们。

我的问题是AttachAsync仅在数据传输完成时返回。这在某些情况下是有道理的。但是,当进行多次数据传输时,列表中的下一次传输将不会在当前连接完成之前启动。我对这个问题的解决方案是处理AttachAsync()的任务.AsTask()以经典方式返回(不使用等待但是继续):

IReadOnlyList<DownloadOperation> currentDownloads =
   await BackgroundDownloader.GetCurrentDownloadsAsync();
foreach (var downloadOperation in currentDownloads)
{
   Task task = downloadOperation.AttachAsync().AsTask();

   DownloadOperation operation = downloadOperation;
   task.ContinueWith(_ =>
   {
      // Handle success
      ...
   }, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion,
   TaskScheduler.FromCurrentSynchronizationContext());

   task.ContinueWith(_ =>
   {
      // Handle cancellation
      ...
  }, CancellationToken.None, TaskContinuationOptions.OnlyOnCanceled,
  TaskScheduler.FromCurrentSynchronizationContext());

  task.ContinueWith(t =>
  {
     // Handle errors
      ...

  }, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted,
  TaskScheduler.FromCurrentSynchronizationContext());
}

它有点工作(在实际代码中我将下载添加到ListBox)。循环遍历所有下载并执行StartAsync。但下载并非真正同时启动。只有一个一次运行,只有当它完成后,下一个继续运行。

针对此问题的任何解决方案?

2 个答案:

答案 0 :(得分:3)

Task的重点是允许您选择并行操作。如果你await那么你告诉代码序列化操作;如果你没有等待,那么你告诉代码要并行化。

您可以做的是将每个下载任务添加到列表中,告诉代码并行化。然后,您可以逐个等待任务完成。

如下:

IReadOnlyList<DownloadOperation> currentDownloads = 
    await BackgroundDownloader.GetCurrentDownloadsAsync();
if (currentDownloads.Count > 0)
{
    List<Task<DownloadOperation>> tasks = new List<Task<DownloadOperation>>();
    foreach (DownloadOperation downloadOperation in currentDownloads)
    {
        // Attach progress and completion handlers without waiting for completion
        tasks.Add(downloadOperation.AttachAsync().AsTask());
    }

    while (tasks.Count > 0)
    {
        // wait for ANY download task to finish
        Task<DownloadOperation> task = await Task.WhenAny<DownloadOperation>(tasks);
        tasks.Remove(task);

        // process the completed task...
        if (task.IsCanceled)
        {
            // handle cancel
        }
        else if (task.IsFaulted)
        {
            // handle exception
        }
        else if (task.IsCompleted)
        {
            DownloadOperation dl = task.Result;
            // handle completion (e.g. add to your listbox)
        }
        else
        {
            // should never get here....
        }
    }
}

答案 1 :(得分:0)

我希望这还不算太晚,但我完全知道您在说什么。我还尝试在应用程序启动时恢复所有下载。

经过数小时的尝试,这是可行的解决方案。

诀窍是让下载操作首先恢复,然后再攻击进度处理程序。

<c:if test="${product.categoryBean.category_name == 'Women'}">                                            
     <div class="catHeading">
         <h2>${product.categoryBean.category_name}</h2>                                             
         <span class="catHeadingViewAll">
         <a href="<c:url value = "product-details/${fn:replace(product.categoryBean.category_name,' ', '')}?subcategory_id=${product.categoryBean.category_id}&caname=${product.categoryBean.category_name}&offset=0"/>">View ALL</a>
         </span>                                            
         <c:forEach items="${product.subCategoryList}" var="sub">  
             <span class="subCatName"><a href="<c:url value = "product-detail/${fn:replace(sub.subcategory_name,' ', '')}?subcategory_id=${sub.subcategory_id}&caname=${sub.subcategory_name}&offset=0"/>">${sub.subcategory_name}</a></span>                                 
         </c:forEach> 
     </div>
</c:if>
<c:choose>
     <c:when test="${product.categoryBean.category_name == 'Women'}">

     </c:when>
     <c:otherwise>                                                
          <div class="catHeading">
             <h2>${product.categoryBean.category_name}</h2>                                             
             <span class="catHeadingViewAll">
             <a href="<c:url value = "product-details/${fn:replace(product.categoryBean.category_name,' ', '')}?subcategory_id=${product.categoryBean.category_id}&caname=${product.categoryBean.category_name}&offset=0"/>">View ALL</a>
             </span>                                            
             <c:forEach items="${product.subCategoryList}" var="sub">  
                  <span class="subCatName"><a href="<c:url value = "product-detail/${fn:replace(sub.subcategory_name,' ', '')}?subcategory_id=${sub.subcategory_id}&caname=${sub.subcategory_name}&offset=0"/>">${sub.subcategory_name}</a></span>                                 
             </c:forEach> 
       </div>