參考:
Android targetSdkVersion 原理
如何選擇 compileSdkVersion, minSdkVersion 和 targetSdkVersion
Google 官方文章:Picking your compileSdkVersion, minSdkVersion, and targetSdkVersion
Android Develop
Google 官方發(fā)布文章解析 compileSdkVersion、minSdkVersion 以及 targetSdkVersion 的含義,以及合理設(shè)置各個(gè)值的意義,原文 Picking your compileSdkVersion, minSdkVersion, and targetSdkVersion
compileSdkVersion
compileSdkVersion 告訴 Gradle 用哪個(gè) Android SDK 版本編譯你的應(yīng)用。它純粹只是在編譯的時(shí)候使用。當(dāng)你修改了 compileSdkVersion 的時(shí)候,可能會(huì)出現(xiàn)新的編譯警告、編譯錯(cuò)誤等(你真的應(yīng)該修復(fù)這些警告,他們的出現(xiàn)一定是有原因的)。需要強(qiáng)調(diào)的是修改 compileSdkVersion 不會(huì)改變運(yùn)行時(shí)的行為,compileSdkVersion 并不會(huì)打包進(jìn)APK里只是在編譯時(shí)使用
因此我們強(qiáng)烈推薦總是使用最新的 SDK 進(jìn)行編譯。在自己的代碼上使用最新SDK的編譯檢查(lint?)可以獲得很多好處,可以避免使用最新棄用的 API ,并且為使用新的 API 做好準(zhǔn)備。
注意,如果使用 Support Library ,那么使用最新發(fā)布的 Support Library 就需要使用最新的 SDK 編譯。通常,新版的 Support Library 隨著新的系統(tǒng)版本而發(fā)布,它為系統(tǒng)新增加的 API 和新特性提供兼容性支持。例如,要使用 23.1.1 版本的 Support Library ,compileSdkVersion 就必需至少是 23 (大版本號(hào)要一致?。?。
minSdkVersion
minSdkVersion 是應(yīng)用可以運(yùn)行的最低版本要求。minSdkVersion 是 Google Play 商店用來(lái)判斷用戶設(shè)備是否可以安裝某個(gè)應(yīng)用的標(biāo)志之一。
在開(kāi)發(fā)時(shí) minSdkVersion 也起到一個(gè)重要角色:lint 默認(rèn)會(huì)在項(xiàng)目中運(yùn)行,它在你使用了高于 minSdkVersion 的 API 時(shí)會(huì)警告你,幫你避免調(diào)用不存在的 API 的運(yùn)行時(shí)問(wèn)題。如果只在較高版本的系統(tǒng)上才使用某些 API,通常使用運(yùn)行時(shí)檢查系統(tǒng)版本的方式解決。
請(qǐng)記住,你所使用的庫(kù)可能有他們自己的 minSdkVersion 。你的應(yīng)用設(shè)置的 minSdkVersion 必需大于等于這些庫(kù)的 minSdkVersion 。例如有三個(gè)庫(kù),它們的 minSdkVersion 分別是 4, 7 和 9 ,那么你的 minSdkVersion 必需至少是 9 才能使用它們。在少數(shù)情況下,你仍然想用一個(gè)比你應(yīng)用的 minSdkVersion 還高的庫(kù)(處理所有的邊緣情況,確保它只在較新的平臺(tái)上使用),你可以使用 tools:overrideLibrary 標(biāo)記,但請(qǐng)做徹底的測(cè)試!
targetSdkVersion
compileSdkVersion 和 minSdkVersion 都非常好理解,前者表示編譯的 SDK 版本,后者表示應(yīng)用兼容的最低 SDK 版本。但是對(duì)于 targetSdkVersion 其實(shí)很難一句話解析清楚,原文:The most interesting of the three, however, is targetSdkVersion. 。以前我也有一些迷糊,看到有些人和我有同樣的困惑,本文試圖徹底解決這個(gè)問(wèn)題。
原文:targetSdkVersion is the main way Android provides forward compatibility
targetSdkVersion 是 Android 系統(tǒng)提供前向兼容的主要手段。這是什么意思呢?隨著 Android 系統(tǒng)的升級(jí),某個(gè) API 或者模塊的行為可能會(huì)發(fā)生改變,但是為了保證APK 的行為還是和以前一致。只要 APK 的 targetSdkVersion 不變,即使這個(gè) APK 安裝在新 Android 系統(tǒng)上,其行為還是保持老的系統(tǒng)上的行為,這樣就保證了系統(tǒng)對(duì)老應(yīng)用的前向兼容性。
這里還是用原文的例子,在 Android 4.4 (API 19)以后,AlarmManager 的 set() 和setRepeat() 這兩個(gè) API 的行為發(fā)生了變化。在 Android 4.4 以前,這兩個(gè) API 設(shè)置的都是精確的時(shí)間,系統(tǒng)能保證在 API 設(shè)置的時(shí)間點(diǎn)上喚醒 Alarm。因?yàn)槭‰娫?Android 4.4 系統(tǒng)實(shí)現(xiàn)了 AlarmManager 的對(duì)齊喚醒,這兩個(gè) API 設(shè)置喚醒的時(shí)間,系統(tǒng)都對(duì)待成不精確的時(shí)間,系統(tǒng)只能保證在你設(shè)置的時(shí)間點(diǎn)之后某個(gè)時(shí)間喚醒。
這時(shí),雖然 API 沒(méi)有任何變化,但是實(shí)際上 API 的行為卻發(fā)生了變化,如果 APK 中使用了此 API,并且在應(yīng)用中的行為非常依賴 AlarmManager 在精確的時(shí)間喚醒,例如鬧鐘應(yīng)用。如果 Android 系統(tǒng)不能保證兼容,老的 APK 安裝在新系統(tǒng)手機(jī)上,就會(huì)出現(xiàn)問(wèn)題。
Android 系統(tǒng)是怎么保證這種兼容性的呢?這時(shí)候 targetSdkVersion 就起作用了。APK 在調(diào)用系統(tǒng) AlarmManager 的 set() 或者 setRepeat() 的時(shí)候,系統(tǒng)首先會(huì)查一下調(diào)用的 APK 的 targetSdkVersion 信息,如果小于 19,就還是按照老的行為,即精確設(shè)置喚醒時(shí)間,否者執(zhí)行新的行為。
我們來(lái)看一下 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);
}
看到這里,首選獲取應(yīng)用的 targetSdkVersion,判斷是否是小于 Build.VERSION_CODES.KITKAT (即 API Level 19),來(lái)設(shè)置 mAlwaysExact 變量,表示是否使用精確時(shí)間模式。
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() 傳入不同的參數(shù),從而影響到了 set() 的執(zhí)行行為。具體的實(shí)現(xiàn)在 AlarmManagerService.java,這里就不往下深究了。
看到這里,發(fā)現(xiàn)其實(shí) Android 的 targetSdkVersion 并沒(méi)有什么特別的,系統(tǒng)使用它也非常直接,甚至很“粗糙”。僅僅是用過(guò)下面的 API 來(lái)獲取 targetSdkVersion,來(lái)判斷是否執(zhí)行哪種行為:getApplicationInfo().targetSdkVersion;
所以,我們可以猜測(cè)到,如果 Android 系統(tǒng)升級(jí),發(fā)生這種兼容行為的變化時(shí),一般都會(huì)保存新舊兩種邏輯,并通過(guò) if-else 方法來(lái)判斷執(zhí)行哪種邏輯。果然,在源碼中搜索,我們會(huì)發(fā)現(xiàn)不少類似 getApplicationInfo().targetSdkVersion < Buid.XXXX 這樣的代碼,相對(duì)于浩瀚的 Android 源碼量來(lái)說(shuō),這些還是相對(duì)較少了。其實(shí)原則上,這種會(huì)導(dǎo)致兼容性問(wèn)題的修改還是越少越好,所以每次發(fā)布新的 Android 版本的時(shí)候,Android 開(kāi)發(fā)者網(wǎng)站都會(huì)列出做了哪些改變,在這里,開(kāi)發(fā)者需要特別注意。
最后,我們也可以理解原文中說(shuō)的那句話的含義,明白了為什么修改了 APK 的 targetSdkVersion 行為會(huì)發(fā)生變化,也明白了為什么修改 targetSdkVersion 需要做完整的測(cè)試了。
Gradle和SDK版本
所以設(shè)置正確的 compileSdkVersion, minSdkVersion 和 targetSdkVersion 很重要。

