Supporting Multiple API Versions in Android

Writing Code for Different API Levels

How do you handle changes in the Android Application Programming Interface (API) in code? By placing code that uses changed APIs into a separate class, then wrapping the use of that class in a test. The test is comparing the device’s API level against the code’s target API level. The Build class provides the relevant version numbers to use in the test. However, there are other considerations in supporting multiple API versions. These factors are explored in this article.

Android Robot Logo in the Supporting Multiple API Versions ArticleNew versions of Android provide new classes, additions to existing classes and deprecated classes and methods. (Deprecated classes and methods are those that are no longer required and will be removed in future releases.)  These changes are due to the Android platform continuously evolving to take advantage of new hardware, new ideas and to improve performance.

Reference Documentation API Level Filter

The Android Reference documentation has details on all the classes in all the APIs. Each part of a class has a label to denote in which API level it first appeared. The API documentation can apply a filter based on API level to grey out the classes and parts of classes that were not available prior to a give API version.

Supporting Multiple API Versions in Android

The Android API Differences Report

To see an overview of the changes between an Android API level and the previous API release view the Android API Differences Report. The report is viewed online at the Android Developers web site. The address is http://developer.android.com/sdk/api_diff/X/changes.html where is the API level to examine the differences from the previous level. For example if X is 9 then the differences report show the changes from API level 8 (Froyo) to API level 9 (Gingerbread). A link to each differences report is in the post Android Versions Numbering and API Levels.

Reading the Android Devices API Version

The API version used by an Android device is read from the static Build class in the android.os package. The Build.VERSION.SDK_INT returns the API level. The various API levels are defined in Build.VERSION_CODES.

For example here is some code to test for API level 9 (the first Android Gingerbread release):

The App running this code must have the minSdkVersion attribute in the manifest (AndroidManifest.xml) set to 4 (Donut) or higher. Prior to API level 4 SDK_INT was not available, it was Build.VERSION.SDK, a string. There are very few devices around early than API level 4 so setting the minSdkVersion to 4 or later should not be an issue, however, a workaround is discussed later.

Detect Other Android API Levels

The above code is easily extended to detected other API levels. So to detect API level 8, Froyo, the code becomes:

Assume this code is compiled against the Gingerbread (API level 9) Android Software Development Kit (SDK). There are no errors reported in the IDE. The code to detect API level 10, the maintenance release of Gingerbread, is added:

The line to detect GINGERBREAD_MR1 is giving an unresolved error. There is no way of knowing what future versions of Android will be called, therefore the Android VERSION_CODES list cannot be predefined. To resolve this error the project must be changed to compile against a later version of the Android SDK (level 10 or higher). If the SDK level the project is compiled against is not changed all the code can do is detect that the API level of the device is higher than expected:

The code can be compiled against API level 9 (Gingerbread) and will still work on lower level APIs. Even though older devices, running Android Froyo (API level 8) or lower do not have Build.VERSION_CODES.GINGERBREAD defined. The code does not read Build.VERSION_CODES.GINGERBREAD, instead at compile time the value is stored into the code (because it is defined as a static constant the compiler knows it will never change and can put the value directly into the code). Thus at run time the above code is the same as:

The same can be done manually if there is a need to detect higher API levels without compiling against higher API SDKs, though this has little practical value:

Switch Statement to Detect Multiple Android API Levels

Compiling against SDK level 9 the code to detect all versions between level 4 and 9 can be done using a switch statement:

It is easy enough to extend this for all APIs (see the post Android Versions Numbering and API Levels to see a list of all the various releases) .

No Build.Version.SDK_INT on Cupcake

It is likely than you will not need to consider Cupcake (API level 3) devices when detecting API levels (due to the low number of devices in use). For an App to run on Cupcake the android:minSdkVersion attribute is set to 3. Thus an App compiled against Gingerbread and able to run on all devices from Cupcake upwards would have this uses-sdk element in the AndroidManifest.xml file:

However, Build.VERSION.SDK_INT is not supported in Cupcake so the following gives an error in the IDE despite being compatible with the SDK:

