Compatibility and Android Programming

The delay in upgrades combined with regular new releases makes compatibility an important issue in Android programming. To reach a broad market, Android developers must create apps that perform well on devices running KitKat, Lollipop, Marshmallow, Nougat, and any more recent versions of Android, as well as on different device form factors.

Targeting different sizes of devices is easier than you might think. Phone screens are a variety of sizes, but the Android layout system does a good job at adapting. Tablets require more work, but you can use configuration qualifiers to do the job (as you will see in Chapter 17). However, for Android TV and Android Wear devices (both of which also run Android), the differences in UI are large enough that you need to rethink the user interaction patterns and design of your app.

A sane minimum

The oldest version of Android that the exercises in this book support is API level 19 (KitKat). There are references to legacy versions of Android, but the focus is on what we consider to be modern versions (API level 19+). With the distribution of Gingerbread, Ice Cream Sandwich, and Jelly Bean dropping month by month, the amount of work required to support those older versions eclipses the value they can provide.

Incremental releases cause little problem with backward compatibility. Major versions can be a different story. The work required to support only 5.x devices is not terribly significant. If you also need to support 4.x devices, you will have to spend time working through the differences in those versions. Luckily, Google has provided libraries to ease the pain. You will learn about these libraries in later chapters.

One of the biggest changes for Android developers came with the release of Honeycomb, Android 3.0. This release was a major shift in the platform that introduced a new UI and new architectural components. Honeycomb was released only for tablets, so it was not until Ice Cream Sandwich that these new developments were widely available. Since then, new releases have been more incremental for developers.

Android has provided help for maintaining backward compatibility. There are also third-party libraries that can help. But maintaining compatibility does complicate learning Android programming.

When you created the GeoQuiz project, you set a minimum SDK version within the New Project wizard, as shown in Figure 6.1. (Note that Android uses the terms SDK version and API level interchangeably.)

Figure 6.1  Remember me?

Screenshot shows Create New Project window in Android studio.

In addition to the minimum supported version, you can also set the target version and the build version. Let’s explain the default choices and see how to change them.

All of these properties are set in the build.gradle file in your app module. The build version lives exclusively in this file. The minimum SDK version and target SDK version are set in the build.gradle file, but are used to overwrite or set values in your AndroidManifest.xml.

Open the build.gradle file that exists in your app module. Notice the values for compileSdkVersion, minSdkVersion, and targetSdkVersion.

Listing 6.1  Examining the build configuration (app/build.gradle)

compileSdkVersion 25
buildToolsVersion "25.0.0"

defaultConfig {
    applicationId "com.bignerdranch.android.geoquiz"
    minSdkVersion 19
    targetSdkVersion 25
    ...
}

Minimum SDK version

The minSdkVersion value is a hard floor below which the OS should refuse to install the app.

By setting this version to API level 19 (KitKat), you give Android permission to install GeoQuiz on devices running KitKat or higher. Android will refuse to install GeoQuiz on a device running, say, Jelly Bean.

Looking again at Table 6.1, you can see why API level 19 is a good choice for a minimum SDK version: It allows your app to be installed on more than 80% of devices in use.

Target SDK version

The targetSdkVersion value tells Android which API level your app is designed to run on. Most often this will be the latest Android release.

When would you lower the target SDK? New SDK releases can change how your app appears on a device or even how the OS behaves behind the scenes. If you have already designed an app, you should confirm that it works as expected on new releases. Check the documentation at developer.android.com/​reference/​android/​os/​Build.VERSION_CODES.xhtml to see where problems might arise. Then you can modify your app to work with the new behavior or lower the target SDK.

Not increasing the target SDK when a new version of Android is released ensures that your app will still run with the appearance and behavior of the targeted version on which it worked well. This option exists for compatibility with newer versions of Android, as changes in subsequent releases are ignored until the targetSdkVersion is increased.

Compile SDK version

The last SDK setting is labeled compileSdkVersion in Listing 6.1. This setting is not used to update the AndroidManifest.xml file. Whereas the minimum and target SDK versions are placed in the manifest when you build your app to advertise those values to the OS, the compile SDK version is private information between you and the compiler.

Android’s features are exposed through the classes and methods in the SDK. The compile SDK version, or build target, specifies which version to use when building your own code. When Android Studio is looking to find the classes and methods you refer to in your imports, the build target determines which SDK version it checks against.

The best choice for a build target is the latest API level (currently 25, Nougat). However, you can change the build target of an existing application if you need to. For instance, you might want to update the build target when a new version of Android is released so that you can make use of the new methods and classes it introduces.

