【安卓】Android 应用的版本兼容 了解一下(理解 minSdkVersion、targetSdkVersion)

前言

众所周知,Android 适用于众多类型的设备,从手机到平板电脑和电视都能搭载使用。为了能在所有这些设备上顺利运行,Android 系统在应用到设备上时,必不可少的需要处理与 Android 应用的兼容性问题。这里就牵扯出两个概念:设备兼容性与应用兼容性。

  • 设备兼容性:设备能够正常运行我们编写的 Android 应用。
  • 应用兼容性:针对市面上千奇百怪的 Android 设备,应用是否兼容每一种可能的设备配置。

对于Android 应用开发者来说 ,我们无需担心设备是否兼容 Android,而是更加关注于我们开发的应用能够在尽可能多的 Android 设备上正常运行,即,上面所说的应用兼容性。

而一个应用的兼容性所覆盖的内容较多,从设备功能到平台版本再到屏幕配置,以及针对不同的国家或语言做出的修改,每一部分能涉及相当多的内容。

这篇文章就先介绍最常见的版本兼容性,顺便帮助大家理解 Android 开发中常见的几个版本相关的属性:minSdkVersiontargetSdkVersionmaxSdkVersioncompileSdkVersion

【安卓】Android 应用的版本兼容 了解一下(理解 minSdkVersion、targetSdkVersion)

自己设计版本兼容

在说明 Android 的应用兼容性之前,我们先做这么一个假设:如果我们自己是 Android API 的开发者,为了让更多的 Android 应用能够跑在我们的系统上,我们应该处理版本兼容问题

一、版本号如何确立

这里我们简单地把 Android 框架 API 想象为一个给其他开发者使用的库。如果我们开发了一个库,让别的开发者拿去用,那么第一个问题就来了,那就是版本号的问题。在几乎所有情况下,我们每发一个版本,都会用一个依次增长的整数来表明这个库是什么版本。这里我们也从1开始来发一个版本:

// 版本号 1 的 平台 API

public boolean doSomething() { /*do something ... */ return true;}

public void print(){

System.out.println("hello version 1");

}

好了,那么这就是我们发的版本号为1的库了。里面包含了两个方法,一个返回库的版本号,一个仅仅做了打印的操作。

二、升级能直接修改代码吗

随着时间的推移,我们现在需要升级一下这个库。之前已经确定了版本号为一个不断增加的整数,之前发的版本号为1,那我们现在需要更新版本号为2的库了。

那么问题就来了,我们现在发现之前库中print()这个方法不太好,或者说打印的字符串不太对,那我们要怎么修改。这就牵扯出以下三个问题了:

  • 能不能删除之前版本中的方法
  • 能不能直接修改之前版本中的方法实现
  • 能不能直接修改方法名

很遗憾的说,上面的三个问题的答案都是否定的。如果直接删除print()方法,那么外部使用之前版本的应用一旦升级,就会因为找不到方法而崩溃;同理,修改方法名也是一样的;至于直接修改方法中的实现呢,那造成的后果可能会更加严重。这里如果我们更改了打印的字符串,外部引用会明显的发现这里不对,跟上个版本不一样;如果你说一个字符串还好,那我可以举一个极端的例子,原本这个方法是打开摄像头的,但是下一个版本你改成闪光灯了,这样外部引用此库的应用升级库版本之后是完全无法工作的。

这里就需要确认一个升级库的约定:向后兼容,进行新增更改!

虽然老的方法不能删除,那么我们可以增加新的方法啊,并且为了标识老的方法不再被维护,可以添加@Deprecated注解。于是下面就是我们的版本号为2的库:

//版本号2 的 平台 API

public boolean doSomething() { /*do something ... */ return true;}

@Deprecated

public void print(){

System.out.println("hello version 1");

}

public void printNew() {

System.out.println("hello verson 2");

}

这里在这个版本号为2的库中,增加了一个printNew()方法,并保留了版本1中的所有内容,只不过将print()方法标记为废弃了。

在开发一个依赖库的时候,我们需要只要考虑到这两方面的问题就基本可以解决版本兼容问题。但 Android API 版本并不是一个依赖库,它还需要被安装在各个设备上,因此我们还需要往下讨论几个问题。

