Sep 08
24
Google Analytics provides an easy to use interface that tells you which pages on your website are
being accessed, how often they are accessed, and lots of other useful information about how your website is used.
The problem for me on a recent project was to collect usage statistics for a Flex application. To use Google analytics
you simply include a small javascript element in each page that’s served. When the browser executes the
javascript a small message is sent to google recording the page URL that was accessed and some other details about the client.
The first problem with a Flex application is that it’s really just a single big(ish) download of the swf (Flash) file, so
all that will get recorded is the fact that someone accessed your application. If the Flex application has several
different views within a view stack none of the navigation between those views will be recorded.
In addition, if the Flex application is using web
services to communicate to a backend server, these requests wont be recorded either. Here’s how I got my Flex application to
record statistics of the navigation within the application and the web service calls made to a backend server.
When you register your website with Google analytics you’re given a javascript element to include in your pages.
Start by adding this javascript to your html file that loads the Flex application. It should look something like this:
Notice the last line is a call to a function _trackPageview() with no arguments. This is the code that sends a message
to google telling them which page was viewed. The function uses the current location when called with no arguments, but supports
a single string argument to allow you to specify a particular URL.
For my solution, I want to call this function passing in a URL that represents either the user navigating to a view, or a web service
call. To do this I need to invent an URL structure for my site that reflects the names of the views and the names of the web service
calls that are made. More on that later. First I need to change the javascript provided by Google to allow me to make an
External call passing in a URL. Notice that I’ve wrapped the original 2 lines in a function called track(url) which takes a single
URL argument and passes it into the call to _trackPageview().
Now in my Flex application, whenever the user clicks on a link that navigates them to another view, I can add the following code to
dispatch a new event that represents the user clicking on the “My Account” link. My application is using
Cairngorm events and Commands for the event handling framework, so the new event looks like this:
package uk.co.blackpepper.controller.events
{
import com.adobe.cairngorm.control.CairngormEvent;
public class AnalyticsEvent extends CairngormEvent
{
public static var EVENT_ID:String = "AnalyticsEvent";
public var url:String;
public function AnalyticsEvent(theUrl:String)
{
super(EVENT_ID);
this.url = theUrl;
}
}
}
And it gets dispatched when a user clicks on a link.
new AnalyticsEvent("/page/My Account").dispatch();
The event is processed by a Command, as shown below, which simply passes the URL onto the javascript function using the
ExternalInterface.
package uk.co.blackpepper.controller.commands
{
import com.adobe.cairngorm.commands.Command;
import com.adobe.cairngorm.control.CairngormEvent;
import uk.co.blackpepper.controller.events.AnalyticsEvent;
import flash.external.ExternalInterface;
public class AnalyticsCommand implements Command
{
public function execute(evt:CairngormEvent):void
{
var event:AnalyticsEvent = evt as AnalyticsEvent;
var result:Object = ExternalInterface.call("track",event.url);
}
}
}
Using this design, events can be dispatched anywhere in the application using different URLs to represent each view.
With the navigation problem solved, the next step was to track the web services calls. I use the AsyncResponder to process
the web service calls, along with a helper super class to initialise the wsdl and setup the web service.
package uk.co.blackpepper.webservices
{
import uk.co.blackpepper.model.ModelLocator;
import mx.rpc.AsyncToken;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.soap.Operation;
public class LoginWebService extends BaseWebService
{
public function LoginWebService()
{
super("LoginService");
}
public function getUserDetails(resultHandler:Function, faultHandler:Function):void
{
var token:AsyncToken = service.getUserDetails(ModelLocator.instance.credentials.userPasswordHash);
token.addResponder(new TrackingAsyncResponder(resultHandler, faultHandler, token, serviceName + "/getUserDetails"));
}
}
}
In order to support the need to associate a URL with the call I created a TrackingAysncResponder that takes a URL as an additional
parameter and generates the required Analytics events depending on if the call was successful or not.
All the web service calls are recorded as URLs in the form /service/[serviceName]/[methodName] and optionally, they have /fault
on the end if there was an error making the call.
package uk.co.blackpepper.webservices
{
import com.adobe.cairngorm.control.CairngormEventDispatcher;
import uk.co.blackpepper.controller.events.AnalyticsEvent;
import uk.co.blackpepper.model.ModelLocator;
import mx.rpc.IResponder;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
public class TrackingAsyncResponder implements IResponder
{
private var _uri:String;
private var _resultHandler:Function;
private var _faultHandler:Function;
private var _token:Object;
public function TrackingAsyncResponder(result:Function, fault:Function, token:Object=null, uri:String=null)
{
_uri = uri;
_resultHandler = result;
_faultHandler = fault;
_token = token;
}
public function result(data:Object):void
{
new AnalyticsEvent("/services/" + _uri)).dispatch();
_resultHandler(data);
}
public function fault(info:Object):void
{
new AnalyticsEvent("/services/" + _uri + "/fault")).dispatch();
_faultHandler(info);
}
}
}
Using this design I can easily record all the links clicked on by users and track all the web service calls made by the application. In addition, because of the way Google Analytics works, I also get lots of additional insight into the users of my application.
