Streaming Install Part 4: Support Streaming Install in the App
After you have your content group map for your app, you need to support streaming install in your app before you can deploy or package it. The most important part of supporting streaming install is that all file access in your app must be preceded by a check for whether the content group (that contains the file to be accessed) has been downloaded (Staged). If you are building a platformer game with each level being an automatic group, and let’s say that the user selects level 2 from the main menu, you can check whether the level 2 content group has been staged by:
var level2 = await Package.Current.GetContentGroupAsync("Level2");
if (level2.State == PackageContentGroupState.Staged) { return true; }
Here, if the state of the “Level2” content group is Staged, then that means all files that are part of the content group has been downloaded and is ready to be accessed by the app. The other states of PackageContentGroupState are NotStaged (group has not been downloaded and is not in queue), Queued (group has not been downloaded but is in queue), Staging (group is currently being downloaded).
You can also use
Package.Current.GetContentGroupsAsync()
to get a collection of all the content groups of your app and their states.
Prioritizing Content Group Download
Logically, if the “Level2” content group state is not Completed, then we should proceed to download it immediately. Continuing from the above snippet, you can do this by:
else {
List<string> contentGroups = new List<string>() { "Level2" };
Return await Package.Current.StageContentGroupsAsync(contentGroups, ContentGroupRequestPriority.Highest);
}
Here, we requested that the “Level2” content group be downloaded with the highest priority, and so if the app is currently being streaming installed, then the platform will pause the download of the current content group, then proceed to start downloading “Level2”. In contentGroups, we could also have listed other content groups and request them all to be downloaded with priority at once (the content group listed first will be downloaded first).
*Note that before the platform can switch to downloading the newly requested content group, it must finish downloading the current block for the content group that’s currently being downloaded. This means that prioritization will not happen immediately and can be slow, depending on the user’s network speed. We recommend showing users a message that the requested content will be downloaded but please be patient while the download priority is being switched.*
It is also important to note here that without any requests to prioritize content groups from the app, the platform will download content groups (and files within content groups) in the order that is listed in the AppxContentGroupMap.xml by default. So from our streaming demo app’s AppxContentGroupMap.xml that is in Part 2, “Level3” will be downloaded after “Level2”.
Prioritizing Content Groups in Other Packages
Apps can also reprioritize content groups from a stream-able resource pack to download first too. You can do this by first getting the resource package from Package.Current.Dependencies, then calling StageContentGroupAsync on the package. So if we are in the streaming demo app and currently downloading a content group in the main pack but want to prioritize FrenchPack2, we can do this by:
foreach (var package in Package.Current.Dependencies)
{
if (package.Id.FullName.Contains("language-fr") && package.IsResourcePackage)
{
List<String> contentGroups = new List<String>() { "FrenchPack2" };
package.StageContentGroupsAsync(contentGroups, ContentGroupRequestPriority.Highest);
}
}
This will make FrenchPack2 the first in the queue for this resource pack, and prioritize this resource pack’s staging session before the main pack’s session (A staging session is a job to download each package). The impact of reprioritizing a resource pack’s content group is that the once that content group is done, the next content group that will be downloaded is the next group in queue for that SAME staging session, which is the session for the resource pack. So if no other group is further prioritized, the resource pack will now be completely downloaded before the main pack resumes downloading. To make the main pack resume from where it was before, you must call StageContentGroupAsync again to prioritize the main pack’s content group after the group in the resource pack has finished downloading.
Using StageContentGroupAsync to Resume the Download
If the download of the app was paused by the user (from the Store) or was paused because of an error, you can reinitiate the download of the app by calling the StageContentGroupsAsync API again on any content group. If the error that paused the download was a transient error, the download will start again. This allows users to not have to navigate away from the app and into the Store to kickstart the download again. However, if a download is resumed this way, the content groups will download in the default order (ignoring the content group that was prioritized with the API that kickstarted the download). You would have to call StageContentGroupAsync with the same content group again to have it download first (essentially, resuming the download after a pause will erase all previous prioritizations). A good way to know if this is happening after calling StageContentGroupsAsync is to see if you start getting notifications (next section) from the group you prioritized or from some other group after sometime (keep in mind here that the platform will always finish the current block for the currently downloading content group first before it reprioritizes).
Getting Notifications of Download Progress
After we prioritized “Level2” to be downloaded first, we should also show the user the download progress of “Level2”, since the user explicitly selected this level and may be waiting to play it.
You can get notifications of progress from the PackageCatalog:
var catalog = PackageCatalog.OpenForCurrentPackage();
catalog.PackageContentGroupStaging += async (c, args) => {
ShowProgress(args.Progress * 100);
}
Where ShowProgress is the function that draws the progress UI for the user. (It is also recommended for catalog to be global so it can continue to receive notifications.)
From these notifications, you should also account for the scenario where your app is getting very slow notifications or no notifications at all, which could mean that the download was stalled for a number of reasons. The platform will always keep retrying a download if a failure occurs, but a prioritization request can also manually kick off a retry. If the download stalls because the user has a slow or disconnected internet, then it might be helpful to display a message of some kind so that the user is aware that the download will take a while or for the user to take action.
Follow the links below for more detailed information on building apps with streaming installation:
- Streaming Install Part 1: UWP Streaming App Installation Overview
- Streaming Install Part 2: Content Group Maps Overview
- Streaming Install Part 3: Content Group Maps Deep Dive
- Streaming Install Part 4: Support Streaming Install in the App
- Streaming Install Part 5: Testing and Debugging a Stream-able App
<more links coming soon>
Questions? Ask in the comments section below.
Andy Liu, Program Manager, Windows Developer Platform