三、应用需要告知什么信息

在继续讨论之前,我们先把依赖库的设想放下,而是将我们开发的东西想象为 Android API,它需要被安装在各种设备之上。由于我们刚发了两个版本,那么现在市面上就会有版本号为1和版本号为2的设备,而未来会有更多的版本号。

现在有这么一个应用,它是依赖版本号为 2 的 API 版本开发的,而且在这个应用中使用到了 printNew()方法。那这问题就来了,这个应用能安装在市面上所有的设备上么?显然不能:假如它被安装在一个 API 版本号为 1 设备上,而这个设备上是没有printNew()方法的,这样的话,应用就会因为找不到方法而崩溃。

因此,一个应用,在被安装到设备上时,必须能够告知设备一些信息。在这里,必须告知设备的信息就是应用在开发时是基于哪一个 API 版本进行开发的。但是在第二条里,我们确定了平台 API 的开发必须是新增更改,这就意味着一个应用如果是基于某一个平台版本开发的,那么在这个平台版本后续的版本上也能够完全支持这个应用。在 Android 开发里就是:Android 应用一般向前兼容新版本的 Android 平台,这个我们后面再说。

于是乎我们需要知道并不是这个应用是基于哪个 API 版本进行开发的,而是它最低能跑在哪个 API 版本之上。在这里,由于它使用了在API版本号为1的平台中没有的printNew()方法,因此这个应用只能指定为 2 了。而且由于保证了上面的平台 API 升级的约定,它既然能在版本号 2 上跑,那么它也就能够在 3、4、5... 以及后续的所有平台版本上跑了。

因为这是应用需要告知我们的信息,所以它需要在应用开发时指定,这里我们先命名它为 minSdkVersion,对于 Android 应用,我们就在 AndroidManifest.xml清单文件中指定:

//应用提供的信息

<uses-sdk android:minSdkVersion="2" />

四、提供一个信息就足够了么

现在应用只告诉了平台它能支持的最低版本,那现在我们就需要想一想了,仅仅告知这个信息足够么?

在回答此问题之前,我们来升级下前面写的平台 API。之前的版本为 2,且添加了printNew()方法,并打印了一个字符串。大家知道System.out.println()这个是在Java 中常用的方法,但是在 Android 中,我们常用android.util.Log工具类来打印某些文本。

但是由于之前我们确定了升级API的原则为新增更改,那么就意味着直接修改代码是绝对不行的,否则应用在新的平台上的行为会改变,这可不是我们想看到的。对于这个问题,我们应该怎么办呢?

既然不能直接修改,那么原来代码显然是要保留的,针对老平台编译的使用之前的打印方式,针对新平台那么我们就采用新的打印方式好了。那答案就出来了,我们可以在运行时判断。那么,版本号为 3 的平台 API 就出来了:

//版本号3 的 平台 API

public boolean doSomething() { /*do something ... */ return true;}

@Deprecated

public void print(){

System.out.println("hello version 1");

}

public void printNew() {

if(应用使用的API版本 <= 2) {

System.out.println("hello verson 2");

} else {

Log.d("tag", "hello version > 2");

}

}

通过这个代码我们就知道了,应用仅仅告诉我们它支持的最小 API 是不够的,我们还需要知道应用是基于哪个平台版本开发和测试的,在这里,如果应用是使用 2 版本,那么就用System.out.println,如果用的是之后的版本开发的,那我们就用android.util.Log来打印。这样就可以保证应用跑在任何设备上都是其想要的行为了。

于是,我们需要再定义一个应用针对哪个版本开发和测试的的属性,这里我们将其命名为targetSdkVersion。这样,最终应用的清单文件为:

<uses-sdk android:minSdkVersion="2"

android:targetSdkVersion="3" />

五、版本兼容设计完成

这样看来好像没有其他问题了。那么现在总结一下,我们自己的平台API版本控制有这么四点需要注意的:

  1. 版本号的确立(从1开始增加的整数);
  2. 版本升级的原则,与所有早期版本保持兼容;
  3. 应用需要告知支持的最小平台版本号;
  4. 应用需要告知针对哪个版本进行开发和测试;

如果我们自己构建 API,大概就是这些问题了。

