Queuing multiple downloads

It is commonplace for enterprise apps to require the need to download documents and content from your organization's file servers. Use of queuing is helpful in this scenario to ensure order and delivery while avoiding pitfalls often associated with spawning multiple asynchronous requests. This recipe uses the Ti.Queue CommonJS module to create a persistent, named queue to perform file downloads.

To demonstrate how to implement a persistent queue, this recipe will download to your device 5 MB sample files from Github. The following screenshots illustrate what this recipe looks like while running on both the iPhone and Android devices.

Queuing multiple downloads

Getting ready

This recipe uses the Ti.Queue CommonJS module. This module and other code assets can be downloaded from the source provided by the book, or individually through the links provided in the See also section at the end of this recipe. Installing these in your project is straightforward. Simply copy the Ti.Queue.js file into your project as shown in the following screenshot:

Getting ready

Network connection

This recipe requires a network connection to download files from Github. Please make sure the device or simulator has proper network connectivity.

How to do it...

Once you have added the Ti.Queue module to your project, you need to create your application namespace in the app.js file and use require to import the module into your code as the following code snippet demonstrates:

//Create our application namespace
var my = {
  qMod : require('Ti.Queue'),
  jobInfo:{total:0,completed:0}
};

Creating the recipe's UI

The following steps outline how to create the UI used in this recipe:

  1. First, a Ti.UI.Window is created to attach all UI elements.
    var win = Ti.UI.createWindow({
      backgroundColor: '#fff', title: 'Download Queue', 
      barColor:'#000',layout:'vertical',fullscreen:false
    });
  2. Next, a Ti.UI.ProgressBar is added to the Ti.UI.Window. This will be used to display the status of the queue.
    var progress = Ti.UI.createProgressBar({
      top:30, height:50,min:0,max:3, value:0, left:10, 
      right:10, message:'Tap button to start download'
    });
    win.add(progress);
  3. The show method is called immediately on our Ti.UI.ProgressBar so that it will be displayed on the Ti.UI.Window correctly.
    progress.show();
  4. Next, a Ti.UI.Button is added to the Ti.UI.Window. This control will be used to trigger the queue download logic.
    var downloadButton = Ti.UI.createButton({
      title:'Run Download Queue', top:40,
      left:10, right:10, height:50, 
    });
    win.add(downloadButton);

Creating a queue

The next step in the recipe is to create a named queue as demonstrated in the following code snippet. By default, any named queue is persistent and will store all jobs between sessions.

//Create a new version of the queue
var queue = new my.qMod("demo");

Note

If a name is not provided when creating a new queue, the queue will not save jobs between app restarts.

Adding jobs to the queue

After the queue has been created, the next step is to create a series of jobs for the queue to manage. The following code block creates three download jobs for the queue to manage.

  1. The first job is created to download a ZIP file from Github. A timestamp is used to provide a unique name.
    var sample1Name = new Date().getTime();
    queue.enqueue({
      title:'Sample ' + sample1Name,
      url:"https://github.com/benbahrenburg/
      Ti.Queue/blob/master/5MB.zip",
      downloadPath:Ti.Filesystem.applicationDataDirectory + 
      sample1Name + '.zip',
      attempts:0
    });
  2. Again, a second job is created to download a ZIP file from Github. A timestamp is used to provide a unique name.
    var sample2Name = new Date().getTime();
    queue.enqueue({
      title:'Sample ' + sample2Name,
      url:"https://github.com/benbahrenburg/
      Ti.Queue/blob/master/5MB.zip",
      downloadPath:Ti.Filesystem.applicationDataDirectory + 
      sample2Name + '.zip',
      attempts:0
    });
  3. Again, a third job is created to download a ZIP file from Github. A timestamp is used to provide a unique name.
    var sample3Name = new Date().getTime();
    queue.enqueue({
      title:'Sample ' + sample3Name,
      url:"https://github.com/benbahrenburg/
      Ti.Queue/blob/master/5MB.zip",
      downloadPath:Ti.Filesystem.applicationDataDirectory + 
      sample3Name + '.zip',
      attempts:0
    });

Recipe's assistant functions

The assist object is used to manage the download process. The following is a discussion on how the assist object works and can be leveraged within your app:

var assist = {
  1. The progressSetup method is used to restart the progress bar when a new download system has been initialized.
      progressSetup : function(min,max){
        progress.min = min;
        progress.max = max;
        progress.value = 0;
        progress.message = 'Starting download';
        downloadButton.title = "Downloading... please wait";
      },
  2. The updateProcess method is used to update the progress bar information and alert the user to the status of the overall download job.
      updateProgress : function(value,text){
        progress.value = value;
        progress.message = text;
      },
  3. The whenFinished method is called after all Ti.Queue jobs have finished or generated errors. This method is used to update UI to alert the user about the queued job which has been processed.
      whenFinish : function(){
        downloadButton.text = "Tap button to start download";
        alert('Finished Download'),
        downloadButton.enabled = true;
      },
  4. The next method is used to process the next job in the Ti.Queue.
      next : function(){
  5. This step in the next method is to check if there are any jobs available. This is done by calling the getLength method on the queue to retrieve the number of jobs currently stored in the queue.
        if(queue.getLength() == 0){
  6. If there are no jobs remaining in the queue, the whenFinish method is called and the download process is exited.
          assist.updateProgress(my.jobInfo.total,
          'Download Completed'),
          assist.whenFinish();
          return;
        }
  7. If a job is available in the queue, the next step is to update the progressValue to show the current processing status as shown in the following code snippet.
        var progressValue = 
        (my.jobInfo.total - queue.getLength());
  8. The next step is to call the peek method on the queue. This allows us to fetch the next item in the queue without popping it from the queue itself. This is used to write the information to the Titanium Studio console for debugging purposes.
        var pkItem = queue.peek();
        Ti.API.info('Peek value: ' + JSON.stringify(pkItem));
  9. The next step is to call the dequeue method on the queue. This function pops the next item from the queue and returns the item. In the following code snippet, the dequeue method is called to provide the next queue job to the item variable.
        var item = queue.dequeue();
  10. The updateProgress method is then called to alert the user to the download progress.
        assist.updateProgress(progressValue,'Downloading ' +
        item.title);
  11. Next the download method is called to start the download process. To create a recursive function, the next method is provided as a callback argument to the download method.
        assist.download(item,assist.next);
    
      },
  12. The download method contains all logic needed to download the queued job from Github.
      download :function(item,callback){
  13. The first step in downloading the file from Github is to create Ti.Network.HTTPClient.
        var done = false;
        var xhr = Ti.Network.createHTTPClient();
        xhr.setTimeout(10000);
  14. The onload callback is fired when the Ti.Network.HTTPClient receives a successful response.
        xhr.onload = function(e){
          if (this.readyState == 4 && !done) {
            done=true;
  15. If the response does not provide a 200 status code, an error is generated.
            if(this.status!==200){
              throw('Invalid http reply status code:' + 
              this.status);
              return;
            }
  16. If the proper status code is returned, a Ti.Filesytem object is then created and the responseData is saved to the provided output path.
            var saveToFile =
            Ti.Filesystem.getFile(item.downloadPath);
            if(saveToFile.exists()){
              saveToFile.deleteFile();
            }
            saveToFile.write(this.responseData);
            saveToFile = null;
  17. After the responseData has been persisted to the filesystem, the callback method is triggered. This allows for the recipe to recursively loop through the queue.
            callback();
          }
        };
  18. The onerror callback is triggered when the Ti.Network.HTTPClient receives an error.
        xhr.onerror = function(e){
  19. When an error is received, the provided job is requeued for another attempt, by calling the requeue method on the assist object.
          assist.requeue(item,callback)
        };
  20. The download progress is started by calling the open and send methods on the Ti.Network.HTTPClient using the queue item's url details as demonstrated in the following code snippet:
        xhr.open('GET', item.url);
        xhr.send();
      },
  21. The requeue method is used to add an item back into the queue. The recipe is designed to provide three attempts at downloading before the job is considered a permanent error.
      requeue :function(item,callback){
        Ti.API.info('requeue is called on download fail.'), 
        Ti.API.info('Allowed to retry 3 times'),
  22. The item's attempt property is checked to determine if the job has been attempted more than three times.
        if(item.attempts > 3){
  23. If the job has been attempted more than three times, the item is not readded to the queue.
          Ti.API.info('Max removed from queue.')
        }else{
  24. If the job has errored, less than three times, the attempts property is incremented and the item is added to the queue again.
          item.attempts++; 
          queue.enqueue(item);
        }
  25. Finally the callback method is fired, moving the download process to the next step in its lifecycle.
        callback();
      }
    };

Start downloading

When the downloadButton is pressed the recipe begins to process the queued jobs.

downloadButton.addEventListener('click',function(e){
  1. The first step in the download process is to check if the recipe has a network connection. If the network is not available, the recipe will alert the user to exit the process.
      if(!Ti.Network.online){
        alert('This recipe requires a network'),
        return;
      }
  2. The next step in the download process is to disable the downloadButton. This avoids the user from processing the button again, once the job has started.
      downloadButton.enabled = false;
  3. The my.jobInfo object is then updated with the current count and status information. This will be used to track the overall download status.
      my.jobInfo.total = queue.getLength();
      my.jobInfo.completed = 0;
  4. Next the processSetup method is called to initialize the Ti.UI.ProgressBar with the correct min and max values.
      assist.progressSetup(my.jobInfo.completed,
      my.jobInfo.total);
  5. Finally, the next method is called on the assist object. This begins the download process and creates a recursive loop that will run until the queue is empty.
      assist.next();
    
    });

See also

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.93.169