Windows Phone Mango开发实践——后台文件传输

后台文件传输

后台文件传输仅支持HTTP和HTTPS的传输,不支持 FTP。通过使用 BackgroundTransferService 实现后台上传和下载文件。GET HTTP方法支持下载文件,POST方法支持下载或上传文件。传输方法是在 BackgroundTransferRequest 对象的Method 属性里设置的。

后台文件传输对于文件系统的限制是所有后台传输中下载的目标路径和上传的源路径都必须是本地文件路径。下载的目标路径指定保存下载的文件的位置,上传的源路径指定上传文件的位置。后台传输的所有本地路径都必须位于您应用程序的独立存储中,在名为"/shared/transfers"的根目录中。该目录是操作系统在安装应用程序时创建的,但如果应用程序删除或重命名该目录,则必须在启动任何文件传输之前重新创建该目录。您可以在"/shared/transfers"根目录下创建您选择的任何其他目录结构,并且可以在传输完成之后复制或删除文件。尝试使用"/shared/transfers"目录之外的路径启动传输将引发异常,所以接受该默认的下载目录即可,在下载完成后再将文件复制至其他目录。

  

后台传输的策略

  • 大小

上传最大文件大小

5MB

通过手机网络连接的最大下载大小

20MB——如果超过此限制,则传输的TransferPreferences属性将自动更改为AllowBattery,它对需要Wi-Fi的传输有影响。

通过Wi-Fi而没有外部电源的最大下载大小

100 MB——大于100 MB的文件必须将传输的TransferPreferences属性设置为None,否则传输将失败。如果不知道传输文件的大小,则其有可能会超出该限制,您应将值设置为None

  • 限制

每个应用程序的队列中未完成的最大请求数(这包括活动和挂起的请求)。

5——完成后不会自动从队列中删除传输。应用程序应该使用Remove(BackgroundTransferRequest)从队列中删除完成的传输,以便为新的传输留出队列空间。

设备上所有应用程序的最大并发传输数

2

设备上所有应用程序的最大排队传输数

500

每个请求的最大HTTP标头数

15

HTTP标头的最大大小

每个16KB

  • 策略
  • 在非并发语音和数据网络上不运行后台传输服务,其中包括:
    • 2GEDGE、标准 GPRS

    后台传输服务在3G以及更高版本的网络上运行。

  • 服务器端所需的文件内容长度标头大于5MB。服务器应该始终在响应中返回内容长度。不这样做可能会导致严重降低传输的性能。
  • 服务器端所需的文件范围标头大于 5MB。服务器应该始终支持范围请求标头。不这样做可能会导致严重降低传输的性能。
  • 慢速传输

如果设备的网络连接速度低于以下速率,则会暂停传输并重试。这些限制(单位为Kb/s)比较低,因此通常不会达到。

网络媒介

最低数据速率

3G

50 Kbps

Wi-Fi/USB

100 Kbps

后台传输程序的认证要求

后台音频的程序属于特定应用程序类型,除了一般应用程序的要求需满足外,还需符合如下的要求。

  • 该应用程序不得启动后台传输,除非用户激活该应用程序提供的可发UI元素。

验证方法:

    • 启动应用程序;
    • 验证该应用程序中是否存在可发现的 UI 元素,并允许后台传输;
    • 激活 UI 元素以开始后台传输;
    • 验证是否开始后台传输。
  • 当应用程序位于前台中时,该应用程序必须允许用户通过可发现的UI元素查看所有活动和挂起的后台传输状态。

    验证方法:

    • 启动应用程序;
    • 启动后台传输;
    • 验证是否存在可发现的UI元素,用于显示正在进行的传输;
    • 验证UI元素是否正确显示后台传输;
    • 激活用于停止后台传输的UI元素;
    • 验证确认进度UI元素不显示已停止的后台传输。
  • 该应用程序必须为用户提供可发现的UI元素,并允许用户取消活动或挂起的后台传输。

    验证方法:

    • 启动应用程序;
    • 启动后台传输;
    • 验证是否存在可发现的UI元素,并允许用户取消后台传输;
    • 激活UI元素以停止后台传输;
    • 验证确认后台传输停止并且不再显示在相关的UI元素中。

