SECRETARÍA GENERAL:
1.2 El Comité Técnico de Ayuda a los Republicanos Españoles, 1939-1940
1.2.1 La recepción de las tres grandes expediciones
I can’t tell you how many times I’ve found the perfect Android SDK class to solve some annoying problem, only to find out that its use is limited to the latest version of the SDK. There is one trick you can use when faced with code that will compile only on later versions of Android: reflection.
While reflection is in no way unique to Android (it’s built into Java), it is some-thing you can use to protect older phones from newfangled classes and methods.
SHAREDPREFERENCES AND APPLY
Long ago, in a galaxy that’s actually quite close, Google figured out that writing to disk on the main thread is a bad thing for performance. During this discovery, they found that the SharedPreferences (something that’s typically used to save user settings and preferences) do actually write to disk when you save them through their commit method. You’ll see what I’m talking about in the following method, which saves a username to the preferences:
public void setUsername(String username){
SharedPreferences prefs =
PreferenceManager.getDefaultSharedPreferences(this);
Editor ed = prefs.edit();
ed.putString(“username”, username);
ed.commit();
}
This works just fine, but as it turns out, commit writes to the disk, and calling this on the main thread is a no-no (for reasons we’ve discussed at length). In SDK version 9, however, Google introduced the apply method to the SharedPreferences Editor class. Again, this is great, but there’s a catch: Any device that tries to use a class containing the apply method will throw a validation exception and crash. So how, you might be wondering, do I use apply on Android SDK 9 (2.3.3) and higher without breaking any 2.2 (or earlier) devices?
ptg7794906 REFLECTING YOUR TROUBLES AWAY
The solution for this problem, and indeed all problems with later declared SDK methods, is to access them using reflection. Reflection allows you to use Java meth-ods without explicitly defining or including them in your code. It’s perfect for handling these kinds of situations.
Ideally, I’d like to call apply if it’s available (SDK 9 and higher) but fall back to commit if apply is going to cause problems. Here’s the new version of setUsername to do exactly that:
public void setUsername(String username){
SharedPreferences prefs =
PreferenceManager.getDefaultSharedPreferences(this);
Editor ed = prefs.edit();
ed.putString(“username”, username);
try{
Method applyMethod =
Editor.class.getDeclaredMethod(“apply”, new Class[]{});
if(applyMethod != null)
applyMethod.invoke(ed, new Object[]{});
else
ed.commit();
}catch(Exception error){
ed.commit();
} }
While this method starts the same as the previous one—getting the Editor and using it to save the string—it diverges when it comes time to save that username.
HANDLING CODE IN OLDER ANDROID VERSIONS 183
ptg7794906 Reflection can throw several different errors (the major one to look out for is
MethodNotFoundException), so the reflection calls will need to be wrapped in a try catch block. I’m first requesting the apply method from the Editor class. If I get it (and it isn’t null, it doesn’t throw an exception, and the lunar phase is exactly right), I’ll then be able to invoke it (again through the reflection call) and we’re done. If it isn’t found as part of the SharedPreferencesEditor class, I’ll fall back on calling commit. Reflection allows me to define and use methods that may not be compiled into earlier versions of the SDK.
ALWAYS KEEP AN EYE ON API LEVELS
In the Android documentation, each class and method has a small gray label read-ing “Since: API Level #” on the right-hand side. If that number is higher than the system you’d like to support, you may need to re-evaluate using that class or method.
Reflection allows you to have the best of both worlds. You can use these lat-est methods on newer devices that support them, while gracefully degrading on devices that don’t.
Keep in mind, however, that reflection is slow and potentially error prone, so use it sparingly and with care. If you’re going to be frequently using a class or method that has two different implementations (Contacts, for example), consider using conditional class loading instead. That is, write two Adapter classes for each version of the class (one for the old, one for the new), and use whichever one is supported. You can always find out which SDK your device is running by checking android.os.Build.Version.SDK.
ptg7794906
WRAPPING UP
In this chapter, you learned how to handle diversity in screen resolution, density, and configuration. You did this through advanced use of the layout folders, the
<include> tag, and Android’s XML layout system. Then you learned how to tell Android which device features you require by putting declarations in the manifest.
Last, you learned about using reflection to take advantage of advanced methods when they’re available and to avoid them when they’re not.
Given all these tools, you should be ready to bring your killer mobile application into play on the tremendous number of devices—from refrigerators to phones to televisions—available to you on the Android platform.
In any case, monotony is boring. Different devices allow for innovation, greater user choice, and funny-looking screen protectors. Now that you’re equipped to handle it, you’ll be scaling resources and rocking the landscape mode with ease.
WRAPPING UP 185