WP7 - Background File Transfer

Windows Phone 7 – Background File Transfer

學了一陣子關於Background Agent的運用後,感覺WP7在Background上的處理,讓開發人員學習非常容易,

但有些觀念可能需要自己多花點時間來看,以免在送審程式時花了不少的時間成本。

今天要介紹的是Background File Transfer,這是WP7.1 SDK提供的另一個Backgrund Agent,適合使用的情境

可想而知:運用於傳輸大量檔案、背景與Server端交換資訊等。因此,往下將針對重點部分一一說明:

 

Microsoft.Phone.BackgroundTransfer

    WP7.1 SDK提供應用程式佇列一至多個檔案進行上傳/下載,並且讓任務可在背景環境下繼續執行,甚至是該

應用程式已長時間不在前景執行,另外該API也提供擷取、查詢目前file transfer運行的狀態,讓end user可以了

解目前的存取狀態。這些功能在Microsoft.Phone.BackgroundTransfer裡都能找到,接著往下介紹二個重要元件:

 

A. BackgroundTransferRequest與BackgroundTransferService

    A-1. BackgroundTransferRequest

            該物件代表一個transfer request,其內容包括:目標、檔案路徑、傳輸方法與現在的傳輸狀態。

            另外,當transfer request下載完成觸發completed事件時,記得呼叫background transfer service將這個

            transfer request進行remove,因為系統必不會自動把它移去

            重點屬性說明:

Name Description
DownloadLocation 取得/設定request檔案下載後要儲存的local path。可搭配建構子一起設定。
Headers 取得request的HTTP headers集合(Dictionary)。
Method 取得/設定request使用的HTTP Method。
TransferPreferences 取得/設定何種條件下transfer可以被使用,屬於列舉值:TransferPreferences Enumeration。可參考[補充]的說明。
TransferStatus 取得transfer的狀態。可配合BytesReceivedBytesSent擷取進度時使用。
TransferError 如果transfer執行了completed,TransferError通常是null。相反的,不為null則代表有錯誤發生。

           另外,二個重要的事件:TransferProgressChangedTransferStatusChanged留到範例來說明。

 

    A-2. BackgroundTransferService

            該物件用於初始化一個新的transfer、查詢或管理已存在的file transfers。其任務為控制所有的transfer物件,

            然而,BackgroundTransferRequest.Method僅支援HTTP與HTTPS,尚不支援FTP的協定,所以目前可以透過

            GET/POST上傳或下載檔案。

            重點屬性說明:

Name Description
Requests 取得向BackgroundTransferServicec中所有啟動的transfer requests。其角色與ScheduledActionService中的GetActions很相似。
Add 增加background transfer request至佇列中。
Find 透過指定的ID(RequestId)去企圖取回特定的background transfer reqeust。
Remove 透過指定的ID(RequestId)去企圖移除特定的background transfer reqeust。

           了解了BackgroundTransferService的角色之後,要特別注意的是:

           a-2-1. 每一個應用程式只能有5個request在給定的時間內

           a-2.2. 企圖增加超過5個request時,系統會自動發出Exception。

           a-2-3. 如果要移除佇列中的request,請透過Remove的方法,該方法移除成功後會觸發Completed的事件。

 

C. Background Transfer Policies (Background Transfer基本守則)

    C-1. File System Restrictions(檔案路徑使用條件)

           所有的background trasnfers都需要有一個 local file path(實際本機路徑)。下載檔案需要指定要儲存的實際位置;

           上傳檔案需指定要從那一個實際位置將檔案上傳,因此,所有的background tansfers使用的檔案路徑,必須存在於

            isolated storage的固定路徑:「/shared/transfers」之中該資料表是在程式被安裝至設備後,自動產生出來的

           如果自己手動刪除或更名了,必須再重建一個相同名稱的資料夾,才能初始化所有的transfers物件

           另外,可在/shared/transfers資料夾下建構需要檔案結構,這是合法的,但如果使用transfers時使用的是非特定的路徑,

           那transfer將無法被初始化則會出現exception。

 

    C-2.  Sizes(檔案大小)

最大上傳檔案大小 5 MB
在行動網路,最大下載檔案大小 20 MB
如果檔案超過限制,TransferPreferences屬性會自動轉變成AllowBattery,這將變成要求於Wi-Fi環境下才能進行傳輸。
在Wi-Fi,沒有外接電源,最大下載檔案大小 100 MB
如果檔案超過100 MB,需設定TransferPreferences屬性為"None",否則傳輸會失敗。如果應用時不確定傳輸檔案的大小時,建議設定成None可以確保傳輸程序不會被拒絕。

 

    C-3. Limits(限制)

