Android 中compileSdkVersion, minSdkVersion 和 targetSdkVersion

2,129 阅读6分钟

在Android Studio项目的app/build.gradle中,我们可以看到类似这样一段代码:

compileSdkVersion 27
defaultConfig {
    applicationId "com.glh.fabdemo"
    minSdkVersion 15
    targetSdkVersion 27
    versionCode 1
    versionName "1.0"
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}

其中有compileSdkVersion 、minSdkVersion 、targetSdkVersion 这三个属性比较特殊,本文将进行逐一讲解:

一.compileSdkVersion compileSdkVersion:SDK编译版本

compileSdkVersion 告诉 Gradle 用哪个 Android SDK 版本编译你的应用。使用任何新添加的 API 就需要使用对应 Level 的 Android SDK。

需要强调的是:修改 compileSdkVersion 不会改变运行时的行为。当你修改了 compileSdkVersion 的时候,可能会出现新的编译警告、编译错误,但新的 compileSdkVersion 不会被包含到 APK 中:它纯粹只是在编译的时候使用。(你真的应该修复这些警告,他们的出现一定是有原因的)

因此我们强烈推荐总是使用最新的 SDK 进行编译。在现有代码上使用新的编译检查可以获得很多好处,避免新弃用的 API ,并且为使用新的 API 做好准备。

注意,如果使用 Support Library ,那么使用最新发布的 Support Library 就需要使用最新的 SDK 编译。例如,要使用 23.1.1 版本的 Support Library ,compileSdkVersion 就必需至少是 23 (大版本号要一致!)。通常,新版的 Support Library 随着新的系统版本而发布,它为系统新增加的 API 和新特性提供兼容性支持。

当然,需要说明的是,部分Support Library中Level 不同,虽然调用方法一样,但源码可能有所改变。

比如在 FloatingActionButton基本使用及踩坑记录 中,Level由24升为25后,recyclerView与floatingActionButton滚动显示、隐藏时,由于CoordinatorLayout源码发生改变,调用hide()就会出现隐藏后无法显示。

另外,还有一个buildToolsVersion。它是用于指定项目构建工具的版本.比如27.0.2。如果有更新的版本,Android Studio会进行提示。

二.minSdkVersion 最小的SDK版本。

如果 compileSdkVersion 设置为可用的最新 API,那么 minSdkVersion 则是应用可以运行的最低要求。minSdkVersion 是 各大Android应用商店用来判断用户设备是否可以安装某个应用的标志之一。

在开发时 minSdkVersion 也起到一个重要角色:lint 默认会在项目中运行,它在你使用了低于 minSdkVersion 的 API 时会警告你,帮你避免调用不存在的 API 的运行时问题。如果只在较高版本的系统上才使用某些 API,通常使用运行时检查系统版本的方式解决。

请记住,你所使用的库,如 Support Library 或 Google Play services,可能有他们自己的 minSdkVersion 。你的应用设置的 minSdkVersion 必需大于等于这些库的 minSdkVersion 。例如有三个库,它们的 minSdkVersion 分别是 4, 7 和 9 ,那么你的 minSdkVersion 必需至少是 9 才能使用它们。在少数情况下,你仍然想用一个比你应用的 minSdkVersion 还高的库(处理所有的边缘情况,确保它只在较新的平台上使用),你可以使用 tools:overrideLibrary 标记,但请做彻底的测试!

三.targetSdkVersion 三个版本号中最有趣的就是 targetSdkVersion 了。 targetSdkVersion 是 Android 提供向前兼容的主要依据,在应用的 targetSdkVersion 没有更新之前系统不会应用最新的行为变化。这允许你在适应新的行为变化之前就可以使用新的 API (因为你已经更新了 compileSdkVersion 不是吗?)。

targetSdkVersion指定的值表示你在该目标版本上已经做过了充分的测试,系统将会为你的应用程序启用一些最新的功能和特征。比如,Android 6.0 系统引用了运行时权限这个功能,如果你将targetSdkVersion 指定为23或者更高,那么系统就会为你的程序启动运行时权限。如果你将targetSdkVersion 指定为22,那么就说明你的程序最高只在Android 5.1系统上做过充分的测试,Android6.0系统中引入的新功能就不会启动了。

说通俗一点:比如你将targetSdkVersion 设置为22,涉及某个(或某几个)权限,你直接在mainfest中配置权限即可,然后在java代码中进行获取权限后的逻辑处理即可。 但如果你将targetSdkVersion 设置为23或更高,你除了在mainfest中配置权限外,还需要在java代码中判断用户是否同意权限,如果同意,执行什么业务操作;如果不同意,执行什么业务操作(一般是提示某某权限被拒后,某某功能无法正常使用).如果你只在mainfest中配置了权限,targetSdkVersion 又大于等于23,Java代码中不进行运行时权限的代码,你的应用程序就直接报错了。

看完上面,你的想法是不是:既然这样,那我把targetSdkVersion 设置低一点不就更保险了。

但作为一位有良知且有追求的开发人员,不建议这么做。

拿Android6.0引入的运行时权限来说,在Android6.0以前,用户必须同意所有的权限,才能安装app。但其实,很多权限其实是不合理的。以QQ 7.5.5.3460版本为例,这里它的权限部分截图:

image.png

我在使用QQ过程中,压根就不需要新建/修改通话记录 这个权限,甚至在想"QQ团队要我这个权限干嘛?我就聊聊天,怎么和通话记录"有关呢?

引用6.0运行时权限后,用户不需要在安装软件的时候一次性授权所有申请的权限,而是可以在软件的使用过程中再对某一项权限申请进行授权。还是拿QQ为例,即使我使用某一个功能时需要新建/新建通话记录权限,当时我拒绝了===>带来的结果是这一个功能或几个功能不能使用,但QQ整体还是可以用来愉快聊天的。

所以将 target 更新为最新的 SDK 是所有应用都应该优先处理的事情。

但这不意味着你一定要使用所有新引入的功能,也不意味着你可以不做任何测试就盲目地更新 targetSdkVersion ,请一定在更新 targetSdkVersion 之前做测试!尤其是国产手机各种厂商定制。并且在开发过程中,还有一种可能,产品经理要求你在运行时权限这里有这样的需求:用户拒绝某项权限后,可以直接跳到修改权限的界面(即设置/权限管理这里),或者用户第一次拒绝后,用户第二次操作这个按钮或界面时,再次出现权限的提示。

由于国产手机的多样化,单纯这一个需求,就够你呛的。当然,GitHub上已经有了很多开源的项目,对android各个版本都进行了兼容,且维护较好。

总而言之:关于targetSdkVersion设置的值,需要自己把握度——既要为用户考虑,提升自己的能力,也要避免坑自己。

四.综合来看 如果你细心的话,你会发现这三个值的关系是:

minSdkVersion<=targetSdkVersion<=compileSdkVersion

这种直觉是合理的,如果 compileSdkVersion 是你的最大值,minSdkVersion 是最小值,那么最大值必需至少和最小值一样大且 targetSdkVersion 必需在二者之间。

理想上,在稳定状态下三者的关系应该更像这样:

minSdkVersion (lowest possible) <= targetSdkVersion == compileSdkVersion (latest SDK)

用较低的 minSdkVersion 来覆盖最大的人群,用最新的 SDK 设置 targetSdkVersion 和 compileVersion 来获得最好的外观和行为。