最佳实践

  • 应用程序启动
    • 使用BackgroundTransferService对象的Requests属性轮询应用程序的所有后台传输。
    • 对于每个请求,为TransferStatusChanged事件注册一个处理程序以便您的应用程序可以响应该应用程序正在运行时发生的状态更改。
    • 对于每个请求,为TransferProgressChanged事件注册一个处理程序。这对于通知用户有关活动传输进度的信息非常有用。
    • 对于每个请求,检查TransferStatus属性,以确定该应用程序处于非活动状态时任何传输的状态是否为已完成或已更改。
    • 使用前面提到的回调函数更新应用程序UI,而不是使用计时器或其他一些轮询服务更改的机制。
  • 添加新的传输请求
    • 使用BackgroundTransferService对象的Add(BackgroundTransferRequest)方法。
    • 首先,查看应用程序是否已达到每个应用程序5个并发传输的限制。如果已达到,则可以通知用户,提示用户等待现有传输完成或取消现有传输。也可以将有关新传输的信息存储在独立存储中,然后在将来的某些时间文件传输队列低于此限制时加载该信息并启动传输。
    • 将对Add的调用放置在一个try块中并捕获任何异常。尝试创建新的传输失败时,您应该向用户提供一个可使用的消息。
    • 使用"/shared/transfers"作为所有传输操作的本地根目录。对于文件下载,请将DownloadLocation属性设置为该目录中的某个文件名。这就是传输完成时文件所在的位置。对于文件上传,请将UploadLocation属性设置为该目录中的某个文件名以指定要上传的文件。
    • 使用RequestUri属性指定文件传输的远程服务器地址。后台传输服务使用UriOriginalString 属性。因此,应该使用Uri.EscapeUriString方法转义Uri中的任何特殊字符(如果Uri尚未转义)。
    • 如果要传输的文件大于100 MB,请将传输的TransferPreferences的属性设置为None,否则传输将失败。如果不知道传输文件的大小,则其有可能会超出该限制,也应将该属性设置为None
  • 传输完成
    • TransferStatu属性将拥有值Completed。
    • 通过检查包含从目标服务器返回的HTTP状态代码的StatusCode属性确定传输是否成功。如果传输成功,则该值将为200或206。建议这两个值都检查。其他任何状态代码都指示服务器错误。处理服务器错误的方式取决于您的应用程序。
    • 检查TransferError属性以帮助确定失败的传输未成功的原因。
    • 如果文件下载成功,您可能希望将该文件从"\shared\transfers"目录移动到独立存储中的某个新位置。
    • 通过调用BackgroundTransferService的Remove(BackgroundTransferRequest)方法从队列中删除传输请求。应用程序限制最多同时存在5个并发传输请求,包括挂起的、活动的以及已完成的请求。系统不会自动删除已完成的传输。如果您删除旧传输失败且超过了应用程序限制,则当您尝试添加新的传输时会引发异常。
  • UI设计的最佳实践
    • 后台文件传输应该由用户启动,如通过点按某个按钮。如果用户未启动传输,则应用程序应该通知用户该应用程序正在代表用户启动传输。
    • 您必须提供允许用户查看所有后台传输的状态和传输进度的 UI。
    • 必须提供用户取消任何以及所有当前传输的机制。
    • 使用将文件传输限制为仅通过 Wi-Fi 以及在连有外部电源时进行的默认传输首选项。提供可让用户选择在没有外部电源的情况下允许通过手机网络连接进行传输的UI。可以使用TransferPreferences属性设置用户的选择。
    • 如果后台传输的状态为WaitingForExternalPower、WaitingForExternalPowerDueToBatterySaverMode、WaitingForWiFi或WaitingForNonVoiceBlockingNetwork,则向用户提供一个消息,通知他们传输正在等待的原因,最好也通知他们恢复传输(例如连接到Wi-Fi网络)所需的步骤。
  • 其他建议
    • 在关联的传输操作完成之前,不要尝试访问或修改"/shared/transfers"目录中的目标文件。
    • BackgroundTransferRequest对象的 Tag 属性可用于将关联的自定义数据与某个传输关联。应用程序可以在创建传输请求时设置值。当使用Requests属性或Find(String)方法检索传输请求时,Tag属性将包含以前设置的数据。该属性的最大长度为 4000个字符,但建议您保持较小的数据大小以便提高性能。
    • TransferStatusChanged和TransferProgressChanged事件的事件处理程序应该快速返回以便用户界面不会变得行动迟缓。从独立存储中读取以及向独立存储中写入的速度可能很慢,如果可能的话,应该在单独的工作线程上执行。
    • TotalBytesToReceive属性指示文件下载的总大小。如果该值为-1,则表示文件大小未知。
    • 服务器应该在HTTP响应标头中返回内容长度,以便让用户知道传输的大小和进度。
    • 服务器必须支持来自客户端的Range请求以改进性能。后台传输有可能会在进行时暂停和恢复。不支持 Range 请求将导致性能降低,因为传输一旦暂停,必须重新启动。

