Shortening the TextEditor invocation time

One slightly annoying feature of my text editor app is the slow invocation time. I think most of the start-up time is spent loading the QML code and processing the code by the QML interpreter. However, most of the code does not need to be processed immediately, e.g. the dialog pages or the file browser page. These could be loaded only when needed. This should speed up the invocation (and probably delay a bit the loading of the dialog and file browser pages but I guess that would be acceptable).

Setting the reference

Before proceeding with the optimizations I think it would be a good idea to measure the actual time used for loading the QML code in the current application. For that we can use the QTime class. Measuring time with it is easy: first call the start() method, then load the QML code and finally call the elapsed() method to get the elapsed time in milli-seconds. We can also use a context property to pass the elapsed time to the QML side for display.

Here is the updated main.cpp file part:

// record the time (in ms) used for loading the QML code
QTime t;
t.start();
QString msElapsed;
viewer.rootContext()->setContextProperty("msElapsed",msElapsed);
viewer.setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
viewer.setMainQmlFile(QLatin1String("qml/texteditor/main.qml"));
viewer.showExpanded();

// pass the elapsed time to the QML side via a context property
msElapsed.sprintf("Time elapsed: %d ms",t.elapsed());
viewer.rootContext()->setContextProperty("msElapsed",msElapsed);

Note that in this case we need to call the setContextProperty function twice: first to introduce the property before loading the QML code and then again to set the property value with the elapsed time.

In the QML side in EditPage.qml we can just replace the initial placeholder text in the editor with the elapsed time in ms.

//property string placeHolder: qsTr("Click here to start typing.")
property string placeHolder: msElapsed

And then set the target to the harmattan device (see Using the harmattan target), recompile and run the application on the N9. The time seems to vary a bit between invocations but the average seems to be about 2.7 seconds on my N9.

invocation-01

Ok, so now we have a reference to compare to.

Delay loading of page stack pages

One method for delaying QML code loading can be used when pushing pages to the page stack. In the TextEditor app there is only one page, the browsePage that will be pushed to the page stack.

// TextEditor requested BrowsePage to be opened for selecting a file.
onBrowseRequested: {
    pageStack.push(browsePage,{folderPath:currentFolder,saveAs:saveRequested});
}

Currently the whole page will be loaded and instantiated in the main.qml file when the application starts. We can avoid that by specifying the source QML file name in place of the page instance name in the pageStack.push call. We can then comment out the browsePage instantiation code.

// Instantiate the BrowsePage component (defined in BrowsePage.qml)
//BrowsePage {
//    id: browsePage;
//}
onBrowseRequested: {
    pageStack.push(Qt.resolvedUrl("BrowsePage.qml"),
                   {folderPath:currentFolder,saveAs:saveRequested});
}

After recompiling and running the app again the invocation time drops to 2.5 seconds so pre-loading the BrowsePage seems to require about 0.2 seconds.

Delay loading of dialog pages

Another method for reducing the QML related loading delay is to use delayed loading with the Loader element. The Loader element makes it possible to load a QML file only when the QML element is first needed.

Let’s say we would like to load the About dialog QML code from the file DialogAbout.qml only when the user first clicks the About entry in the file menu. To accomplish this we first need to instantiate the Loader element in the main.qml file.

// QML component loader
Loader {
    id: myLoader
}

Then we need to update the event handler for the About entry to set the Loader source. Then we can access the loaded QML element using the Loader item property. For the dialog boxes we can define a signal ‘show’ that can be used to open the dialog.

// Menu>About was clicked
onMenuAboutClicked: {
    myLoader.source = "DialogAbout.qml"
    myLoader.item.show()
}

The DialogAbout.qml file is shown below. It contains the basic About dialog code embedded into an Item element. To open the dialog the show signal can be called which then invokes the onShow event handler.

import QtQuick 1.1
import com.nokia.meego 1.0
import "appDefaults.js" as AppDefaults

Item {
    signal show
    onShow: dialog.open()
    QueryDialog {
        id: dialog
        titleText: "TextEditor "+AppDefaults.VERSION
        message: "A simple text editor for the Nokia N9.\n\n"+
                 AppDefaults.HOMEPAGE+"\n"+
                 "License: GPL3\n"+
                 "Contact: <n9dyfi@gmail.com>\n";
        acceptButtonText: "Go to homepage"
        rejectButtonText: "Close"
        onAccepted: {
            Qt.openUrlExternally(AppDefaults.HOMEPAGE)
        }
    }
}

Ok, let’s move all the QueryDialogs to separate QML files and use the Loader to load them. Result: 1.8 seconds. That’s an additional 0.7 seconds improvement. As a conclusion we can reduce the invocation time by 0.9 seconds (about 32%) by delaying both the page stack loading and the QueryDialog loading.

Loading QML code from a resource file

It is also possible to put all the QML codes into a Qt resource file that will then be embedded into the executable binary. Let’s see if that will affect the application invocation/QML load time.

What we need to do is to add a new resource file and then add all the QML files to the resource file. Then we need to change to main.cpp and specify that the main.qml file will be loaded from the resource.

//viewer.setMainQmlFile(QLatin1String("qml/texteditor/main.qml"));
viewer.setSource(QUrl("qrc:/qml/texteditor/main.qml"));

The result? 1.8 seconds. That is the same time as before. So we can say that putting the QML codes into a resource file does not improve the QML loading times. However, having the QML source as part of the executable binary might sometimes be useful.