Hands-On Lab
在您的应用上使用后台传输服务
Lab version: 1.0.0
Last updated: 2/29/2012
内容
当你开发版本号为Mango的Windows Phone应用程序时,您可能需要传输文件到应用程序上。同时您的应用程序可能需要这些文件才能正常工作,也有一些情况下可以异步下载这文件,当传输完成时通知应用程序。像这样包括下载较大的文件(音乐和视频文件),上传一些较大的报告到服务器上或者备份应用程序的数据库。
在版本号为Mango的Windows Phone 手机中,新增的功能中就包括能够在后台执行文件传输任务,并且得到有关传输的进度通知。此功能的一个重要组成部分是后台程序保持激活状态,即使应用程序处于休眠状态,甚至是终止。
本实验演示了如何使用版本为Mango的Windows Phone的API添加后台传输服务到您的应用程序中,并演示了如何处理后台传输服务的请求。
目标
本实验将帮助您完成下列目标:
· 添加后台传输服务(上传和下载)到您的Windows Phone 手机应用程序中。
· 了解后台传输服务的工作原理和管理您的应用程序请求
前提条件
您在开始本次动手实验前,请先确认达到下列前提条件:
· MicrosoftVisual Studio 2010或者Microsoft Visual C# Express 2010,和Windows® Phone 7 Codenamed “Mango” DeveloperTools,下载地址:
http://go.microsoft.com/?linkid=9772716
· 您应该具备上一个实验的Windows® Phone 7应用程序开发知识。
实验提纲
这个动手实验将包括一个完整的实验,包括下列任务:
1. 创建一个同步服务的应用程序和执行数据库的备份/恢复的逻辑。
2. 实现在用户界面中备份或者恢复数据库
预计完成时间
完成该实验预计花费30-45分钟的时间
这个实验是基于 Tidy 应用程序。Tidy应用程序允许用户管理项目中包含的任务,每个任务有一个时间限制。用户从设置屏幕上备份和恢复应用程序的数据库。我们开始这个实验时首先需要拷贝备份恢复功能已移除的应用程序。这个版本可以在实验安装文件夹Sources\Begin中找到。 实验结束后,功能上相同,您最终会与Sources\End文件夹中的代码一致。
任务 1 –添加后台传输支持
虽然调用后台传输服务的API在Windows PhoneMango并不复杂,但它在应用程序中使用还是比较繁琐的。在这个任务中,我们添加一个类,它处理应用程序的上传与下载任务。这些方法将演示如何在执行备份和恢复时使用Microsoft.Phone.BackgroundTransfer.BackgroundTransferRequest和Microsoft.Phone.BackgroundTransfer.BackgroundTransferService 类。
1. 打开Source\Begin工程文件夹中的Todo.sln解决方案文件。
提示: 该应用程序使用本机的REST WCF服务上传/下载文件,并模拟真实世界的备份过程。该项目位于FileUploaderWcfRestService目录下并添加这个功能到解决方案中,为了与本地主机的WCF服务,你还需要启动VisualStudio 2010中的管理权限。此外要想成功使用数据库SDF文件(在Windows Phone Mango中使用的数据库是SQL CE),您需要添加新的MIME类型到您的IIS中:
扩展名(Extension): .sdf
MIME类型: application/octet-stream
添加所有IIS站点或虚拟目录(默认名称FileUploaderWcfRestService)到本次实验所建的WCF REST service项目中。
2. 在解决方案中找到SERVER_FileUploader文件夹并找到FileUploaderWcfRestService项目。查看IUploaderService.cs 类和UploaderService.cs类。我们将在下面的步骤中使用这两个类中的上传/下载的功能。
3. 导航到Todo.Business项目,在扩展接口文件夹中找到ISyncProvider.cs文件,此文件定义ISyncProvide的接口,我们将使用它来创建我们的同步服务功能。
C#
public interface ISyncProvider
{
string Name { get; }
void Upload(string username, string password, string filename);
void Download(string username,string password, string filename);
}
4. 还是在Todo.Business项目中,找到Misc文件夹下得SyncServices.cs文件,为了更新应用程序上传/下载进度的情况,我们添加两个事件参数(在Todo.Misc命名空间下已经定义)-在上传或者下载的过程中我们将使用进度信息来通知应用程序的UI:
C#
public class DownloadUploadFinishedEventArgs : EventArgs
{
public DownloadUploadFinishedEventArgs()
{
}
}
public class DownloadUploadProgressEventArgs : EventArgs
{
private long progress, total;
private bool isUpload;
public DownloadUploadProgressEventArgs(bool isUpload, long progress, long total)
{
this.progress = progress;
this.total = total;
this.isUpload = isUpload;
}
public long Progress
{
get { return progress; }
}
public long Total
{
get { return total; }
}
public bool IsUpload
{
get { return isUpload; }
}
}
5. 在Todo.Business项目中的Misc文件夹下创建一个新类,并以LocalhostSync命名,并实现ISyncProvider接口:
C#
public class LocalhostSync : ISyncProvider
{
#region ISyncProvider Members
public string Name
{
get { return LocalizedStrings.LocalhostSyncName; }
}
public void Upload(string username, string password,string filename)
{
// ...
}
public void Download(string username, string password,string filename)
{
// ...
}
#endregion
}
C#
const stringserviceUploadLocationURL = "http://localhost/FileUploaderWcfRestService/UploaderService/File/";
const stringdownloadLocationURL = "http://localhost/FileUploaderWcfRestService/Backups/";
const stringTransfersFiles = "shared\transfers";
const stringdownloadedDBName = "ToDo_NEW.sdf";
const stringlocalDBName = "ToDo.sdf";
注意,如果您决定要改变主机RESTWCF的URL,同时你必须改变上传和下载位置的URL.
7. 让我们实现上传功能:
C#
public voidUpload(string username, string password, string filename)
{
// Make sure there is a database to upload and create a copy ofit
using (IsolatedStorageFile iso =
IsolatedStorageFile.GetUserStoreForApplication())
{
if (!iso.FileExists(localDBName))
return;
iso.CopyFile(localDBName,"/" + TransfersFiles + "/" + localDBName,true);
}
// Queue a background transfer to upload the database copy
BackgroundTransferRequest btr = newBackgroundTransferRequest (new Uri
(serviceUploadLocationURL + localDBName,UriKind.Absolute));
btr.TransferPreferences= TransferPreferences.AllowBattery;
btr.Method= "POST";
btr.UploadLocation= new Uri("/" + TransfersFiles + "/" + localDBName, UriKind.Relative);
btr.TransferStatusChanged+= new EventHandler<BackgroundTransferEventArgs>(
btr_UploadTransferStatusChanged);
btr.TransferProgressChanged+= new EventHandler<BackgroundTransferEventArgs>(
btr_TransferProgressChanged);
Microsoft.Phone.BackgroundTransfer.BackgroundTransferService.Add(btr);
}
请参阅如何创建BackgroundTransferRequest实例来保证progress事件正常运行。
8. 将下面事件的代码加入到类中:
C#
public delegate void DownloadFinishedEventHandler(object sender, DownloadUploadFinishedEventArgs e);
public delegate void DownloadProgressEventHandler(object sender, DownloadUploadProgressEventArgs e);
public event DownloadFinishedEventHandler DownloadFinished;
public event DownloadFinishedEventHandler UploadFinished;
public event DownloadProgressEventHandler DownloadUploadProgress;
9. 在步骤7中添加指定的事件处理程序:
C#
void btr_TransferProgressChanged(object sender, BackgroundTransferEventArgse)
{
// If there are bytes to send this is an upload operation
bool isUploading = e.Request.TotalBytesToSend > 0 ? true : false;
if (null != DownloadUploadProgress)
DownloadUploadProgress(this new DownloadUploadProgressEventArgs(isUploading,
isUploading ?
e.Request.BytesSent : e.Request.BytesReceived,
isUploading ?
e.Request.TotalBytesToSend : e.Request.TotalBytesToReceive));
}
void btr_UploadTransferStatusChanged(object sender, BackgroundTransferEventArgse)
{
if (e.Request.TransferStatus == TransferStatus.Completed)
{
using (IsolatedStorageFile iso =
IsolatedStorageFile.GetUserStoreForApplication())
{
if (iso.FileExists(e.Request.UploadLocation.OriginalString))
iso.DeleteFile(e.Request.UploadLocation.OriginalString);
}
BackgroundTransferService.Remove(e.Request);
if (null != e.Request.TransferError)
{
// LOG the error hereif relevant for the application
}
else
{
if (null != UploadFinished)
UploadFinished(this, new DownloadUploadFinishedEventArgs());
}
}
}
btr_TransferProgressChanged 处理程序确定它是否处理上传或下载操作,然后出发相应的DownloadUploadProgress事件。当处理下载操作时,我们将重新使用它。
btr_UploadTransferStatusChanged处理程序是具体的上传请求。它通过执行以下方式来完成上传操作:
a. 从“transfers”location中删除临时文件。
b. 移除从BackgroundTransferService请求队列中完成的请求。
c. 通知其他应用程序上传完成(Notifying the rest of theapplication about completing upload.)。
10. 让我们处理下载文件。添加下面代码到下载功能函数中:
C#
public voidDownload(string username, string password, stringfilename)
{
BackgroundTransferRequest btr =
new BackgroundTransferRequest(
new Uri(downloadLocationURL + localDBName,UriKind.Absolute),
new Uri(TransfersFiles + @"\" + downloadedDBName,
UriKind.Relative));
btr.TransferPreferences = TransferPreferences.AllowBattery;
btr.TransferStatusChanged +=
new EventHandler<BackgroundTransferEventArgs>(
btr_DownloadTransferStatusChanged);
btr.TransferProgressChanged +=
new EventHandler<BackgroundTransferEventArgs>(
btr_TransferProgressChanged);
BackgroundTransferService.Add(btr);
}
在我们实施之前,这个有点类似上传的方法。值得注意的是在这个时候我们如何使用不同的事件处理程序来控制状态的改变。
11. 最后,让我们添加下载完成处理程序:
C#
void btr_DownloadTransferStatusChanged(object sender,
BackgroundTransferEventArgs e)
{
if (e.Request.TransferStatus == TransferStatus.Completed)
{
BackgroundTransferService.Remove(e.Request);
if (null != e.Request.TransferError)
{
//LOG the error hereif relevant for the application
}
else
{
//Overwrite the DB
using (IsolatedStorageFile iso =
IsolatedStorageFile.GetUserStoreForApplication())
{
iso.CopyFile("/" + TransfersFiles + "/" + downloadedDBName,
"/" + localDBName, true);
iso.DeleteFile("/" + TransfersFiles + "/" + downloadedDBName);
if (null != DownloadFinished)
DownloadFinished(this, new DownloadUploadFinishedEventArgs());
}
}
}
}
至于在btr_UploadTransferStatusChanged处理程序中,我们从请求队列中删除完成的请求,并触发一个事件,这个事件标志着下载已经完成,但同时我们也要确保所下载的来取代当前数据库中的数据。
12. 这样就完成了任务。在接下来的任务中,我们将绘制UI。
任务 2 – 用户界面中数据库备份/恢复的布局
在前面的任务重,我们已经奠定了数据库中备份/恢复中上传和下载数据库文件的基础,在此任务中,我们将改变应用程序的接口使数据库可以备份和恢复。
1. 编译后运行应用程序。导航到“设置”屏幕中(通过应用程序栏来设置菜单项):
Figure 1
在设置页面中有 Backup/Restore 按钮
上面的屏幕,可以使用在前面的任务中创建的处理程序来启动备份和恢复。此时,按钮不起作用。
2. 我们开始更新设置后的ViewModel,这将使我们控制应用程序的设置。在TODO项目中,打开ViewModels文件夹,打开SettingsViewModel.cs文件,添加下面的字段到SettingsViewModel中:
C#
private LocalhostSync syncProvider;
这将用于保持数据库的备份和恢复同步。
3. 添加以下属性和字段到SettingsViewModel类中:
C#
private boolsyncInProgress;
/// <summary>
/// Whether a sync operationis currently in progress or not.
/// </summary>
public boolSyncInProgress
{
get
{
return syncInProgress;
}
set
{
if (value != syncInProgress)
{
syncInProgress = value;
NotifyPropertyChanged("SyncInProgress");
}
}
}
private longsyncTotal;
/// <summary>
/// The total size of thecurrent sync operation. This is meaningless if an
/// operation is not currently in progress.
/// </summary>
public longSyncTotal
{
get
{
return syncTotal;
}
set
{
if (value != syncTotal)
{
syncTotal = value;
NotifyPropertyChanged("SyncTotal");
}
}
}
private longsyncProgress;
/// <summary>
/// The current progress ofthe current sync operation. This is meaningless
/// if an operation is not currently in progress.
/// </summary>
public longSyncProgress
{
get
{
return syncProgress;
}
set
{
if (value != syncProgress)
{
syncProgress = value;
NotifyPropertyChanged("SyncProgress");
}
}
}
我们将使用这些属性来显示所有备份/恢复操作的进度,以及在用户界面中防止用户同时启动多个操作。
4. 修改类的构造函数,以便它看起来像一下代码片段(新代码以黄色突出显示):
C#
public SettingsViewModel()
{
syncProvider =new LocalhostSync();
syncProvider.DownloadFinished += DownloadFinished;
syncProvider.UploadFinished += UploadFinished;
syncProvider.DownloadUploadProgress += OperationProgress;
periodicTask =ScheduledActionService.Find(PeriodicTaskName) as PeriodicTask;
if (periodicTask!= null)
IsBackgroundProcessingAllowed = periodicTask.IsEnabled;
else
IsBackgroundProcessingAllowed = true; ;
LoadSettings();
}
我们更新构造函数来初始化同步provider并注册其事件,这可以让我们根据同步provider的状态来更新ViewMode的属性。
private void OperationProgress(object sender, DownloadUploadProgressEventArgs e)
{
SyncTotal = e.Total;
SyncProgress = e.Progress;
}
private void UploadFinished(object sender, DownloadUploadFinishedEventArgs e)
{
SyncInProgress = false;
}
private void DownloadFinished(object sender, DownloadUploadFinishedEventArgs e)
{
MessageBox.Show(ApplicationStrings.Msg_RestoreWarning);
SyncInProgress = false;
}
6. 添加下面两种方法到类中:
C#
public voidUploadDatabase()
{
SyncProgress= 0;
SyncInProgress= true;
syncProvider.Upload("x", "x","x");
}
public voidDownloadDatabase()
{
SyncProgress= 0;
SyncInProgress= true;
syncProvider.Download("x", "x","x");
}
这些方法使用同步provider备份和恢复数据库。因为我们的provider不需要credentials。we provide itwith nonsensical ones。
7. 使用下列代码来更新OnBackup和Onrestore方法:
C#
void OnBackup(object param)
{
UploadDatabase();
}
void Onrestore(object param)
{
DownloadDatabase();
}
8. 编译并运行应用程序。您现在应该能够备份和恢复应用程序的数据库了。
该实验向您展示了如何使用Windows Phone Mango APIS添加后台文件下载和上传到您的应用程序上,同时后台文件传输可用于创建备份/恢复您的应用程序所需的功能。