IT tutorials
 
Technology
 

Windows Phone 8 : Multitasking - Background Transfer Service

8/21/2013 11:15:19 AM
- Free product key for windows 10
- Free Product Key for Microsoft office 365
- Malwarebytes Premium 3.7.1 Serial Keys (LifeTime) 2019

One of the most common reasons developers want to use background processes for Windows Phone is so that they can download or upload data from the Internet. Having to create an entire agent (and have the user manage that agent) just to accomplish that sort of work seems unnecessary—because it is.

The Windows Phone includes a special service called the Background Transfer Service (BTS). This service’s job is to allow you to queue up downloads and uploads to be performed by the system. These transfers do not require that your application stay running to be performed. Essentially, the service enables you to send it a small number of requests that it will perform on your behalf. But to be able to use the service, you need to be aware of a number of requirements and limitations.

Requirements and Limitations

An overarching goal of the BTS is to enable you to perform the work you need to do without affecting the performance of the phone, or affecting the user of the phone, in a negative way. To meet these goals, the BTS works in the following specific ways:

• Transfer protocol:

– All transfers are made via HTTP or HTTPS.

– Supported verbs include GET and POST for downloads and POST for uploads.

• Transfer location:

– All transfers must take place to or from isolated storage in a special subdirectory called /shared/transfers.

– This directory is created during installation, but if your application deletes it, you must re-create it before any transfers.

– You can create any files or directories under this subdirectory.

• Forbidden headers:

– If-Modified-Since

– If-None-Match

– If-Range

– Range

– Unless-Modified-Since

• Size policies:

– Maximum upload: 5MB

– Maximum download via cellular connection: 20MB1

1 If a download exceeds 20MB, the remaining data will require download via Wi-Fi or PC connection and external power.

– Maximum download via Wi-Fi on battery: 100MB

– Maximum download via Wi-Fi on external power: unlimited

• Request limits:

– Maximum outstanding requests per application: 5

– Maximum concurrent requests: 2

– Maximum number of headers per request: 15

– Maximum size of each HTTP header: 16KB

• Supported networks:

– 2G, EDGE, Standard GPRS: not supported

– 3G or higher: supported but must have minimum 50Kbps throughput

– Wi-Fi/PC connection: supported but must have minimum 100Kbps throughput

Requesting Transfers

To get the BTS to perform a transfer, you must first create a new request. A request takes the form of a BackgroundTransferRequest object. At a minimum, you must specify the source and destination of your request like so:

// Determine the source and destination
var serverUri = new Uri("http://shawntwain.com/01-Anne.mp3");
var isoStoreUri = new Uri("/shared/transfers/01-Anne.mp3",
                          UriKind.Relative);

// Create the Request
var request = new BackgroundTransferRequest(isoStoreUri, serverUri);

The constructor for the request can accept two URIs that specify where to copy from and where to copy to. Typically this takes the form of an Internet URI for the source (for downloading) and a relative URI to the special /shared/transfers directory in isolated storage. To make the request, you can simply add this to the requests on the service (for instance, the BackgroundTransferService class):

BackgroundTransferService.Add(request);

This will queue up the request and have the service perform the transfer for you. To request an upload the method is similar, but the source and destination are reversed as well as specifying the method:

// Determine the source and destination
var serverUri = new Uri("http://shawntwain.com/01-Anne.mp3");
var isoStoreUri = new Uri("/shared/transfers/01-Anne.mp3",
                          UriKind.Relative);

// Create the Request
var request = new BackgroundTransferRequest(serverUri);

// Set options
request.UploadLocation = isoStoreUri;
request.Method = "POST";

// Queue the Request
BackgroundTransferService.Add(request);

The constructor for the BackgroundTransferRequest that takes two arguments is specifically for downloading files from the server. When you want to request an upload, you need to create the request by specifying the server URI that represents where the upload is going. Then you need to specify the UploadLocation as a URI that represents the path to files in the special transfer directory from which to upload.

In addition to simply specifying the source and destination for your request, you can also specify other optional properties on the BackgroundTransferRequest class, including the following.

Headers: This enables you to set specific headers for the HTTP transfer.

Tag: This enables you to set an arbitrary string that contains additional information you can use when retrieving the request.

TransferPreferences: This is an enumeration that specifies which circumstances are allowed, including

None: Only allow transfers while on external power and using a high-speed network (for example, Wi-Fi or PC connection). This is the default.