You can modify the minimum SDK version, target SDK version, and compile SDK version in your build.gradle file, but note that modification of this file requires that you sync your project with the Gradle changes before they will be reflected. To do this, select ToolsAndroidSync Project with Gradle Files. This will trigger a fresh build of your project with the updated values.

Adding code from later APIs safely

The difference between GeoQuiz’s minimum SDK version and build SDK version leaves you with a compatibility gap to manage. For example, what happens if you call code from an SDK version that is later than the minimum SDK of KitKat (API level 19)? When your app is installed and run on a KitKat device, it will crash.

This used to be a testing nightmare. However, thanks to improvements in Android Lint, potential problems caused by calling newer code on older devices can be caught at compile time. If you use code from a higher version than your minimum SDK, Android Lint will report build errors.

Right now, all of GeoQuiz’s simple code was introduced in API level 19 or earlier. Let’s add some code from API level 21 (Lollipop) and see what happens.

Open CheatActivity.java. In the OnClickListener for the SHOW ANSWER button, add the following code to present a fancy circular animation while hiding the button.

Listing 6.2  Adding activity animation code (CheatActivity.java)

mShowAnswerButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (mAnswerIsTrue) {
            mAnswerTextView.setText(R.string.true_button);
        } else {
            mAnswerTextView.setText(R.string.false_button);
        }
        setAnswerShownResult(true);

        int cx = mShowAnswerButton.getWidth() / 2;
        int cy = mShowAnswerButton.getHeight() / 2;
        float radius = mShowAnswerButton.getWidth();
        Animator anim = ViewAnimationUtils
                .createCircularReveal(mShowAnswerButton, cx, cy, radius, 0);
        anim.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                mShowAnswerButton.setVisibility(View.INVISIBLE);
            }
        });
        anim.start();
    }
});

The createCircularReveal(…) method creates an Animator from a few parameters. First, you specify the View that will be hidden or shown based on the animation. Next, you set a center position for the animation as well as the start radius and end radius of the animation. You are hiding the SHOW ANSWER button, so the radius moves from the width of the button to 0.

Before the newly created animation is started, you set a listener that allows you to know when the animation is complete. Once complete, you will show the answer and hide the button.

Finally, the animation is started and the circular reveal animation will begin. (You will learn much more about animation in Chapter 32.)

The ViewAnimationUtils class and its createCircularReveal(…) method were both added to the Android SDK in API level 21, so this code would crash on a device running a lower version than that.

After you enter the code in Listing 6.2, Android Lint should immediately present you with a warning that the code is not safe on your minimum SDK version. If you do not see a warning, you can manually trigger Lint by selecting AnalyzeInspect Code.... Because your build SDK version is API level 21, the compiler itself has no problem with this code. Android Lint, on the other hand, knows about your minimum SDK version and will complain loudly.

The error messages read something like Call requires API level 21 (Current min is 19). You can still run the code with this warning, but Lint knows it is not safe.

How do you get rid of these errors? One option is to raise the minimum SDK version to 21. However, raising the minimum SDK version is not really dealing with this compatibility problem as much as ducking it. If your app cannot be installed on API level 19 and older devices, then you no longer have a compatibility problem.

A better option is to wrap the higher API code in a conditional statement that checks the device’s version of Android.

Listing 6.3  Checking the device’s build version first (CheatActivity.java)

mShowAnswerButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (mAnswerIsTrue) {
            mAnswerTextView.setText(R.string.true_button);
        } else {
            mAnswerTextView.setText(R.string.false_button);
        }
        setAnswerShownResult(true);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            int cx = mShowAnswerButton.getWidth() / 2;
            int cy = mShowAnswerButton.getHeight() / 2;
            float radius = mShowAnswerButton.getWidth();
            Animator anim = ViewAnimationUtils
                    .createCircularReveal(mShowAnswerButton, cx, cy, radius, 0);
            anim.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    mShowAnswerButton.setVisibility(View.INVISIBLE);
                }
            });
            anim.start();
        } else {
            mShowAnswerButton.setVisibility(View.INVISIBLE);
        }
    }
});

The Build.VERSION.SDK_INT constant is the device’s version of Android. You then compare that version with the constant that stands for the Lollipop release. (Version codes are listed at http://​developer.android.com/​reference/​android/​os/​Build.VERSION_CODES.xhtml.)

Now your circular reveal code will only be called when the app is running on a device with API level 21 or higher. You have made your code safe for API level 19, and Android Lint should now be content.

Run GeoQuiz on a Lollipop or higher device, cheat on a question, and check out your new animation.

You can also run GeoQuiz on a KitKat device (virtual or otherwise). It will not have the circular animation, but you can confirm that the app still runs safely.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.15.189.48