The error can be ignored by adding the annotation @SuppressLint(“NewApi”) before the method that has that code (see Configuring lint checking in Java). The code will run without problems on all devices with an API higher than level 4 (Donut). However, it will generate a java.lang.VerifyError exception on Cupcake devices. This is because Build.VERSION.SDK_INT does not exist on those devices. In other cases when using newer API features errors other than java.lang.VerifyError can occur, for example java.lang.NoSuchMethodError.

API Level Detecting Code on Cupcake Devices

On Cupcake (API level 3) devices the string value Build.VERSION.SDK is used. The code to detect Cupcake is:

However, Build.VERSION.SDK is deprecated for all later API versions.

The Support Library Helps Coding for Multiple API Versions

The problem of non-existent classes or class elements, or changes to classes in later API levels, can cause problems for Apps supporting older devices. Some of the new Android classes have versions available in the Support Library, allowing a subset of newer API features to be supported in older devices. The Support Library is added automatically to new Eclipse or Android Studio Projects. But the Support Library does not included all the newer API features and does not run on Cupcake (API level 3).

Detecting Cupcake and Later API Levels

It has been seen that Build.VERSION.SDK_INT is called to detect API levels, however, for Cupcake devices Build.VERSION.SDK is used. So would this code work on Cupcake devices?:

No, the java.lang.VerifyError exception still occurs. Even though the test ensures that Build.VERSION.SDK_INT is not called on Cupcake devices. Java knows that it is not available on that device when it loads the class. This will occur in other situations. Calls to newer API features can prevent a class from loading even if the call is wrapped in a check for the correct API level. To get round this problem the code accessing the newer features must be moved into a separate class to take advantage of a Java feature, class lazy loading.

Lazy Loading is the Key to Supporting Multiple API Versions

Java tries to be efficient for performance and memory usage reasons. A class is only loaded when it is first used. That means if a class is not accessed it is not loaded. This ability can be used to get round the java.lang.VerifyError exception. Move the code that causes this error into a separate class and use the class where the code was used. In the example above the Build.VERSION.SDK_INT usage is moved to a new class, here called DetectAPI():

Using the new class prevents the java.lang.VerifyError on Cupcake devices:

Integer API Level for Cupcake and Earlier

In most cases Apps will support Android Donut (API level 4) and can uses Build.Version.SDK_INT to test for the API level. If there is a need to support Cupcake and earlier then it is good to have a utility class that allows for a single API level test. First generate a class that only has the integer version of the API level:

This can be used in Cupcake by wrapping it in a test on the Build.VERSION.SDK string. Also by converting that string to an integer a general class can be built that returns an integer for all API levels:

This is can be used to test for any device level:

 Another Example of Supporting Multiple API Versions in Android

Here is the code to get an Android device to operate the internal vibrator for one second (the internal vibrator is used for haptic feedback):

The App requires the VIBRATE permission (<uses-permission android:name=”android.permission.VIBRATE”/>) in the AndroidManifest.xml file. The test for null is for completeness. The call to getSystemService(VIBRATOR_SERVICE) always returns an object, even if the device does not have a vibrator. In Android HoneyComb (API level 11) the Vibrator object was given another method, hasVibrator(). This allows code for the vibrator to be skipped if the device does not support it:

If the minSdkVersion attribute is set to any value lower than 11 then this code produces a java.lang.NoSuchMethod exception. This code can be changed to support older devices. Again the API dependent code is moved to a separate class file:

The API level 11 hasVibrator() call is now separated from the main program and is only accessed if the device is Honeycomb or later:

The code happily runs on all devices from API level 4, even though devices with API levels 4 to 10 do not have the hasVibrator() method. To support Cupcake simply change Build.VERSION.SDK_INT to APILevel.DeviceLevel() and use the classes previously defined.

Conclusion

The Android SDK and Java makes supporting multiple API versions possible, all the way back to the first commercially successful version of Android, Cupcake. With the Java lazy loading of classes an App can add new features if required and still work on older devices. This article lays down the framework to achieve that. For further information it is suggested that the Support Library developer documentation is read and try out the Support Library Samples in The Android SDK.

Leave a Reply

Your email address will not be published. Required fields are marked *

Human Verification: In order to verify that you are a human and not a spam bot, please enter the answer into the following box below based on the instructions contained in the graphic.