TL;DR: When switching back and forth between pages in a QML UI, caching the contents speeds up the loading time of the pages.
When writing QML, using a Loader can come in handy: Certain UI elements are loaded in a deferred way, e.g. not at app startup, but when the user clicks a button. When alternating between source elements of a Loader, the old component will always be deleted, as the documentation states: "Setting source to a new URL will also cause the item created by the previous URL to be unloaded."
This might not always be what the user wants, so we have written a CachingLoader component, which (you guessed it) caches all components that have been loaded before:
For benchmarking the code the above repository loads 2 pages with big images to simulate non-trivial loading time of sub-pages. The test shows that for the first time when a page is loaded, all three ways of deferred loading (Loader with "source" property, Loader with "sourceComponent" property, CachingLoader) behave the same.
However, when a previously loaded page is loaded again, the Loader object has to re-read it from disk. The CachingLoader has not unloaded it, but just restores it from the cache for immediate displaying without reading anything from disk:
In this example there is no real difference when loading files via the "source" or "sourceComponent" attribute, the slight differences seem to be only variation between measurements.
Memory usage is a bit harder to measure, because QML does not seem to release the deleted pages right away, but waits for the garbage collector to kick in. However, in general the CachingLoader is trading loading time vs. memory usage, which means when using the CachingLoader the memory usage will be higher. A normal Loader object can release the memory of previously loaded pages, while the CachingLoader keeps all pages in its cache.
The next steps for the CachingLoader would be to introduce a "property bool isCacheable" to have more control about whether pages should be cached or not.
Small caveat: When the CachingLoader caches one of its pages, it sets its "enabled" attribute to false, which means it won't receive no more mouse and keyboard events (when loading it from the cache the attribute is of course set to true again). However, care must be taken for other activity coming from e.g. Connections attributes; those should probably made active only when the root page is active.
Hi,
I've added the possibility to use a Component as source:
import QtQuick 2.15
Item {
id: root
Component { id: empty
QtObject{
property bool enabled: false
property bool visible: false
}
}
property var cachedPages: []
property Component sourceComponent: empty
property Component oldSourceComponent: empty
function reset() {
sourceComponent = empty
oldSourceComponent = empty
sourceUrl = ""
oldSourceUrl = ""
}
onSourceComponentChanged: {
if (oldSourceComponent != empty) {
cachedPages[oldSourceComponent].visible = false
cachedPages[oldSourceComponent].enabled = false
}
if (sourceComponent in cachedPages) {
console.log(sourceComponent + "found in cache, reusing it")
var cachedPage = cachedPages[sourceComponent]
cachedPage.enabled = true;
cachedPage.visible = true;
} else {
if (sourceComponent != empty || sourceComponent != undefined) {
console.log(sourceComponent + "not found in cache, creating it")
…