Monday, March 12, 2012

N9, QML, Javascript and differential update.

I had a server app that had both a desktop and web interface. Though I discovered that the web interface was less than monkey finger friendly on the n9 and required some authentication each time. I thought this was a job for QML and javascript on the device. The good news is that I now have a client using just those tools, so no cross compilation needed at all ;)

The core of the UI is updated via a timer which runs an ajax call, parses the JSON response and gives that to updateModel(). The first implementation of the later method was to clear the listModel and repopulate from scratch each time. Rather wasteful, even though it was only being called once every 2-3 seconds, it was eating up 8-9% CPU of the n9 in top. A better solution is to iterate the existing items, performing update, remove, or add as you go. This needs a "key" field for each tuple and a stable total order on the model JSON data, either from the server or through an explicit client side sort.

I'll probably put this up on github soon, but in the meantime, this is the updateModel() which has the smarts I mentioned above. It takes 3-4% of CPU instead, and should maintain view state better on the device. I use objid as the key, and the "state" is cloned to objstate because I noticed some issues there with QML interaction using state as the property. Interesting what one can do with QML+JS in very short time for the n9.


function updateModel( model, data )
{
var existingIndex = 0;
var existingIndexMax = model.count;
var id;

for( id in data )
{
var found = false;
var name = data[ id ];
var col = data[ id ];
var k;
var row = {};
row[ "objid" ] = id;
row[ "name" ] = "v";
for( k in col )
{
if( k == "state" ) {
row[ "objstate" ] = col[k];
}
else {
row[ k ] = col[ k ];
}
}

// find it if it exists...
for( ; existingIndex < existingIndexMax; ++existingIndex )
{
if( model.get( existingIndex ).objid == id )
{
found = true;
model.set( existingIndex, row );
++existingIndex;
break;
}
else
{
// This element from the local model no longer exists on the server,
// remove it. Do not advance the index as we have removed an element instead.
model.remove( existingIndex );
--existingIndex;
}
}

// it didn't exist, add it as new.
if( !found )
{
model.append( row );
}
}
}

No comments: