参考:
Android targetSdkVersion 原理
如何选择 compileSdkVersion, minSdkVersion 和 targetSdkVersion
Google 官方文章:Picking your compileSdkVersion, minSdkVersion, and targetSdkVersion
Android Develop
Google 官方发布文章解析 compileSdkVersion、minSdkVersion 以及 targetSdkVersion 的含义,以及合理设置各个值的意义,原文 Picking your compileSdkVersion, minSdkVersion, and targetSdkVersion
compileSdkVersion
compileSdkVersion 告诉 Gradle 用哪个 Android SDK 版本编译你的应用。它纯粹只是在编译的时候使用。当你修改了 compileSdkVersion 的时候,可能会出现新的编译警告、编译错误等(你真的应该修复这些警告,他们的出现一定是有原因的)。需要强调的是修改 compileSdkVersion 不会改变运行时的行为,compileSdkVersion 并不会打包进APK里只是在编译时使用
因此我们强烈推荐总是使用最新的 SDK 进行编译。在自己的代码上使用最新SDK的编译检查(lint?)可以获得很多好处,可以避免使用最新弃用的 API ,并且为使用新的 API 做好准备。
注意,如果使用 Support Library ,那么使用最新发布的 Support Library 就需要使用最新的 SDK 编译。通常,新版的 Support Library 随着新的系统版本而发布,它为系统新增加的 API 和新特性提供兼容性支持。例如,要使用 23.1.1 版本的 Support Library ,compileSdkVersion 就必需至少是 23 (大版本号要一致!)。
minSdkVersion
minSdkVersion 是应用可以运行的最低版本要求。minSdkVersion 是 Google Play 商店用来判断用户设备是否可以安装某个应用的标志之一。
在开发时 minSdkVersion 也起到一个重要角色:lint 默认会在项目中运行,它在你使用了高于 minSdkVersion 的 API 时会警告你,帮你避免调用不存在的 API 的运行时问题。如果只在较高版本的系统上才使用某些 API,通常使用运行时检查系统版本的方式解决。
请记住,你所使用的库可能有他们自己的 minSdkVersion 。你的应用设置的 minSdkVersion 必需大于等于这些库的 minSdkVersion 。例如有三个库,它们的 minSdkVersion 分别是 4, 7 和 9 ,那么你的 minSdkVersion 必需至少是 9 才能使用它们。在少数情况下,你仍然想用一个比你应用的 minSdkVersion 还高的库(处理所有的边缘情况,确保它只在较新的平台上使用),你可以使用 tools:overrideLibrary 标记,但请做彻底的测试!
targetSdkVersion
compileSdkVersion 和 minSdkVersion 都非常好理解,前者表示编译的 SDK 版本,后者表示应用兼容的最低 SDK 版本。但是对于 targetSdkVersion 其实很难一句话解析清楚,原文:The most interesting of the three, however, is targetSdkVersion. 。以前我也有一些迷糊,看到有些人和我有同样的困惑,本文试图彻底解决这个问题。
原文:targetSdkVersion is the main way Android provides forward compatibility
targetSdkVersion 是 Android 系统提供前向兼容的主要手段。这是什么意思呢?随着 Android 系统的升级,某个 API 或者模块的行为可能会发生改变,但是为了保证APK 的行为还是和以前一致。只要 APK 的 targetSdkVersion 不变,即使这个 APK 安装在新 Android 系统上,其行为还是保持老的系统上的行为,这样就保证了系统对老应用的前向兼容性。
这里还是用原文的例子,在 Android 4.4 (API 19)以后,AlarmManager 的 set() 和setRepeat() 这两个 API 的行为发生了变化。在 Android 4.4 以前,这两个 API 设置的都是精确的时间,系统能保证在 API 设置的时间点上唤醒 Alarm。因为省电原因 Android 4.4 系统实现了 AlarmManager 的对齐唤醒,这两个 API 设置唤醒的时间,系统都对待成不精确的时间,系统只能保证在你设置的时间点之后某个时间唤醒。
这时,虽然 API 没有任何变化,但是实际上 API 的行为却发生了变化,如果 APK 中使用了此 API,并且在应用中的行为非常依赖 AlarmManager 在精确的时间唤醒,例如闹钟应用。如果 Android 系统不能保证兼容,老的 APK 安装在新系统手机上,就会出现问题。
Android 系统是怎么保证这种兼容性的呢?这时候 targetSdkVersion 就起作用了。APK 在调用系统 AlarmManager 的 set() 或者 setRepeat() 的时候,系统首先会查一下调用的 APK 的 targetSdkVersion 信息,如果小于 19,就还是按照老的行为,即精确设置唤醒时间,否者执行新的行为。
我们来看一下 Android 4.4 上 AlarmManger 的一部分源代码:
private final boolean mAlwaysExact;
AlarmManager(IAlarmManager service, Context ctx) {
mService = service;
final int sdkVersion = ctx.getApplicationInfo().targetSdkVersion;
mAlwaysExact = (sdkVersion < Build.VERSION_CODES.KITKAT);
}
看到这里,首选获取应用的 targetSdkVersion,判断是否是小于 Build.VERSION_CODES.KITKAT (即 API Level 19),来设置 mAlwaysExact 变量,表示是否使用精确时间模式。
public static final long WINDOW_EXACT = 0;
public static final long WINDOW_HEURISTIC = -1;
private long legacyExactLength() {
return (mAlwaysExact ? WINDOW_EXACT : WINDOW_HEURISTIC);
}
public void set(int type, long triggerAtMillis, PendingIntent operation) {
setImpl(type, triggerAtMillis, legacyExactLength(), 0, operation, null);
}
这里看到,直接影响到 set() 方法给 setImpl() 传入不同的参数,从而影响到了 set() 的执行行为。具体的实现在 AlarmManagerService.java,这里就不往下深究了。
看到这里,发现其实 Android 的 targetSdkVersion 并没有什么特别的,系统使用它也非常直接,甚至很“粗糙”。仅仅是用过下面的 API 来获取 targetSdkVersion,来判断是否执行哪种行为:getApplicationInfo().targetSdkVersion;
所以,我们可以猜测到,如果 Android 系统升级,发生这种兼容行为的变化时,一般都会保存新旧两种逻辑,并通过 if-else 方法来判断执行哪种逻辑。果然,在源码中搜索,我们会发现不少类似 getApplicationInfo().targetSdkVersion < Buid.XXXX 这样的代码,相对于浩瀚的 Android 源码量来说,这些还是相对较少了。其实原则上,这种会导致兼容性问题的修改还是越少越好,所以每次发布新的 Android 版本的时候,Android 开发者网站都会列出做了哪些改变,在这里,开发者需要特别注意。
最后,我们也可以理解原文中说的那句话的含义,明白了为什么修改了 APK 的 targetSdkVersion 行为会发生变化,也明白了为什么修改 targetSdkVersion 需要做完整的测试了。
Gradle和SDK版本
所以设置正确的 compileSdkVersion, minSdkVersion 和 targetSdkVersion 很重要。
所以编译时用到的 compileSdkVersion 是和其他构建设置放在一起的(如上图的: buildToolsVersion)作为Android 的设置。其他两个稍有不同,他们在构建变体(build variant)的那里声明。defaultConfig 是所有构建变体的基础,也是设置这些默认值的地方。
你可以想象在一个更复杂的系统中,应用的某些版本可能会有不同的 minSdkVersion 。
minSdkVersion 和 targetSdkVersion 会被打包进最终的 APK 文件中,如果你查看生成的 AndroidManifest.xml 文件,你会看到类似下面这样的标签:
如果你在 manifest 文件中手工设置,你会发现 Gradle 在构建时会忽略它们(尽管其它构建系统如eclipse、ant可能会依赖它们)。
综合来看如果你按照上面示例那样配置,你会发现这三个值的关系是:
minSdkVersion <= targetSdkVersion <= compileSdkVersion
理想上,在稳定状态下三者的关系应该更像这样:
minSdkVersion (lowest possible) <= targetSdkVersion == compileSdkVersion (latest SDK)
用较低的 minSdkVersion 来覆盖最大的人群,用最新的 SDK 设置 target 和 compile 来获得最好的外观和行为。
附:部分英文原文
compileSdkVersion
compileSdkVersion is your way to tell Gradle what version of the Android SDK to compile your app with. Using the new Android SDK is a requirement to use any of the new APIs added in that level.
It should be emphasized that changing your compileSdkVersion does not change runtime behavior. While new compiler warnings/errors may be present when changing your compileSdkVersion, your compileSdkVersion is not included in your APK: it is purely used at compile time. (You should really fix those warnings though — they were added for a reason!)
Therefore it is strongly recommended that you always compile with the latest SDK. You’ll get all the benefits of new compilation checks on existing code, avoid newly deprecated APIs, and be ready to use new APIs.
Note that if you use the Support Library, compiling with the latest SDK is a requirement for using the latest Support Library releases. For example, to use the 23.1.1 Support Library, you must have a compileSdkVersion of at least 23 (those first numbers need to match!). In general, a new version of the Support Library is released alongside a new platform version, providing compatibility shims to newly added APIs as well as new features.
minSdkVersion
If compileSdkVersion sets the newest APIs available to you, minSdkVersion is the lower bound for your app. The minSdkVersion is one of the signals the Google Play Store uses to determine which of a user’s devices an app can be installed on.
It also plays an important role during development: by default lint runs against your project, warning you when you use any APIs above your minSdkVersion, helping you avoid the runtime issue of attempting to call an API that doesn’t exist. Checking the system version at runtime is a common technique when using APIs only on newer platform versions.
Keep in mind that libraries you use, such as any of the Support Libraries or Google Play services, may have their own minSdkVersion — your app’s minSdkVersion must be at least as high as your dependencies’ minSdkVersion — if you have libraries that require 4, 7, and 9, your minSdkVersion must be at least 9. In rare cases where you want to continue to use a library with a higher minSdkVersion than your app (and deal with all edge cases/ensure the library is only used on newer platform versions), you can use the tools:overrideLibrary marker, but make sure to test thoroughly!
When deciding on a minSdkVersion, you should consider the stats on theDashboards, which give you a global look on all devices that visited the Google Play Store in the prior 7 days — that’s your potential audience when putting an app on Google Play. It is ultimately a business decision on whether supporting an additional 3% of devices is worth the development and testing time required to ensure the best experience.
Of course, if a new API is key to your entire app, then that makes the minSdkVersion discussion quite a bit easier. Just remember that even 0.7% of 1.4 billion devices is a lot of devices.
targetSdkVersion
The most interesting of the three, however, is targetSdkVersion. targetSdkVersion is the main way Android provides forward compatibility by not applying behavior changes unless the targetSdkVersion is updated. This allows you to use new APIs (as you did update your compileSdkVersion right?) prior to working through the behavior changes.
Much of the behavior changes that targetSdkVersion implies are documented directly in the VERSION_CODES, but all of the gory details are also listed on the each releases’ platform highlights, nicely linked in the API Levels table.
For example, the Android 6.0 changes talk through how targeting API 23 transitions your app to the runtime permissions model and the Android 4.4 behavior changes detail how targeting API 19 or higher changes how alarms set with set() and setRepeating() work.
With some of the behavior changes being very visible to users (the deprecation of the menu button, runtime permissions, etc), updating to target the latest SDK should be a high priority for every app. That doesn’t mean you have to use every new feature introduced nor should you blindly update your targetSdkVersion without testing — please, please test before updating your targetSdkVersion! Your users will thank you.