This time we will improve our little view framework by adding on demand loading of the views.
For your app to have a decent startup time you can’t load all of your QML at one go when your app starts up. You might get away with it if you have a very simple app, but once you start adding more and more views which might contain images (these too will get loaded right away) you’ll end up in trouble.
The solution to the problem is using the Loader element of QML. In case you haven’t read the docs let’s have quick recap. Loader is an element that is very (or at least quite) fast to parse and construct. The idea is that you can ask a Loader to construct a QML component for you not when the loader itself is constructed, but at the time you choose it to do so. The source of the loader can either be a QML file or aComponent element that has already been parsed. Since we want to speed up the startup it doesn’t make much sense for us to have our views parsed during the startup and postpone only the construction which is usually relatively fast (excluding loading of images).
Using Loader is simple. Set the source (url) or sourceComponent (a reference to a Component element’s id). After this you can access your loaded QML component via the item property of the Loader (not with the id of the component you defined or with the id of the root element inside the .qml file you loaded). Setting source to a local file or setting the sourceComponent are synchronous. If you refer to a QML file over a networked protocol the loading will naturally be asynchronous and you have to watch the loaded signal of the Loader. The item constructed by Loader can be destroyed by setting sourceto an empty string or setting sourceComponent to null.
My only wish for Loader (or some specialized flavor of it) would be that it could be completely transparent i.e. the loaded component would become the loader itself and it wouldn’t be necessary to reference the content via item property. Properties of the loaded component would become properties of the loader and functions respectively. This of course poses problems like clashes with the names of the properties, signals and functions, but I’d be willing to take those into account. Referring via item isn’t really that bad, but sometimes you build something you realize you should’ve probably loaderized in the first place and would like to change it with a snap of fingers. Also binding to the properties (without warnings) of the loaded component can be a bit cumbersome as we’ll see later. I’m not expecting to see a transparent loader happening so just keep in mind if you’re making something that is heavyish and not shown always or right away, loaderize it from the get-go.
For loaderizing (great word BTW) our KittyApp views, we will develop a small utility component called ViewLoader (version 1, we’ll refine it a bit later). ViewLoader allows us to loaderize the views with just a few lines per view. In addition loading views it will also support unloading of tje,. This can be useful if you have (heavy) views that you expect the user not to visit many times per session.
Let’s see what the ViewLoader has eaten:
The root element of the component is naturally a Loader (duh). The viewSource property is the URL to the QML file (the view) to be loaded. To keep things slim ViewLoader expects loads to be synchronous, so play nice and feed it with files or references to Qt resource files. The keepLoaded property defines whether the view should be destroyed once it’s deactivated. The anchor and opacity definitions are there for convenience reasons. Since our KittyApp views are dimensionally homogeneous (fancy words <3), we can define them here instead of where the ViewLoaders elements are declared. QDeclarativeEngine has an internal component cache, so ViewLoader.qml gets parsed only once and thus we have to parse less QML. Yay! activationComplete() and deactivationComplete() forward the calls to the loaded view (if it has them). The deactivation callback is also used for destroying the view if keepLoaded is false. loadView() is pretty self explanatory – it loads the view. It’s used by ViewSwitcher to load the view, but you can also call it yourself, if you know that some view will be needed and there’s a good spot (there’s already something going on for the user, but you’re waiting for input or there’s an asynchronous request going on) to do it.
To tie things together we’ll add couple of lines to ViewSwitcher from the previous post:
The only difference to the previous version is the two lines where the loadView()gets called. We are checking if loadView() exists, because this gives us the extra flexibility of mixing Item based views and ViewLoader views. Neat!
In the next post we’ll add some context sensitivity to the UI. I’ll post the whole source code and a Symbian SIS file, so you can experiment yourself. Until then, farewell you QDeclarativeItem deriving rapscallions!