Windows 8 Javascript app activation/launch deferra

2019-06-08 16:05发布

问题:

So currently in a windows 8 WinJS app I'm coding, I am trying to get the loading of an xml file to take place in the app startup sequence, while the splash screen is still showing, as this xmldoc element is needed for when the home page loads, and loading of the home page will fail without it.

This is my initiation sequence in default.js:

(function () {
    "use strict";

    var activation = Windows.ApplicationModel.Activation;
    var app = WinJS.Application;
    var nav = WinJS.Navigation;
    var sched = WinJS.Utilities.Scheduler;
    var ui = WinJS.UI;

    app.addEventListener("activated", function (args) {
        if (args.detail.kind === activation.ActivationKind.launch) {
            if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
                // TODO: This application has been newly launched. Initialize
                // your application here.
                console.log("Newly Launched!");

                var localSettings = Windows.Storage.ApplicationData.current.localSettings;
                WinJS.Namespace.define("MyGlobals", { localSettings: localSettings });

                // APP RUN TYPE CHECK AND SEQUENCE (FIRST RUN / NOT FIRST RUN):
                if (MyGlobals.localSettings.values['firstRunCompleted']) {
                    console.log("NOT FIRST RUN!");
                    // CACHE VERSION CHECK. IF APP HAS BEEN UPDATED, INITIATE NEWLY ADDED CACHE VALUES HERE:
                } else {
                    console.log("FIRST RUN!")
                    MyGlobals.localSettings.values['firstRunCompleted'] = true;
                };


                //loadXML(); have tried many things with this. doesn't work.

                

            } else {
                // TODO: This application has been reactivated from suspension.
                // Restore application state here.
                var currentVolume = app.sessionState.currentVolume;
                if (currentVolume) {
                    console.log("RESTORE FROM SUSPENSION");
                    console.log(currentVolume);
                };
            }

            nav.history = app.sessionState.history || {};
            nav.history.current.initialPlaceholder = true;

            // Optimize the load of the application and while the splash screen is shown, execute high priority scheduled work.
            ui.disableAnimations();
            var p = ui.processAll().then(function () {
                return nav.navigate(nav.location || Application.navigator.home, nav.state);
            }).then(function () {
                return sched.requestDrain(sched.Priority.aboveNormal + 1);
            }).then(function () {
                ui.enableAnimations();
            });

            args.setPromise(p);
            args.setPromise(WinJS.UI.processAll().then(function completed() {

                loadSavedColour();
                 
                // Populate Settings pane and tie commands to Settings flyouts.
                WinJS.Application.onsettings = function (e) {
                    e.detail.applicationcommands = {
                        "helpDiv": { href: "html/Help.html", title: WinJS.Resources.getString("settings_help").value },
                        "aboutDiv": { href: "html/About.html", title: WinJS.Resources.getString("settings_about").value },
                        "settingsDiv": { href: "html/Settings.html", title: WinJS.Resources.getString("settings_settings").value },
                    };
                    WinJS.UI.SettingsFlyout.populateSettings(e);
                }
                 

As you can see where I have the commented line of "loadXML()", that is where I need the loadXML() function to take place. Here is my loadXML() function:

function loadXML() {
        Windows.ApplicationModel.Package.current.installedLocation.getFolderAsync("foldername").then(function (externalDtdFolder) {
            externalDtdFolder.getFileAsync(MyGlobals.localSettings.values['currentBook']).done(function (file) {
                Windows.Data.Xml.Dom.XmlDocument.loadFromFileAsync(file).then(function (doc) {
                    WinJS.Namespace.define("MyGlobals", {
                        xmlDoc: doc,
                    });
                })
            })
        });
    };

(loadXML is a working function and works in other scenarios)

However, the issue is that before the loadXML function finishes, the app splash screen goes away, and the next home.html home page loads, which starts the accompanying home.js, which has a function that requires the MyGlobals.xmlDoc object that loadXML should have made. This immediately crashes the app, as MyGlobals.xmlDoc is undefined/null. I used to have this app working by running loadXML in home.js for the home.html page directly, but in that scenario the XML document is reloaded every time navigation is made to the page, wasting time and resources. As such, I'm trying to move the xmldocument loading into the app startup/initialization. Thanks a lot!

回答1:

loadXML has async functionality and you need to handle that.

You shouldn't expect that the loadFromFileAsync (or any of the other async functions) have completed before it returns to the caller. If your code doesn't wait, you'll find that the MyGlobals.xmlDoc value won't be set when you need it.

I've renamed it below to be more accurate as to its behavior. The big change is that it returns a Promise that can be used by the caller to properly wait for the Xml doc to be loaded. This Promise could be used with other Promises to wait on multiple conditions if you'd like (or in the case, other async work).

function loadXMLAsync() {
    return new WinJS.Promise(function (complete, error, progress) {
        var localSettings = MyGlobals.localSettings.values;
        var installedLocation = Windows.ApplicationModel.Package.current.installedLocation;
        installedLocation.getFolderAsync("foldername").then(function (externalDtdFolder) {
            externalDtdFolder.getFileAsync(values['currentBook']).done(function (file) {
                Windows.Data.Xml.Dom.XmlDocument.loadFromFileAsync(file).then(function (doc) {
                    complete(doc);
                });
            });
        });
    });
};

Then, in use:

loadXmlAsync().then(function(doc) {
    WinJS.Namespace.define("MyGlobals", {
        xmlDoc: doc,
    });
    // and any other code that should wait until this has completed
});

The code above does not handle errors.



回答2:

I believe what you need to do is to extend the splash screen to give your app more time to initialize UI. For your scenario, it's loading the xml.

I will suggest you read How to extend the splash screen (HTML). The main idea is to display an extended splash screen(you can use the same image as the default one) in the activated event, then call your loadXML.



回答3:

In addition to other comments, what you really need to do is include the promise from loadXml with what you pass to args.setPromise. As you know, setPromise is a way to tell the app loader to wait until that promise is fulfilled before removing the splash screen. However, in your code you're calling setPromise multiple times. What you should be doing is joining all the promises you care about (animations, loadXml, and setting loading) with WinJS.Promise.join, so that you get a single promise that's then waiting on all the other three, and when that one is fulfilled, then remove the splash screen.

Alan's suggestion for an extended splash screen is helpful if that whole loading process ends up taking too long, as doing an extended splash gives you total control over what's happening and when the transition happens to your main page.