branding-blog

Androidinous Intentions

So you've acquired an Android Dev Phone 1 (the page goes to great lengths to avoid telling you that it's just an unlocked G1). You've downloaded Eclipse, installed the Android plugin, and created a "MyProject" app that says "Hello World, MyProject".

Now what?

Well, if you've been paying attention, you'll have heard of two (or three) new paradigms:

  • Tasks correspond vaguely to the user's idea of accomplishing something, like "sending an e-mail", and an associated "activity stack" which is hidden from the programmer.
  • Activities correspond loosely to subtasks, like "picking a contact".
  • Intents are issued by activities (like SendEmailActivity) to launch other activities (like ContactPickerActivity).

But that's about it. UI programming is traditionally a pain, which is why I like the iPhone SDK's InterfaceBuilder -- unfortunately, Android UI design is essentially Java-style code-based layout (dominated by boring, square layouts) with XML layout resources to save you some of the tediousness. The nearest equivalent of InterfaceBuilder is DroidDraw, but it's buggy and confusing, and it's easier to just use LinearLayout and RelativeLayout in XML since you need a scalable UI1 -- unlike WYSIWYG web design, you can't just say "best viewed at 1024×768" and ignore the people stuck at 640×480 when the largest common screen size is 320×480.

Intents don't really look integrated, even though that's the whole point -- an Intent usually starts a new full-screen Activity, even if you wanted a "recent contacts" popup menu. The only way to reuse third-party controls (which Android calls views) is to copy-paste code,2 causing code-duplication which they've tried so hard to avoid. And since the included UI widgets are a bit minimalistic, you often need to reinvent the wheel:

  • Want a text field with a clear button? Tough. The Android way is to click-and-hold, select "select all", and press DEL (backspace). I was hammering DEL for days.
  • Want to handle tapping, double-tapping,3 or dragging? Many standard widgets will convert touch events into onClick and onLongClick callbacks, but for a custom control, you're on your own.
  • Unlike on the iPhone,4 very little touch preprocessing occurs, so the touch point is in the wrong place and you have to guess if two taps are close enough to count as a double-click.
  • You need to set a timer for long-clicks and double-clicks, but it's not clear which timer is suitably thread-safe.5 There's an ACTION_CANCEL event which you presumably get on a phone call during a long click, but how about between the first and second click of a double-click? The documentation isn't very helpful.

Documentation is the usual confusing API reference (like Javadoc, but less ugly) with occasional explanatory notes but no corresponding programming guide: Most Android devs will be familiar with the activity lifecycle diagram, but despite having read it, I still found Dan Morrill's presentation useful, only to realise that it was 7 months old and slightly obsolete (onFreeze() no longer exists, for a start).

Some APIs are still fairly primitive, like Google's MapView, which is in a separate library2 even though its images are bundled with the system resources. This is mostly an indicator that it's not all there yet6 -- it contains just enough to support the Maps application, which is less featureful than the iPhone's Maps app.

So what's good about Android?

  • There some good stuff under-the-hood: android.os contains things which would have been nice to have in Java, like CountDownTimer, ConditionVariable, and SystemClock.7
  • There are very few singletons. This lets two Activities share the same Dalvik VM, but also mean you're less likely to end up refactoring global-variable spaghetti.
  • UI programming is still better than on S60:8 Custom controls just work (you don't have to worry about CONE or EIKON or UIKON or AVKON) and you don't have to write your own layout code. There are occasional NullPointerExceptions with unhelpful backtraces, but it's still better than CONE 14.
  • You can actually write background services -- the lack of background processing is one of the main gripes about the iPhone SDK. Sadly, there's no easy way to see what's running down your battery.
  • There's a marketplace. When it supports paid apps, allegedly it'll be on par with the iPhone App Store. There's virtually nothing on other platforms: BREW apps effectively need operator approval, and I don't know anyone who's used Nokia Download.

Ultimately, Android still needs a lot of UI work. Of the apps I've seen, ShopSavvy has the best (and prettiest) UI -- later I realised that it looks and feels like an iPhone app (with a navigation bar and new views sliding in and everything!). They've obviously put a lot of effort into recreating what the iPhone does for you out of the box. Apple must be doing something right.

I'll finish with the usual deluge of footnotes:

  1. People using Android apps expect both portrait and landscape (due to the G1's keyboard) -- and upcoming Android phones will probably have different screen sizes -- so a scalable UI is a must. On the iPhone, you can get away with only supporting 320×480 portrait.
  2. There's a tag for loading libraries which aren't already loaded, but no tag to share your own. The code seems to be loaded from /system/framework which is mounted read-only.
  3. Double-tapping doesn't seem to be a standard UI paradigm on Android; neither is multi-touch, though the G1 hardware supports it. As a result, most UIs rely heavily on the menu button, which is wonderfully intuitive.
  4. The touch point registered by a touch screen is usually somewhere unhelpfully obscured by your finger, so the iPhone does some preprocessing to move the touch point to where you think you touched. It's essential for anything smaller than about 32×32 and impractical for apps to do manually -- counting double-taps manually is comparatively easy.
  5. Ideally you'd request a dummy touch event in 500 ms, automatically cancelled if something significant happens in the intervening time. All of the edge-cases are automatically handled correctly, and you don't need to worry about cancelling the timer.
  6. Your activity must be a MapActivity so it can't be one of the other subclasses. "Only one MapActivity is supported per process." The ItemizedOverlay is confusing and doesn't support long-clicks or dragging OverlayItems. The MapView doesn't produce onLongClick events or onClick events from the touch screen. The result is that you need to write a lot of code to even approach the iPhone's Maps app.
  7. SystemClock.uptimeMillis() is "guaranteed monotonic", but the documentation also notes that "this value may get reset occasionally (before it would otherwise wrap around)". The underlying code seems to take a timespec, convert to nanoseconds, and divide by a million, so it wraps when time_t does (about 68 years, instead of 292 million).
  8. Have you ever tried a custom dialog on Symbian? You need to use a CEikDialog (to get softkeys/menus), add a "dialog line" with a custom control ID, implement a create-custom-control function to convert the ID into a custom control, stick all your controls into that custom control (CEikDialogs only does line-based layout), and override the dialog-sizing to subtract exactly 2 pixels which are inexplicably added to the dialog height.