Backwards Compatibility with the N Developer Preview
(note: the following is an excerpt from an upcoming update to The Busy Coder’s Guide to Android Development)
In an ideal world, we would develop apps for Android N using the standard approaches for backwards compatibility:
-
setting
minSdkVersion
to the lowest that we support -
using
Build.VERSION.SDK_INT
and the appropriateBuild.VERSION_CODES
value to gracefully degrade on older devices -
testing our app on both old and new devices
When Android N ships in final form, presumably we will be able to do all of that.
However, the N Developer Preview follows in the footsteps of prior developer previews, making it difficult to test apps properly.
Am I On N?
To gracefully degrade on pre-N devices, we need to know whether or not we are running on N. In theory, you would use something like this:
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.N) {
// do something N-specific
}
Alas, this does not work, as in the N Developer Preview:
Build.VERSION.SDK_INT
is 23, the same value as for Android 6.0Build.VERSION_CODES.N
is 10000
Following the approach that Google used for the M Developer Preview,
you can check Build.VERSION.CODENAME
instead:
public static boolean iCanHazN() {
return("N".equals(Build.VERSION.CODENAME));
}
Later on, if and when Build.VERSION.SDK_INT
and
Build.VERSION_CODES.N
start behaving properly, you can replace your
utility method implementation with one that compares the versions more
traditionally.
Running an N Build on Older Environments
If you set your minSdkVersion
to something below 'N'
, your app
continues to build properly and run on N Developer Preview environments.
However, if you try to run it on older environments, you will find that they
will not install the app. This makes it very difficult to test whether
you are handling backwards compatibility properly.
The problem is that the build tools force both minSdkVersion
and
targetSdkVersion
to 'N'
if targetSdkVersion
is set to 'N'
. And,
if your targetSdkVersion
is set lower than 'N'
, you cannot test
behaviors that depend upon targetSdkVersion
, such as
the ban on the file:
Uri
scheme.
You could set up two copies of your project: one configured for an N build and one configured as you had it before the N Developer Preview was released.
Another approach is to reverse the build tools behavior in certain circumstances, such as for a custom build type. This avoids duplicating the projects and perhaps getting stuff out of sync.
Here is an app/build.gradle
file that does this:
apply plugin: 'com.android.application'
android {
compileSdkVersion 'android-N'
buildToolsVersion "24.0.0 rc1"
defaultConfig {
minSdkVersion 15
targetSdkVersion 'N'
}
buildTypes {
bc {
initWith(buildTypes.debug)
applicationIdSuffix '.bc'
}
}
// based on http://stackoverflow.com/a/27372806/115145
applicationVariants.all { variant ->
if (variant.buildType.name=='bc') {
variant.outputs.each { output ->
output.processManifest.doLast {
def manifestOutFile = output.processManifest.manifestOutputFile
def xml = new XmlParser().parse(manifestOutFile)
def usesSdk = xml.'uses-sdk'
usesSdk.replaceNode {
'uses-sdk'('android:minSdkVersion': '15',
'android:targetSdkVersion': '15')
}
def fw = new FileWriter(manifestOutFile.toString())
new XmlNodePrinter(new PrintWriter(fw)).print(xml)
}
}
}
}
}
If you do a debug
build, you will get an N
-native build. If you
do a bc
build instead, you will get 15 for your minSdkVersion
and targetSdkVersion
. Your project could use other values for those
properties, of course, by updating the Groovy replaceNode
closure
and substituting in the values that you want.
Note, though, that Android Studio 1.5.1 does not like the bc
build
type and refuses to run it. Installing the app via a command-line
build (e.g., gradle installBc
) works fine. I have not tested
this on Android Studio 2.x.
There may be other workarounds to these issues, but these are the ones that I found during my initial investigations into the N Developer Preview.