动手实践——后台下载3D模型

本节使用的代码是通过使用组合Silverlight和XNA的项目模板。在Windows Phone OS 7.1中启动时,可将Silverlight和XNA内容合并到同一个应用程序或游戏。这样您可以使用Silverlight将多格式文本支持功能添加到您的XNA游戏,而且您还可以将包含XNA内容的页面添加到Silverlight应用程序。本节重点讲解如何通过后台下载功能实现远程下载XNA模型。

首先在Silverlight的MainPage.xaml中显示下载列表,使用Visual Studio 2010打开解决方案ModelViewer\ModelViewer.sln,其中MainPage.xaml在设计视图中显示内容如图14-1 MainPage。列表分为两个部分,本地模型列表和远程模型列表。本例中使用Windows Phone Mango提供的BackgroundTransferService类创建和管理下载功能。当用户在远程XNA模型列表中选择XNA模型,点击下载按钮后。应用程序将XNA模型下载至手机中,并将其从远程模型列表移动到本地模型列表。

图18-1 MainPage

后台传输服务的实现方法:创建后台传输请求BackgroundTransferRequest,添加至后台传输服务BackgroundTransferService队列。

BackgroundTransferRequest类需要添加引用Microsoft.Phone.BackgroundTransfer。

Silverlight/XNA Project: ModelViewer File: Downloads \Download.cs

using Microsoft.Phone.BackgroundTransfer;

Download类的Start方法实现创建后台传输服务请求。requestUri声明下载地址,downloadUri声明保存的地址。当应用程序将下载请求增加到后台下载服务BackgroundTransferService的队列中后,Windows Phone操作系统在后台任务重中执行下载。此时,即使应用程序不处于激活状态,下载过程仍在继续。

在本例中,应用程序监听TransferStatusChanged事件和TransferProgressChanged事件。TransferStatusChanged事件传递BackgroundTransferEventArgs对象,通过判断下载请求的TransferStatus属性。此时因为应用程序不再需要监听后台传输请求,所以就执行取消TransferStatusChanged事件的订阅操作。

Silverlight/XNA Project: ModelViewer File: Downloads \Download.cs

public void Start()
{
// 创建后台文件传输的请求
BackgroundTransferRequest request = new BackgroundTransferRequest(requestUri, downloadUri);
request.TransferPreferences = TransferPreferences.AllowCellularAndBattery;
requestId = request.RequestId;
// 订阅传输事件TransferStatusChanged和TransferProgressChanged
request.TransferStatusChanged += request_TransferStatusChanged;
request.TransferProgressChanged += request_TransferProgressChanged;

// 在后台传输服务中添加请求
BackgroundTransferService.Add(request);
}