每個應用程序的最大未完成請求佇列量。(包括:active與pending的請求) 5
由於傳輸完成後,傳輸請求並不會被自動移除,需要透過Remove(BackgroundTransferRequest)去除存在佇列中已完成的項目。
設備上跨所有應用程序的目前最大傳輸程序 2
當前最多只有二個Background File Transfer在運作。
設備上跨所有應用程序的最大佇列傳輸程序
(Maximum queued transfers across all applications on the device)
500
每個Requeset的HTTP headers最大數量 15
HTTP headers的最大容量 16 KB each

           另外,在使用HTTP headers時,有幾個保留字是不可以使用於BackgroundTransferRequest,如下:

           「If-Modified-Since」、「If-None-Match」、「If-Range」、「Range」與「Unless-Modified-Since」。

 

    C-4. Policies(守則)

    ‧background file transfer不支援在「non-simultaneous voice」與「2G, EDGE, Standard GPRS」環境使用。

       =>因此,background file transfer只支援在Wi-Fi、3G(或更快速)的網路環境。

    ‧當檔案大小超過5 MB時,HTTP headers中的「content-length」與「range」 是必要存在的。Server端應該

       永遠在response中回傳content-length的值。如果不這樣做,可能會造成嚴重的傳輸效能退化。

 

    C-5. Slow Transfer(較慢的速度)

     ‧如果網路的速度低於下列的速度,傳輸任務會被paused(暫停)與retried(重新連線):

         => 3G (50 kbps);Wi-Fi(100 kbps)

 

〉範例說明

該範例為撰寫一個透過手機下載圖示至本機IIS裡。看起來非常容易吧,因為在前年我已經有寫過一個

下載檔案的程式範例:<Windows Phone 7 – 下載檔案至Isolated Storage>,當時是使用WebClient實作Download的功能,

但它只能在程式執行時使用,今天範例則是要讓它也能在背景執行。

 

a. 程式啟動後,先檢查「/shared/transfers」是否存在;

   1: private void Initialization()
   2: {
   3:     using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication())
   4:     {
   5:         //確認特定的目錄是否存在,如果沒有則需要建立。
   6:         if (!isoStore.DirectoryExists("/shared/transfers"))
   7:         {
   8:             isoStore.CreateDirectory("/shared/transfers");
   9:         }
  10:     }
  11: }

 

b. 建立background transfer request物件,並且加上判斷是否已有5個transfer request出現在佇列中;

   1: private void CreateBgTransferRequest()
   2: {
   3:     //檢查目前該應用程式的Background transfer service佇列中是否有5個transfer request了。
   4:     if (BackgroundTransferService.Requests.Count() >= 5)
   5:     {
   6:         MessageBox.Show("此應用程序的背景文件傳輸請求的最大數量已超過。");
   7:         return;
   8:     }
   9:     Uri tUri = new Uri("http://www.zbeauty.cn/UpLoadFile/Image/2011/9/5/151515157693767.jpg", UriKind.Absolute);
  10:     Uri tLocalUri = new Uri("/shared/transfers/151515157693767.jpg", UriKind.RelativeOrAbsolute);
  11:  
  12:     BackgroundTransferRequest tRequest = new BackgroundTransferRequest(tUri, tLocalUri);
  13:     tRequest.Method = "GET";
  14:     tRequest.Tag = "151515157693767.jpg";   //作為識別用
  15:  
  16:     //設定TransferPreferences 為AllowCellular,有外接電源,只要Cellular或wifi就可以下載
  17:     tRequest.TransferPreferences = TransferPreferences.AllowCellular;
  18:  
  19:     //註冊要關心的事件
  20:     tRequest.TransferProgressChanged += new EventHandler<BackgroundTransferEventArgs>(tRequest_TransferProgressChanged);
  21:     tRequest.TransferStatusChanged += new EventHandler<BackgroundTransferEventArgs>(tRequest_TransferStatusChanged);
  22:  
  23:     try
  24:     {
  25:         //加入佇列
  26:         BackgroundTransferService.Add(tRequest);
  27:     }
  28:     catch (InvalidOperationException ex)
  29:     {
  30:         MessageBox.Show("Unable to add background transfer request. " + ex.Message);
  31:     }
  32:     catch (Exception)
  33:     {
  34:         MessageBox.Show("Unable to add background transfer request.");
  35:     }
  36: }

 

c. 監控狀態的改變,成功後移除background tansfer request;

   1: void tRequest_TransferStatusChanged(object sender, BackgroundTransferEventArgs e)
   2: {
   3:     //用於更新畫面的圖示
   4: }
   5:  
   6: void tRequest_TransferProgressChanged(object sender, BackgroundTransferEventArgs e)
   7: {
   8:     switch (e.Request.TransferStatus)
   9:     {
  10:         case TransferStatus.Completed:
  11:             if (e.Request.StatusCode == 200 || e.Request.StatusCode == 206)
  12:             {
  13:                 //代表下載成功了,要手動將它移除
  14:                 RemoveTransferRequest(e.Request.RequestId);
  15:             }
  16:             else
  17:             {
  18:                 //代表下載失敗了,一定要手動將它移除
  19:                 RemoveTransferRequest(e.Request.RequestId);
  20:  
  21:                 if (e.Request.TransferError != null)
  22:                 {
  23:                     Dispatcher.BeginInvoke(() =>
  24:                         {
  25:                             MessageBox.Show(e.Request.TransferError.Message);
  26:                         });
  27:                 }
  28:             }
  29:             break;
  30:         case TransferStatus.WaitingForExternalPower:
  31:             break;
  32:  
  33:         case TransferStatus.WaitingForExternalPowerDueToBatterySaverMode:
  34:             break;
  35:  
  36:         case TransferStatus.WaitingForNonVoiceBlockingNetwork:
  37:             break;
  38:  
  39:         case TransferStatus.WaitingForWiFi:
  40:             break;
  41:     }
  42: }
  43:  
  44: private void RemoveTransferRequest(string transferID)
  45: {
  46:     // Use Find to retrieve the transfer request with the specified ID.
  47:     BackgroundTransferRequest transferToRemove = BackgroundTransferService.Find(transferID);
  48:     // Try to remove the transfer from the background transfer service.
  49:     try
  50:     {
  51:         BackgroundTransferService.Remove(transferToRemove);
  52:     }
  53:     catch (Exception e)
  54:     {
  55:         // Handle the exception.
  56:     }
  57: }

 

d. 執行下載與產生圖示;

   1: private void mbtnDonwload_Click(object sender, EventArgs e)
   2: {
   3:     //執行增加background transfer request
   4:     CreateBgTransferRequest();
   5: }
   6:  
   7: //觸發事件是Background transfer request在completed時。
   8: private void LoadImage(string pLocalPath)
   9: {
  10:     //將isolated storage中儲存的圖片載出來
  11:     var isoFile = IsolatedStorageFile.GetUserStoreForApplication();
  12:     using (var imageStream = isoFile.OpenFile(
  13:         pLocalPath, FileMode.Open, FileAccess.Read))
  14:     {
  15:         var imageSource = PictureDecoder.DecodeJpeg(imageStream);
  16:         imgView.Source = imageSource;
  17:     }
  18: }

 

e. 執行畫面;

<執行前>000 <執行後> 111

 

[補充]

TransferPreferences Enumeration

Member Name Description
None (預設值) 只有在當設備使用外接電源與具有Wi-Fi連線,才允許進行傳輸。
AllowCellular 當設備使用外接電源,具有Cellular或Wi-Fi連線時,允許傳輸。
AllowBattery 當設備使用Wi-Fi連線,具有電池或外接電源時,允許傳輸。
AllowCellularAndBattery 當設備使用電池或外接電源,具有Wi-Fi或Cellular連線時,允許傳輸。

     上述的說明,如果在實作應用時,能夠預期用戶下載的資料大小與環境條件,建議還是按照上述的設定去提示用戶,

     一來可以確保用戶得知限制的提示,二來可以讓程式運作更接近用戶的使用習慣與穩定性。

======

以上是針對Background File Transfer的介紹,該功能學習起來跟Background Agent系列的其他元素相似,

重點在於注意Background的限制,其他的處理就跟一般HttpRequest或WebClient的使用接近了。另外,

在msdn上也有提供在撰寫background Agent的應用時,建議使用實體設備進行測試比較接近實際結果。

送審時,別忘記先對一下「Additional Requirements for Specific Application Types」的限制與要求喔。

 

References:

Background File Transfers Overview for Windows Phone

Background File Transfer Best Practices for Windows Phone

How to: Implement Background File Transfers for Windows Phone (重要)

Background File Transfer Best Practices for Windows Phone (必讀,學習後必看的內容)

Background Transfer Service Sample

Microsoft.Phone.BackgroundTransfer

31 Days of Mango | Day #26: Background File Transfer

Windows Phone 7 Background File Transfer

Leveraging Background Services and Agents in Windows Phone 7 (Mango)

Additions in the Windows Phone SDK 7.1 - Background File Transfers

[WP7.1] Background File Transfer Service

 

Dotblogs Tags: