Wednesday, June 17, 2009


I am the alpha dog!

By which I mean, my Android app is now in alpha test: I just uploaded it onto my HTC Magic, and whaddaya know, it runs, it doesn't crash, it actually does stuff. Indeed, I just updated WikiTravel via its interface. (Slightly buggily, but hey, alpha test.)

Mind you, the Android app is a fairly trivial piece of programming next to the AppEngine middleware that does the actual parsing and updating of WikiTravel. Which is the way it should be. Resources are scarce on a smartphone, and both processing and battery power are relatively meagre; I expect the design pattern of choice is going to be "get as much of the work done as you can on your server farm, then communicate simple low-bandwidth stuff to the phone."

Ultimately, the phone isn't much more than a user interface. In fact, now that I think about it, my whole app follows the classic Model-View-Controller design: WikiTravel is the model, the smartphone is the view, and my AppEngine service is the controller. Huh. Plus ça change, plus c'est la même chose.

Anyway, a list of tips, tricks, and annoyances since last we met:

Labels: , , ,

Sunday, June 7, 2009


GQL blues, or, the bloom is off the rose

So I've been doing lots of little stuff of late. I wrote my own Preferences edit screen, only to find that Android comes with an automagic one (sigh, although, I also wish this had been more obvious in the documentation.) I explored most of the mysteries of ExpandableListView, which is a pain to use, but would be an even bigger pain not to use.

And I've built the basic spine of my application - an Android app talking to an AppEngine service, which takes a few well-specified requests and feeds back a few tightly structured (and low-bandwidth) responses. The smartphone app never talks to WikiTravel directly; instead it goes through AppEngine, which can a) do all the heavy lifting, b) log and track what's going on, c) cache data more intelligently, d) provide another layer of control and indirection.

It's actually working pretty well; I'm updating WikiTravel successfully. (Well, from the emulator. I'm going to buy a real phone next week, and I expect to unearth more problems with the app when I do...) The UI is sinfully ugly, and right now it can only show you the well-formatted kind of WikiTravel listings to update, and there are a bunch of niggling things to fix and hardcoded strings to move to R.string and kludges to klean up, but by and large I'm pretty pleased with the progress.

However. I have found the first thing about the Android-AppEngine stack about which I have serious reservations. I'm talking about AppEngine's "GQL" data storage.

Now, on the one hand, this object database is quite cool. You can create, save, and write simple SQL-like queries for objects very easily. It reminds me, pardon me while I date myself, of the good old days of working with GemStone for Smalltalk, a wonderfully elegant (but ahead of its time, and hence cripplingly slow) object database for the world's purest OO language.

But if memory serves you could do a lot more with Gemstone than GQL. Especially when it comes to queries. Now, I'm biased from years of experience with SQL databases - I already know how to make them do what I want, whereas with GQL I have to work it out from a fairly thin foundation - but even so, there is much to be desired.

For instance: you can only have one inequality operator (eg "height < 5") in a query. Which means that "height > 3 AND height < 5" is actually impossible to perform. But wait, there's more; if there's an order by clause as well, it must refer to the field that uses the inequality operator. So "height < 5 ORDER BY emailAddress" is similarly impossible. Oh yes, and fetching more than 1000 rows at a time is equally impossible. As is counting all the rows that match a given WHERE clause. The official workaround for that last is to write a Sharded Counter, which, I think you'll agree, is orders of magnitude more complex than writing "SELECT COUNT(*)".

In general, getting around these restrictions means restructuring your object model heavily ... if you can get around them at all. OK, you denormalize databases for performance, and SQL has its quirks and blind spots too, but jeez, this is way past that: it's more like getting a car and being told "Listen, the steering wheel only turns right, so you have to make three right turns every time you want to go left."

I'm sure they have their reasons. Scaling being one. And for an app like mine, where the data-storage requirements are pretty simple and straightforward, this all works fine. But if I was building something seriously data-intensive? I would think not just twice but thrice before using AppEngine, at least until it supports a more fully-featured datastore.

Labels: ,

Monday, June 1, 2009


zipme, baby

So I've finished the first crude version of my middleware and uploaded it to AppEngine. Looks like I'm going to have to upload the indexes by hand, though - I ran it through its paces before uploading it, but got a NeedIndexError: no matching index found when I tried to run it on Oh well. No biggie. I'll do that tomorrow and then go back to my Android app.

But mostly I wanted to tell you about a fun little app called zipme. AppEngine doesn't let you directly examine the source of the files you've uploaded. However, someone named "manatlan" wrote "zipme", a single python file that you add to your root directory so that you can subsequently download the entirety of your source code, zipped, from AppEngine. See here. (It's configured so you have to be logged in as admin, in case you don't wanna show your source to the world...)

eta: spoke too soon - the indexes are now up n' running. However, JavaScript form handling is not. Well, this is why you deploy early and deploy often, so that you don't get bit by it at the last minute. Goin' on a bug hunt, brb...

etaa: in case you're curious, I realized after five minutes that the culprit was neither AppEngine deployment nor my code: it was the NoScript in my browser.

Labels: , ,

This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]