前幾天 Google 官方發佈文章解析 compileSdkVersion、minSdkVersion 以及 targetSdkVersion 的含義,以及合理設置各個值的意義,原文 Picking your compileSdkVersion, minSdkVersion, and targetSdkVersion(後面簡稱 「原文」),還有翻譯版。html
其中,compileSdkVersion 和 minSdkVersion 都很是好理解,前者表示編譯的 SDK 版本,後者表示應用兼容的最低 SDK 版本。可是對於 targetSdkVersion 其實很難一句話解析清楚,原文用了「萬能」的詞 —— interesting 來描述。之前我也有一些迷糊,看到有些人和我有一樣的困惑,本文試圖完全解決這個問題。java
原文是這麼說的:android
targetSdkVersion is the main way Android provides forward compatibilityapp
targetSdkVersion 是 Android 系統提供前向兼容的主要手段。這是什麼意思呢?隨着 Android 系統的升級,某個系統的 API 或者模塊的行爲可能會發生改變,可是爲了保證老 APK 的行爲仍是和之前兼容。只要 APK 的 targetSdkVersion 不變,即便這個 APK 安裝在新 Android 系統上,其行爲仍是保持老的系統上的行爲,這樣就保證了系統對老應用的前向兼容性。ide
這裏仍是用原文的例子,在 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,就仍是按照老的行爲,即精確設置喚醒時間,否者執行新的行爲。ui
咱們來看一下 Android 4.4 上 AlarmManger 的一部分源代碼:google
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 須要作完整的測試了。
寫完這篇文章,再回頭去看一下原文的 targetSdkVersion 那一段,發現做者是說的多麼「滴水不漏」