所以編譯時(shí)用到的 compileSdkVersion 是和其他構(gòu)建設(shè)置放在一起的(如上圖的: buildToolsVersion)作為Android 的設(shè)置。其他兩個(gè)稍有不同,他們?cè)跇?gòu)建變體(build variant)的那里聲明。defaultConfig 是所有構(gòu)建變體的基礎(chǔ),也是設(shè)置這些默認(rèn)值的地方。
你可以想象在一個(gè)更復(fù)雜的系統(tǒng)中,應(yīng)用的某些版本可能會(huì)有不同的 minSdkVersion 。
minSdkVersion 和 targetSdkVersion 會(huì)被打包進(jìn)最終的 APK 文件中,如果你查看生成的 AndroidManifest.xml 文件,你會(huì)看到類似下面這樣的標(biāo)簽:
<uses-sdk android:targetSdkVersion="23" android:minSdkVersion="7" />
如果你在 manifest 文件中手工設(shè)置,你會(huì)發(fā)現(xiàn) Gradle 在構(gòu)建時(shí)會(huì)忽略它們(盡管其它構(gòu)建系統(tǒng)如eclipse、ant可能會(huì)依賴它們)。
綜合來(lái)看如果你按照上面示例那樣配置,你會(huì)發(fā)現(xiàn)這三個(gè)值的關(guān)系是:
minSdkVersion <= targetSdkVersion <= compileSdkVersion
理想上,在穩(wěn)定狀態(tài)下三者的關(guān)系應(yīng)該更像這樣:
minSdkVersion (lowest possible) <= targetSdkVersion == compileSdkVersion (latest SDK)
用較低的 minSdkVersion 來(lái)覆蓋最大的人群,用最新的 SDK 設(shè)置 target 和 compile 來(lái)獲得最好的外觀和行為。
附:部分英文原文
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.