private void request_TransferStatusChanged(object sender, BackgroundTransferEventArgs e)
{
BackgroundTransferRequest request = e.Request;
// 传输完成,取消订阅
if(request.TransferStatus == TransferStatus.Completed)
{
request.TransferStatusChanged -= request_TransferStatusChanged;
request.TransferProgressChanged -= request_TransferProgressChanged;

if(isAborted)
OnDownloadAborted();
else
OnDownloadFinished(request);
}
}

注意:
后台传输请求可能不会立即执行,在后台传输服务使用一个特殊的调度程序来管理下载请求。例如,当电池电量有限的情况下,操作系统可能会暂停传输,以减少能源消耗。
大于100 MB的文件必须将传输的TransferPreferences属性设置为None,否则传输将失败。如果不知道传输文件的大小,则其有可能会超出该限制,建议应将其值设置为None

当下载完成时request_TransferStatusChanged事件处理函数移动文件,通知应用程序从队列中删除的BackgroundTransferRequest。Windows Phone不会自动从队列中自动删除下载请求,必须由应用程序开发人员编写代码执行。OnDownloadAborted方法删除下载的临时文件,并且通知下载取消的事件。

Silverlight/XNA Project: ModelViewer File: Downloads \Download.cs

public event EventHandler DownloadFinished;
public event EventHandler DownloadAborted;
private void OnDownloadFinished(BackgroundTransferRequest request)
{
// 将完成下载的文件从"/shared/transfers"移至目标目录
Storage.MoveFile(downloadPath, targetPath);

// 更新本地模型列表和远程模型列表的UI
if(DownloadFinished != null)
DownloadFinished(this, EventArgs.Empty);

// 从BackgroundTransferService 移除已经完成下载任务的请求
BackgroundTransferService.Remove(request);
}

private void OnDownloadAborted()
{
// 删除临时下载文件,即"/shared/transfers"目录下的文件
Storage.DeleteFile(downloadPath);


// 取消正在下载的任务

if(DownloadAborted != null)
DownloadAborted(this, EventArgs.Empty);
}

为实现在用户界面的蓝色进度条的显示,需响应传输请求BackgroundTransferRequest的传输进度TransferProgressChanged事件。

Silverlight/XNA Project: ModelViewer File: Downloads \Download.cs

public event EventHandler<DownloadProgressEventArgs> DownloadProgress;
private void request_TransferProgressChanged(object sender, BackgroundTransferEventArgs e)
{
BackgroundTransferRequest request = e.Request;
// 只要传输还在继续则更新进度条
if(request.TransferStatus == TransferStatus.Transferring)
{
// 更新进度
if(DownloadProgress != null)
DownloadProgress(this, new DownloadProgressEventArgs(request.BytesReceived, request.TotalBytesToReceive));
}
}

图18-2 后台传输进度条

注意:
当应用程序处于休眠状态或者停止运行,后台传输服务都会持续执行。在此期间,后台传输服务将所有的过程和状态事件存放在队列中,当应用程序重新激活时将接收到所有的相关更新事件。

此应用程序允许用户中止正在进行的下载。当下载开始,远程模型列表中的下载按钮显示"X",表示停止下载。用户点击该按钮,ModelMetadata类将调用Download类的Abort方法停止下载。

在MainPage.xaml中远程模型列表ListBox,绑定SelectionChanged事件处理函数和Item模板ModelDT。

Silverlight/XNA Project: ModelViewer File: MainPage.xaml

<ListBox x:Name="lstLocalModels" ItemsSource="{Binding LocalModels}"
SelectionChanged="lstLocalModels_SelectionChanged" ItemTemplate="{StaticResource ModelDT}" Margin="0" Grid.Row="1" BorderBrush="Red" />

在MainPage.xaml中定义列表项的数据模板,数据模板中的包含绑定Name和Description的TextBlock,以及下载进度条ProgressBar。

Silverlight/XNA Project: ModelViewer File: MainPage.xaml

