Android compatibility made easy
Backwards compatibility. If you’re an application developer these words can send chills down your spine. You want to take advantage of the newest features available as your platform evolves, but you don’t want to abandon users running older OS versions. At best, this means extra work; at worst, maintaining entirely different codebases. If you’re an Android developer, here’s how to keep that extra work to a minimum.
There are two documented options for writing compatibile code, published on the Android Developers blog from 2009. Here’s the short version:
- Option 1 - Reflection: Use
class.getMethodto get a handle to any potentially incompatible methods. If the class isn’t available, a
NoSuchMethodExceptioncan be caught to execute a second code path.
- Option 2 - Wrapper class: Put all of your incompatible code inside one class, then reference it inside a second class. This keeps potentially incompatible method calls outside of your core application code.1
In both cases, I’m trying to avoid a side effect of Dalvik’s bytecode verifier. Any invalid method access would cause a class to be marked as invalid by the classloader, and would be discarded before it even has a chance to run. If you didn’t take precautions — or made a mistake — your core application code could get discarded. Yikes!
To make things simpler Android 2.0 modified the way the verifier works so that invalid method access no longer causes classes to be discarded. Instead, the access is just replaced with a
throw opcode. This would still cause a crash when the method access occurs, only it would be catchable.
There’s a complicated way to get around this that takes advantage of the fact that the verifier is lazy, and only runs when a class is first accessed. Architecturally, it’s quite elegant.
But it turns out almost nobody uses anything less than Android 2.2 anymore2, which means I can actually use this new behavior to my advantage to make something that’s not just elegant, but elegantly simple:
Yup, that’s it. Just a simple if-statement.
Since the verifier now converts invalid method calls into exceptions, I can jump over the offending lines. (
SDK_INT was introduced in Android 1.6 and
Build.VERSION_CODES values are decoded at compile time, so neither of those will cause any prolems either.)
So relax, backwards compatibility doesn’t need to be scary.