那么接下来,我们就来看看 Android 官方是如何处理这些问题的。
【安卓】Android 应用的版本兼容 了解一下(理解 minSdkVersion、targetSdkVersion)

Android 的版本兼容

依照我们前面设计的四个问题,我们来依照顺序来看 Android 官方是怎么处理的。

Android API 级别

可见,Android 官方的版本号设计也是与我们所设计的版本号类似,都是从 1 开始的整数,并依次增加。官方还给出了Android 平台版本所支持的 API 级别,这里就不贴了,想看的话可以点文末的链接或者去 Android 的官方网站看看。

Android API 级别的兼容性

这里能看出 Android 的版本升级与我们设计的一样,首先就是要保证与早期版本的 API 兼容。在继续讨论应用的兼容性前我们先聊两个概念:

应用向前兼容性

应用向后兼容性

作为应用开发者,通过上面的描述咱们可以简单理解为:一个应用如果能在当前的API级别上跑,那么就可以在以后的API上,但未必能在早期的API上跑。于是乎,为了让平台知道这个应用能不能再自己的这个版本上跑,应用就需要提供一些信息。这就是我们提出的第三和第四个问题了。

Android 应用选择平台版本和 API 级别

首先,我们上面分析过了,应用必须向外面告知minSdkVersiontargetSdkVersion。在Android 上,是这么描述这个两个属性的,以及maxSdkVersion这个属性:

android:minSdkVersion

android:targetSdkVersion

这里通过一张图是最能说明这个属性是怎么用的了:
【安卓】Android 应用的版本兼容 了解一下(理解 minSdkVersion、targetSdkVersion)
这是 AlarmManager的构造和cancel()方法。首先在构造方法中获取到应用指定的targetSdkVersion并存放在mTargetSdkVersion中。在cancel()方法里,Android 会判断应用针对哪个 API 级别开发和测试的。可以看到应用针对新的API级别和老的级别,反应到平台上,其行为是不一样的。

我们在看 Android 的源码时,会经常发现这样的代码,使用方法也类似,从这些代码中就能够看出targetSdkVersion的作用了。

android:maxSdkVersion

总结一下就是不要声明该属性,甚至你可以忘掉这个属性的存在。

compileSdkVersion

至于这个声明,其实不用太在意,它只是我们在查看源代码和编译时才发挥作用的,它与应用兼容性关系不大。它指定了 Gradle 用哪个版本的 API 级别来编译你的应用,这样你在代码里就能够使用这个 API 级别提供的方法和功能。

一般来说我会把这个属性设置为与targetSdkVersion相同,这样在点击查看某个源码时,查看的就是要针对的 API 级别对应的源代码。不过只要compileSdkVersion不低于targetSdkVersion就行了,否则 Android Studio 会有这样的警告:
【安卓】Android 应用的版本兼容 了解一下(理解 minSdkVersion、targetSdkVersion)
另外,如果你需要查看某个版本的 Android 源码,那你也可以更改这个值。例如,你更改compileSdkVersion为28,那从代码点进去查看到的Android源码就是来源于28的;更改为30,那点进去查看的就是30的源码。

现在我们掌握了这个几个属性的作用了吧。作为开发者,理解这几个属性并选取对应的 API 级别是比较重要的。下面就来一下总结。

总结

这篇文章里,我们先自己设想了一下如果自己设计 Android 的版本兼容会是怎么样,并设计解决了发现的4个问题。然后再进入到 Android 官方的设计思维中,并看到 Google 的大佬们是怎么解决这些问题的。并顺便理解了应用版本声明的几个属性 minSdkVersiontargetSdkVersionmaxSdkVersioncompileSdkVersion

关于 Android 的版本兼容,我想,基本理解到这里也就可以了,至少作为应用开发者,我们知道了怎么选minSdkVersiontargetSdkVersion版本号以及它们背后的意义了。
【安卓】Android 应用的版本兼容 了解一下(理解 minSdkVersion、targetSdkVersion)


Android 兼容性-平台版本.
Android API 级别 use-sdk.

以上是 【安卓】Android 应用的版本兼容 了解一下(理解 minSdkVersion、targetSdkVersion) 的全部内容, 来源链接: utcz.com/a/98228.html

回到顶部