<DataTemplate x:Key="ModelDT">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="280" />
<ColumnDefinition Width="60" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" Margin="0,0,0,2" VerticalAlignment="Top">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" MinHeight="7" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Name}" Grid.Row="0" FontSize="{StaticResource PhoneFontSizeLarge}" Margin="12,-2,12,0" HorizontalAlignment="Left" VerticalAlignment="Top" />
<TextBlock Text="{Binding Description}" Margin="12,-5,12,0" HorizontalAlignment="Left" Grid.Row="1" VerticalAlignment="Top" />
<ProgressBar Minimum="0" Maximum="100"
Value="{Binding DownloadProgress}"
Visibility="{Binding IsInProgress, Converter={StaticResource BoolToVisibilityConverter}}" Margin="0" Grid.Row="2" MinHeight="5" VerticalAlignment="Top" Width="468"
/>
</Grid>
<Button
Grid.Column="1"
Margin="0"
Padding="0"
Click="abortButton_Click"
BorderThickness="0"
Foreground="{StaticResource PhoneAccentBrush}"
Width="60"
Height="60"
Visibility="{Binding IsInProgress, Converter={StaticResource BoolToVisibilityConverter}}"
>
<Border
Height="30"
BorderBrush="{StaticResource PhoneAccentBrush}"
BorderThickness="2"
CornerRadius="15"
Padding="10,2,10,4" >
<TextBlock
Text="X"
FontSize="{StaticResource PhoneFontSizeSmall}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
</Border>
</Button>
</Grid>
</DataTemplate>

Download类的Abort方法实现取消下载的功能,Abort方法调用后台传输服务检索定制requestId的后台传输服务请求,并从后台传输服务的队列中删除请求。

Silverlight/XNA Project: ModelViewer File: Downloads \Download.cs

public bool Abort()
{
BackgroundTransferRequest request = BackgroundTransferService.Find(requestId);

if(request != null)
{
isAborted = true;
BackgroundTransferService.Remove(request);
}

return request != null;
}

注意:
后台传输服务API只提供应用程序访问自己的传输服务请求队列,不能访问其他的应用程序的传输请求队列。

当后台传输服务请求BackgroundTransferRequest从队列中删除时,后台传输服务BackgroundTransferRequest触发TransferStatusChanged,并将传输状态属性设置为TransferStatus.Completed。由于下载完成的传输状态属性也为TransferStatus.Completed,所以在Download类的Abort方法中设置isAborted为True。以此来区分下载取消和下载完成。

Silverlight/XNA Project: ModelViewer File: Downloads \Download.cs

private void request_TransferStatusChanged(object sender, BackgroundTransferEventArgs e)
{
BackgroundTransferRequest request = e.Request;

if(request.TransferStatus == TransferStatus.Completed)
{
request.TransferStatusChanged -= request_TransferStatusChanged;
request.TransferProgressChanged -= request_TransferProgressChanged;

if(isAborted)
OnDownloadAborted();
else
OnDownloadFinished(request);
}
}

Windows Phone Mango限制后台传输请求队列大小为5项,向队列中添加更多的请求会导致应用程序异常。

DownloadManager类管理下载队列,DownloadManagerStartDownloadProcessPendingDownloads方法演示如何使用队列。

Silverlight/XNA Project: ModelViewer File: Downloads \DownloadManager.cs

private static readonly Collection<Download> downloads = new Collection<Download>();
private static readonly Queue<Download> pendingDownloads = new Queue<Download>();

public static void StartDownload(Download download)
{
if(BackgroundTransferService.Requests.Count() < 5)
{
download.DownloadFinished += download_DownloadFinished;
download.DownloadAborted += download_DownloadAborted;

download.Start();
}
else
{
pendingDownloads.Enqueue(download);
}
}
private static void ProcessPendingDownloads()
{
if(pendingDownloads.Count > 0)
{
Download download = pendingDownloads.Dequeue();
StartDownload(download);
}
}

当应用程序请求的下载数量达到极限时,下载请求将保存在pendingDownloads队列而不是BackgroundTransferService队列。本例中pendingDownloads队列在应用程序逻辑删除或者关闭时将被删除。

原文地址:https://www.cnblogs.com/xuesong/p/2680233.html