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添加後臺文件下載和上傳到您的應用程序上,同時後臺文件傳輸可用於創建備份/恢復您的應用程序所需的功能。