Wednesday, July 27, 2011

Tracking user behaviour in an Android app

If your app asks the the INTERNET permission then I recommend that you embed an analytics library into your app right from the start. This will let you get near real time information about who is using your app and how they are using your app.

Adding an analytics library is easy. I use Flurry and at it's simplest you just add the following to each of your Activities:

    @Override
    public void onStart() {
        super.onStart();
        FlurryAgent.onStartSession(this, FLURRY_KEY_FOR_THIS_APP);
    }

    @Override
    protected void onStop() {
        super.onStop();
        FlurryAgent.onEndSession(this);
    }
This will give you all kinds of information about your users. Such as how many times is your app used per day and how much time do user's spend on it. And also in what order are different activities invoked and how much time ares user's spending on each Activity.

Subsequently you can start getting finer grained information by reporting on specified events, such as how times was a new game started and hence on average how many games are played in a single session by a user. This is a simple as adding the following as required.

FlurryAgent.onEvent("gameStarted");

You can even provide a Map of arbitrary parameters to be associated with the event.

final Map<String, String> params = new HashMap<String, String>();
params.put("score", getScore(jumble));
params.put("percentFound", getPercentFound(jumble));
params.put("wordsPerMinute", getWordsPerMinute(jumble));
FlurryAgent.onEvent("gameOver", params);

The event data has let me get a good understanding of how people are playing the game. And helped me tailor my development efforts so that I'm spending time improving areas that are of interest and relevance to my users. Don't get me wrong, direct feedback from user's is gold, but it's rare and it's the voice of a highly motivated individual, it may not reflect the vast majority, that's where the statistics provided by an analytic engine comes to the fore.

Overall the data from Flurry (and I expect any analytic engine) is more than 10 times as much information as is available via the Android Market. I just wish I had it embedded right from the beginning, because it's not entirely clear how many user's are still running old versions for which I have no info.

Tuesday, June 7, 2011

Best 2 design decisions I made for my Android app

I published my first app for the Android ecosystem a bit over a month ago. It's a word puzzle game called Jumblee. There are 2 design decision that I made early on that have paid tremendous dividends and I believe are worthy of consideration for all Android apps.

The first was to include ACRA to capture any app failures no matter what Android version, and to post details of the failure including the stacktrace to a GoogleDoc hosted spreadsheet. You can configure ACRA to report silently or to present a dialog to the user and to capture a variety of information including a user comment. I chose a simple Toast notification and posting of the standard set of fields. I also configured the target spreadsheet so that I receive an email the moment it is modified.

Having ACRA embedded meant that I was aware the instant one of my users found the first bug (and believe me, no matter what testing regime you put in place, Android's heterogeneous hardware and OS environment will cause you to miss something). Before 99% of my users had come across the issue I already had a solution and a new version of my app ready for distribution.

Which leads me to the second decision that has paid back its effort ten fold. Its no use having a new version of your app that fixes a killer bug if no one knows the new version exists. So I built in a component that on startup hits my server to find out the latest version of Jumblee (you could probably have it ping the Market instead using the unofficial market-api). If a more recent version is available it displays a dialog letting the user know and asking if they'd like to download the new version now.

This has meant that my user's have kept rolling forward with new versions quite quickly and I'm not swamped with reports of bugs that have long since been fixed.

I know that the Android Market periodically reminds users about new versions of apps, but the timing of those notifications isn't clear to me, and when I'm confronted with a plethora of updates at once (especially when out of wireless coverage) I sometimes clear and ignore the lot. I'm more likely to accept a single update that is relevant to me right now and I think that is the same for my users.

Well, without these 2, I would have been in a world of pain trying to support my app, and wouldn't have as good a market rating as I do. Its not much of an investment for a heap of gain, so I'd heartily recommend you consider using both techniques.

Bon apetit.

Wednesday, May 18, 2011

AsyncTasks where you control the thread resource

In Android the AsyncTask class is designed to perform some long running task such as DB or net access in a background thread with pre and post execute tasks run on a UI thread, to update your user interface.

In theory its great, in practice it can be a trap.

The problem is that up until Honeycomb AsyncTask only has a single execute method that took no params and that the resource on which the task would execute is shared by all other AsyncTask instances. Worse still the threading resource depends on the version of Android. In Donut (1.5 and now again in the no arg execute method Honeycomb) a single thread is shared amongst all AsyncTask instances. From Eclair to Gingerbread an exhaustible thread pool provides the processing.

This means that if your code uses an AsyncTask it can be blocked by code in another AsyncTask task that might be running unbeknownst to you in a library. This was what was happening to me.

I commonly present a managed dialog before starting a long running task that requires the user to wait until completion and remove it after the task has completed. And I use to do so using AsyncTask. So I started to wonder why sometimes my wait dialogs were taking a REALLY long time to be dismissed. It turns out that a library I use was using AsnycTask (legitimately) to perform net access and update the UI in the post-execute. The problem was that if several of these tasks were started they quickly (or immediately in 1.5) exhausted the AsyncTask processing resource and my dialogs took s long time to go away.

The solution is to create an AsyncTask in which I gain more control of the processing resource available. Which is what has occured in Honeycomb where AsyncTask has gained a new execute method that takes an Executor. But my soluiton needs to work for any version of Android.

So here's my SimpleAsyncTask. It provides 2 execute methods. One takes an Executor and uses that to provide processing resources. The other constructs and starts a new Thread to execute the task.
/**
 * A simple representation of an asynchronous task that has pre and post executions that touch the UI.
 * <p>
 *     It is similar to {@link android.os.AsyncTask} except that it defines its own TaskExecutor
 *     instead of sharing one TaskExecutor between all AsyncTasks.
 * </p>
 * <p>
 *     That means that I can control the task queue and the threads executing the tasks.
 * </p>
 * User: William
 * Date: 16/05/11
 * Time: 7:46 PM
 */
public class SimpleAsyncTask {

    private final Handler handler;

    public SimpleAsyncTask() {
        this.handler = new Handler(Looper.getMainLooper());
    }

    /**
     * Executes this SimpleAsyncTask on its own Thread.
     */
    public void execute() {
        onPreExecute();
        final Runnable runnable = getRunnableToExecute();
        new Thread(runnable).start();
    }

    /**
     * Executes this SimpleAsyncTask using the supplied Executor.
     *
     * @param executor  Executor with which to execute this Task.
     */
    public void execute(Executor executor) {
        onPreExecute();
        final Runnable runnable = getRunnableToExecute();
        executor.execute(runnable);
    }

    /**
     * @return Runnable that executes the pre and post block on the MainLooper UI Thread and the background task in another thread.
     */
    private Runnable getRunnableToExecute() {
        return new Runnable() {

            @Override
            public void run() {
                doInBackground();

                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        onPostExecute();
                    }
                });
            }
        };
    }

    protected void onPreExecute() {
    }

    protected void doInBackground() {
    }

    protected void onPostExecute() {
    }
}
Note that it doesn't cater for generic arguments or passing or arguments from the doInBackground to the onPostExecute methods, IMHO that was over engineered. It is simpler to embed them as attributes in your Task subclass.

It also doesn't expose completion states, but if you need this capability it is trivial to add.

And finally  it doesn't cater for cancellation, but again this is trivial to add, and most of the work will be done in your override of doInBackground method in any case.


 Feel free to use it in any way you can.