AllowCellular: Allows the request while on a cellular network but must be on external power.

AllowBattery: Allows the request on Wi-Fi but does not require an external power source.

AllowCellularAndBattery: Allows the transfer on cellular and while on battery. You should use this only for small and immediately needed requests. Use judiciously!

Requesting the transfers is adequate, but it is also up to your application to monitor the transfers (and possibly let the user know the status of the transfer). That is where monitoring of your own transfers becomes necessary.

Monitoring Requests

The BackgroundTransferService class supports a static property called Requests that represents an enumerable list of all the current requests (including those that have completed and those that have failed). Because access to the requests is through a property, you might be lured into expecting that the collection represents the overall status of requests, but that is not how it works. When you access the property, it returns a copy of the current state of the requests, but these are not updated as the requests are processed. So even though you can use data binding to show the user the list of requests and their current status, they will not update until you retrieve the list of results again.


Retrieving the Requests

Every time you access the Requests property on the BackgroundTransferService, it makes a copy of the current state of each request. This process is not cheap, so it is recommended that you do not access this property in a tight loop or based on a subsecond timer.


Let’s look at a strategy for keeping the user apprised of the status of requests. To start, you should keep a local cache of the last requests you retrieved from the BackgroundTransferService class, like so:

public partial class MainPage : PhoneApplicationPage
{
  // Constructor
  public MainPage()
  {
    InitializeComponent();
  }

 IEnumerable<BackgroundTransferRequest> _requestCache = null;

  // ...
}

You should retrieve the requests when someone navigates to the page, so you can show the status to the user:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
  base.OnNavigatedTo(e);

  // Update the Page
  RefreshBindings();
}

In this example, the RefreshBindings method is used to retrieve the requests again, as well as bind them to the user interface:

void RefreshBindings()
{
  // Dispose of the old cache to stop memory leaks
  if (_requestCache != null)
  {
    foreach (var request in _requestCache)
    {
      // Clean up the cache to prevent leaks
      request.TransferProgressChanged -=
        request_TransferProgressChanged;
      request.TransferStatusChanged -= request_TransferStatusChanged;
      request.Dispose();
    }
  }

  // Get the Updated Requests
  _requestCache = BackgroundTransferService.Requests;

  // Wire up the events
  foreach (var request in _requestCache)
  {
    request.TransferProgressChanged +=
      request_TransferProgressChanged;
    request.TransferStatusChanged += request_TransferStatusChanged;
  }

  // Rebind
  filesListBox.ItemsSource = _requestCache;
}

The first part of this method takes any existing cache (as this will be called in a number of cases as the transfers change in status) and cleans them so that they don’t leak resources. The returned requests support the IDisposable interface, so you must call Dispose on each of them. In addition, this example is unwiring two events you are wiring up every time you retrieve the requests. After the cleanup is complete, the code retrieves the current state of the requests. When it has the new requests, it wires up two events—one that indicates that the request’s progress has changed (for instance, the transfer is proceeding) and one that is fired when the status of the transfer changes (succeeded or failed). Lastly, the new cache is rebound to the user interface (a ListBox in this case).

On the face of it, this is a lot of work where you might be used to just binding to a collection that simply changes the underlying data using binding interfaces (for instance, INotifyPropertyChanged and INotifyCollectionChanged). This is not how the BackgroundTransferService’s requests work. You must rebind them on every update.

To ensure that the page is updated as the progress is changed, the handler for each request’s TransferProgressChanged event simply calls the RefreshBinding method:

void request_TransferProgressChanged(object sender,
                                     BackgroundTransferEventArgs e)
{
  // Update the Page
  RefreshBindings();
}

This does mean that the underlying data is being refreshed quite a lot, but that’s necessary to update the user with the latest progress information. You can decide to update it only as transfers complete (which happens less often). This example handles that event as well:

void request_TransferStatusChanged(object sender,
                                   BackgroundTransferEventArgs e)
{
  // Limited to 5 requests
  // So remove it when it's done
  if (e.Request.TransferStatus == TransferStatus.Completed)
  {
    if (BackgroundTransferService.Find(e.Request.RequestId) != null)
    {
      BackgroundTransferService.Remove(e.Request);
    }
    e.Request.Dispose();
  }

  // Update the Page
  RefreshBindings();
}

The difference here is that not only is the code calling RefreshBindings, but if a request is complete, it is being removed from the service. Although removing the completed items is suggested, you can decide exactly when to do this. The reason completed requests are removed automatically is so that when the user relaunches your application, you can see which requests succeeded; therefore as this example shows, you will need to remove those requests. This becomes important because (as stated earlier) you can have only five requests at a time. If you have five requests in the service—even if they are all complete—the sixth request will throw an exception. It is up to you to manage the list of transfers.

When displaying the status of the requests, the BackgroundTransferRequest objects that are returned include several pieces of read-only data:

RequestId: This is a generated GUID that can be used to track the request.

TotalBytesToReceive: This is the total number of bytes to be downloaded in the request. This value is 0 until the request has started.

BytesReceived: This is the number of bytes downloaded so far.

TotalBytesToSend: This is the total number of bytes to be uploaded in the request.

BytesSent: This is the number of bytes sent so far.

TransferStatus: This is an enumeration that indicates the current state of the transfer. The state can be one of the following:

None: The system has not queued the request yet.

Transferring: The request is being performed.

Waiting: Typically this means it is waiting for other pending transfers to be completed.

WaitingForWiFi: The request is queued, but it cannot start until a Wi-Fi connection is available.

WaitingForExternalPower: The request is queued, but it is waiting for external power to be available (for instance, the phone is on battery).

WaitingForExternalPowerDueToBatterySaverMode: The battery is low and all requests have been suspended.

WaitingForNonVoiceBlockingNetwork: The request is queued, but it is waiting for a higher-speed cellular network or Wi-Fi to be available to complete the request.

Paused: The BTS has stopped the request temporarily.

Completed: The request is complete. You should check the TransferError to ensure that the request was successful.

Unknown: The system is unable to determine the state of the transfer.

StatusCode: This is the HTTP status code (a number) that indicates what was returned by the server.

TransferError: This is the exception (if any) that was encountered during the transfer. You should check to see that the TransferError is null when a transfer is complete to ensure that the transfer completed successfully.

The way your application manages requests is crucial if you are going to use the BTS because your application is the only place to manage your requests. The user does not have an operating system management screen for the BTS.

Although keeping the user apprised of the status of the transfers is important (no matter how you let him know about the status), the work is worth the effort because building an efficient and robust download and upload system expressly for your application is not an easy task. By relying on the BTS to accomplish these types of transfers, it should be easier to write your application.

 
Others
 
- Windows Phone 8 : Multitasking - Location-Aware Apps
- Active Directory 2008 : Managing Enterprise Security and Configuration with Group Policy Settings -- Implementing an Audit Policy (part 2) - Auditing Directory Service Changes
- Active Directory 2008 : Managing Enterprise Security and Configuration with Group Policy Settings -- Implementing an Audit Policy (part 1) - Auditing Access to Files and Folders
- Active Directory 2008 : Managing Enterprise Security and Configuration with Group Policy Settings -- Managing Software with Group Policy (part 2)
- Active Directory 2008 : Managing Enterprise Security and Configuration with Group Policy Settings -- Managing Software with Group Policy (part 1)
- Microsoft Lync Server 2010 : PBX Integration - Key Improvements
- Microsoft Lync Server 2010 : PBX Integration - End-User Scenarios
- Microsoft Lync Server 2010 : PBX Integration - Integration Methods
- Microsoft Lync Server 2010 : PBX Integration - Telephony Overview
- Windows 8 : Maintaining Data Access and Availability - Using Branch Caching
 
 
Top 10
 
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 2) - Wireframes,Legends
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 1) - Swimlanes
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Formatting and sizing lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Adding shapes to lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Sizing containers
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 3) - The Other Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 2) - The Data Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 1) - The Format Properties of a Control
- Microsoft Access 2010 : Form Properties and Why Should You Use Them - Working with the Properties Window
- Microsoft Visio 2013 : Using the Organization Chart Wizard with new data
Technology FAQ
- Is possible to just to use a wireless router to extend wireless access to wireless access points?
- Ruby - Insert Struct to MySql
- how to find my Symantec pcAnywhere serial number
- About direct X / Open GL issue
- How to determine eclipse version?
- What SAN cert Exchange 2010 for UM, OA?
- How do I populate a SQL Express table from Excel file?
- code for express check out with Paypal.
- Problem with Templated User Control
- ShellExecute SW_HIDE
programming4us programming4us