### **[Android 8.0 行為變更](https://developer.android.com/about/versions/oreo/android-8.0-changes.html)**
**API 差異**
[API 25 至 26 (O DP3)](https://developer.android.com/sdk/api_diff/26/changes.html)
**另請參閱**
[Android 8.0 功能和 API](https://developer.android.com/preview/api-overview.html)
Android 8.0 除了提供諸多新特性和功能外,還對系統和 API 行為做出了各種變更。本文重點介紹您應該了解并在開發應用時加以考慮的一些主要變更。
其中大部分變更會影響所有應用,而不論應用針對的是何種版本的 Android。不過,有幾項變更僅影響針對 Android 8.0 的應用。為清楚起見,本頁面分為兩個部分:針對所有 API 級別的應用和針對 Android 8.0 的應用。
### **針對所有 API 級別的應用**
這些行為變更適用于 在 Android 8.0 平臺上運行的 所有應用,無論這些應用是針對哪個 API 級別構建。所有開發者都應查看這些變更,并修改其應用以正確支持這些變更(如果適用)。
#### **后臺執行限制**
Android 8.0 為提高電池續航時間而引入的變更之一是,當您的應用進入已緩存狀態時,如果沒有活動的組件,系統將解除應用具有的所有喚醒鎖。
此外,為提高設備性能,系統會限制未在前臺運行的應用的某些行為。具體而言:
* 現在,在后臺運行的應用對后臺服務的訪問受到限制。
* 應用無法使用其清單注冊大部分隱式廣播(即,并非專門針對此應用的廣播)。
默認情況下,這些限制僅適用于針對 O 的應用。不過,用戶可以從 Settings 屏幕為任意應用啟用這些限制,即使應用并不是以 O 為目標平臺。
Android 8.0 還對特定函數做出了以下變更:
* 如果針對 Android 8.0 的應用嘗試在不允許其創建后臺服務的情況下使用 startService() 函數,則該函數將引發一個 IllegalStateException。
* 新的 Context.startForegroundService() 函數將啟動一個前臺服務。現在,即使應用在后臺運行,系統也允許其調用 Context.startForegroundService()。不過,應用必須在創建服務后的五秒內調用該服務的 startForeground() 函數。
如需了解詳細信息,請參閱后臺執行限制。
#### **Android 后臺位置限制**
為節約電池電量、保持良好的用戶體驗和確保系統健康運行,在運行 Android 8.0 的設備上使用后臺應用時,降低了后臺應用接收位置更新的頻率。此行為變更會影響包括 Google Play 服務在內的所有接收位置更新的應用。
此類變更會影響以下 API:
* Fused Location Provider (FLP)
* Geofencing
* GNSS Measurements
* Location Manager
* Wi-Fi Manager
為確保您的應用按預期方式運行,請完成以下步驟:
* 查看您的應用的邏輯,并確保您使用的是最新的位置 API。
* 測試您的應用是否在每個用例中都表現出預期行為。
* 考慮使用 Fused Location Provider (FLP) 或地理圍欄來處理依賴于用戶當前位置的用例。
如需了解此類變更的詳細信息,請參閱后臺位置限制。
#### **應用快捷鍵**
Android 8.0 對應用快捷方式做出了以下變更:
* com.android.launcher.action.INSTALL_SHORTCUT 廣播不再會對您的應用有任何影響,因為它現在是私有的隱式廣播。相反,您應使用 ShortcutManager 類中的 requestPinShortcut() 函數創建應用快捷方式。
* 現在,ACTION_CREATE_SHORTCUT Intent 可以創建可使用 ShortcutManager 類進行管理的應用快捷方式。此 Intent 還可以創建不與 ShortcutManager 交互的舊版啟動器快捷方式。在以前,此 Intent 只能創建舊版啟動器快捷方式。
* 現在,使用 requestPinShortcut() 創建的快捷方式和在處理 ACTION_CREATE_SHORTCUT Intent 的操作組件中創建的快捷方式均已轉換為功能齊全的應用快捷方式。因此,應用現在可以使用 ShortcutManager 中的函數來更新這些快捷方式。
* 舊版快捷方式仍然保留了它們在舊版 Android 中的功能,但您必須在應用中手動將它們轉換成應用快捷方式。
如需了解有關應用快捷方式變更的更多信息,請參閱固定快捷方式和微件預覽功能指南。
#### **語言區域和國際化**
Android 7.0(API 級別 24)引入能指定默認類別語言區域的概念,但是某些 API 在本應使用默認 DISPLAY 類別語言區域時,仍然使用不帶參數的通用 Locale.getDefault() 函數。現在,在 Android 8.0 中,以下函數使用 Locale.getDefault(Category.DISPLAY) 來代替 Locale.getDefault():
* Currency.getDisplayName()
* Currency.getSymbol()
* Locale.getDisplayScript()
當為 Locale 參數指定的 displayScript 值不可用時,Locale.getDisplayScript(Locale) 同樣回退到 Locale.getDefault()。
與語言區域和國際化有關的其他變更如下:
* 調用 Currency.getDisplayName(null) 會引發 NullPointerException,以與文檔規定的行為保持一致。
* 時區名稱的分析方法發生變化。之前,Android 設備使用在啟動時取樣的系統時鐘值,緩存用于分析日期時間的時區名稱。因此,如果在啟動時或其他較為罕見的情況下系統時鐘出錯,可能對分析產生負面影響。
* 現在,一般情況下,在分析時區名稱時分析邏輯將使用 ICU 和當前系統時鐘值。此項變更可提供更加準確的結果,如果您的應用使用 SimpleDateFormat 等類,此結果可能與之前的 Android 版本不同。
* Android 8.0 將 ICU 的版本更新至版本 58
#### **提醒窗口**
如果應用使用 SYSTEM_ALERT_WINDOW 權限并且嘗試使用以下窗口類型之一來在其他應用和系統窗口上方顯示提醒窗口:
* TYPE_PHONE
* TYPE_PRIORITY_PHONE
* TYPE_SYSTEM_ALERT
* TYPE_SYSTEM_OVERLAY
* TYPE_SYSTEM_ERROR
...那么,這些窗口將始終顯示在使用 TYPE_APPLICATION_OVERLAY 窗口類型的窗口下方。如果應用針對的是 Android 8.0,則應用會使用 TYPE_APPLICATION_OVERLAY 窗口類型來顯示提醒窗口。
如需了解詳細信息,請參閱針對 Android 8.0 的應用的行為變更內的提醒窗口的常用窗口類型部分。
#### **輸入和導航**
隨著 Android 應用出現在 Chrome 操作系統和平板電腦等其他大尺寸設備上,我們看到,用戶在 Android 應用中又重新開始使用鍵盤導航。在 Android 8.0 中,我們又再次使用鍵盤作為導航輸入設備,從而為基于箭頭鍵和 Tab 鍵的導航構建了一種更可靠并且可預測的模型。
尤其要指出的是,我們對元素焦點行為做出以下變更:
* 現在,如果您沒有為 View 對象(前景或背景圖片)定義任何焦點狀態顏色,框架會為 View 設置默認的焦點突出顯示顏色。此焦點突出顯示標志是基于操作組件主題背景的漣漪圖片。
如果您不希望 View 對象在接收焦點時使用此默認突出顯示標志,請在包含 View 的布局 XML 文件中將 android:defaultFocusHighlightEnabled 屬性設置為 false,或者將 false 傳遞至應用界面邏輯中的 setDefaultFocusHighlightEnabled()。
* 要測試鍵盤輸入對界面元素焦點有何影響,您可以啟用 Drawing > Show layout bounds 開發者選項。在 Android 8.0 中,此選項在當前具有焦點的元素上顯示一個“X”圖標。
另外,Android 8.0 中的所有工具欄元素自動組成鍵盤導航鍵區,用戶可以更加輕松地導航進入和離開每個作為一個整體的工具欄。
如需詳細了解如何在您的應用中改善對鍵盤導航的支持,請閱讀支持鍵盤導航指南。
#### **網頁表單自動填充**
現在,Android 自動填充框架提供對自動填充功能的內置支持,對于安裝到運行 Android 8.0 的設備上的應用,與 WebView 對象相關的下列函數已經發生變化:
**WebSettings**
* getSaveFormData() 函數現在返回 false。之前,此函數返回 true。
* 調用 setSaveFormData() 不再有任何效果。
**WebViewDatabase**
* 調用 clearFormData() 不再有任何效果。
* hasFormData() 函數現在返回 false。之前,當表單包含數據時,此函數返回 true。
#### **無障礙功能**
現在,無障礙服務可識別應用的 TextView 對象內部的所有 ClickableSpan 實例。
如需了解有關如何讓您的應用更便于訪問的更多信息,請參閱無障礙功能。
#### **網絡連接和 HTTP(S) 連接**
Android 8.0 對網絡連接和 HTTP(S) 連接行為做出了以下變更:
* 無正文的 OPTIONS 請求具有 Content-Length: 0 標頭。之前,這些請求沒有 Content-Length 標頭。
* HttpURLConnection 在包含斜線的主機或頒發機構名稱后面附加一條斜線,使包含空路徑的網址規范化。例如,它將 http://example.com 轉化為 http://example.com/。
* 通過 ProxySelector.setDefault() 設置的自定義代理選擇器僅針對所請求的網址(架構、主機和端口)。因此,僅可根據這些值選擇代理。傳遞至自定義代理選擇器的網址不包含所請求的網址的路徑、查詢參數或片段。
* URI 不能包含空白標簽。
之前,平臺支持一種權宜方法,即允許主機名稱中包含空白標簽,但這是對 URI 的非法使用。此權宜方法只是為了確保與舊版 libcore 兼容。開發者如果對 API 使用不當,將會看到一條 ADB 消息:“URI example..com 的主機名包含空白標簽。此格式不正確,將不被未來的 Android 版本所接受。”Android 8.0 廢除了此權宜方法;系統對格式錯誤的 URI 會返回 null。
* Android 8.0 在實現 HttpsURLConnection 時不會執行不安全的 TLS/SSL 協議版本回退。
* 對隧道 HTTP(S) 連接處理進行了如下變更:
* 在通過連接建立隧道 HTTP(S) 連接時,系統會在 Host 行中正確放置端口號 (:443) 并將此信息發送至中間服務器。之前,端口號僅出現在 CONNECT 行中。
* 系統不再將隧道連接請求中的 user-agent 和 proxy-authorization 標頭發送至代理服務器。
在建立隧道時,系統不再將隧道 Http(s)URLConnection 中的 proxy-authorization 標頭發送至代理。相反,由系統生成 proxy-authorization 標頭,在代理響應初始請求發送 HTTP 407 后將其發送至此代理。
同樣地,系統不再將 user-agent 標頭由隧道連接請求復制到建立隧道的代理請求。相反,庫為此請求生成 user-agent 標頭。
* 如果之前執行的 connect() 函數失敗,send(java.net.DatagramPacket) 函數將會引發 SocketException。
* 如果存在內部錯誤,DatagramSocket.connect() 會引發 pendingSocketException。對于 Android 8.0 之前的版本,即使 send() 調用成功,后續的 recv() 調用也會引發 SocketException。為確保一致性,現在這兩個調用均會引發 SocketException。
* 在回退到 TCP Echo 協議之前,InetAddress.isReachable() 會嘗試執行 ICMP。
* 對于某些屏蔽端口 7 (TCP Echo) 的主機(例如 google.com),如果它們接受 ICMP Echo 協議,現在也許能夠訪問它們。
* 對于確實無法訪問的主機,此項變更意味著調用需要兩倍的時間才能返回結果。
#### **藍牙**
Android 8.0 對 ScanRecord.getBytes() 函數檢索的數據長度做出以下變更:
* getBytes() 函數對于所接收的字節數不作任何假定。因此,應用不應受所返回的任何最小或最大字節數的影響。相反,應用應當計算所返回數組的長度。
* 兼容藍牙 5 的設備返回的數據長度可能會超出之前最大約 60 個字節的限制。
* 如果遠程設備未提供掃描響應,則也可能返回少于 60 個字節的數據。
#### **無縫連接**
Android 8.0 對 WLAN 設置進行了多項改進,這樣可以更輕松地選擇能夠提供最佳用戶體驗的 WLAN 網絡。具體變更包括:
* 穩定性和可靠性改進。
* 更加直觀的界面。
* 一個合并的 WLAN 首選項菜單。
* 當附近存在優質的已保存網絡時在兼容設備上自動激活 WLAN。
#### **安全性**
Android 8.0 包含以下與安全性有關的變更:
* 此平臺不再支持 SSLv3。
* 在與未正確實現 TLS 協議版本協商的服務器建立 HTTPS 連接時,HttpsURLConnection 不再嘗試回退到之前的 TLS 協議版本并重試的權宜方法。
* Android 8.0 將使用安全計算 (SECCOMP) 過濾器來過濾所有應用。允許的系統調用列表僅限于通過 bionic 公開的系統調用。此外,還提供了其他幾個后向兼容的系統調用,但我們不建議使用這些系統調用。
* 現在,您的應用的 WebView 對象將在多進程模式下運行。網頁內容在獨立的進程中處理,此進程與包含應用的進程相隔離,以提高安全性。
* 您無法再假定 APK 駐留在名稱以 -1 或 -2 結尾的目錄中。應用應使用 sourceDir 獲取此目錄,而不能直接使用目錄格式。
* 如需了解與使用原生庫有關的安全性增強的信息,請參閱原生庫。
有關提升應用安全性的其他準則,請參閱面向 Android 開發者的安全性。
#### **隱私性**
Android 8.0 對平臺做出了以下與隱私性有關的變更。
* 現在,平臺改變了標識符的處理方式。
* 對于在 OTA 之前安裝到某個版本 Android 8.0(API 級別 26)的應用,除非在 OTA 后卸載并重新安裝,否則 ANDROID_ID 的值將保持不變。要在 OTA 后在卸載期間保留值,開發者可以使用密鑰/值備份關聯舊值和新值。
* 對于安裝在運行 Android 8.0 的設備上的應用,ANDROID_ID 的值現在將根據應用簽署密鑰和用戶確定作用域。應用簽署密鑰、用戶和設備的每個組合都具有唯一的 ANDROID_ID 值。因此,在相同設備上運行但具有不同簽署密鑰的應用將不會再看到相同的 Android ID(即使對于同一用戶來說,也是如此)。
* 只要簽署密鑰相同(并且應用未在 OTA 之前安裝到某個版本的 O),ANDROID_ID 的值在軟件包卸載或重新安裝時就不會發生變化。
* 即使系統更新導致軟件包簽署密鑰發生變化,ANDROID_ID 的值也不會變化。
要借助一個簡單的標準系統實現應用獲利,請使用廣告 ID。廣告 ID 是 Google Play 服務針對廣告服務提供的唯一 ID,此 ID 可由用戶重置。
* 查詢 net.hostname 系統屬性返回的結果為空。
#### **記錄未捕獲的異常**
如果某個應用安裝的 Thread.UncaughtExceptionHandler 未移交給默認的 Thread.UncaughtExceptionHandler,則當出現未捕獲的異常時,系統不會終止應用。從 Android 8.0 開始,在此情況下系統將記錄異常堆棧跟蹤情況;在之前的平臺版本中,系統不會記錄異常堆棧跟蹤情況。
我們建議,自定義 Thread.UncaughtExceptionHandler 實現始終移交給默認處理程序處理;遵循此建議的應用不受 Android 8.0 此項變更的影響。
#### **聯系人提供程序使用情況統計方法的變更**
在之前版本的 Android 中,聯系人提供程序組件允許開發者獲取每個聯系人的使用情況數據。此使用情況數據揭示了與某個聯系人相關聯的每個電子郵件地址和每個電話號碼的信息,包括與該聯系人聯系的次數以及上次聯系該聯系人的時間。請求 READ_CONTACTS 權限的應用可以讀取此數據。
如果應用請求 READ_CONTACTS 權限,它們仍可以讀取此數據。從 Android 8.0 開始,使用情況數據查詢會返回近似值,而不是精確值。不過,Android 系統內部仍然會保留精確值,因此,此變更不會影響 auto-complete API。
此行為變更會影響以下查詢參數:
* TIMES_CONTACTED
* TIMES_USED
* LAST_TIME_CONTACTED
* LAST_TIME_USED
#### **集合的處理**
現在,AbstractCollection.removeAll() 和 AbstractCollection.retainAll() 始終引發 NullPointerException;之前,當集合為空時不會引發 NullPointerException。此項變更使行為符合文檔要求。
#### **Android 企業版**
Android 8.0 更改了企業應用(包括設備規范控制器 (DPC))的某些 API 和功能的行為。這些變更包括:
* 新增多種行為,幫助應用支持完全托管設備中的工作資料。
* 變更系統更新處理、應用驗證和身份驗證方式,以提高設備和系統的完整性。
* 改進用戶在配置、通知、“最近使用的應用”屏幕和 Always on VPN 方面的體驗。
如需查看 Android 8.0 中的所有企業版變更和了解它們可能給您的應用帶來的影響,請閱讀企業中的 Android。
### **針對 Android 8.0 的應用**
這些行為變更專門應用于針對 O 平臺或更高平臺版本的應用。針對 Android 8.0 或更高平臺版本進行編譯,或將 targetSdkVersion 設為 Android 8.0 或更高版本的應用開發者必須修改其應用以正確支持這些行為(如果適用)。
#### **提醒窗口**
使用 SYSTEM_ALERT_WINDOW 權限的應用無法再使用以下窗口類型來在其他應用和系統窗口上方顯示提醒窗口:
* TYPE_PHONE
* TYPE_PRIORITY_PHONE
* TYPE_SYSTEM_ALERT
* TYPE_SYSTEM_OVERLAY
* TYPE_SYSTEM_ERROR
相反,應用必須使用名為 TYPE_APPLICATION_OVERLAY 的新窗口類型。
使用 TYPE_APPLICATION_OVERLAY 窗口類型顯示應用的提醒窗口時,請記住新窗口類型的以下特性:
* 應用的提醒窗口始終顯示在狀態欄和輸入法等關鍵系統窗口的下面。
* 系統可以移動使用 TYPE_APPLICATION_OVERLAY 窗口類型的窗口或調整其大小,以改善屏幕顯示效果。
* 通過打開通知欄,用戶可以訪問設置來阻止應用顯示使用 TYPE_APPLICATION_OVERLAY 窗口類型顯示的提醒窗口。
#### **內容變更通知**
Android 8.0 更改了 ContentResolver.notifyChange() 和 registerContentObserver(Uri, boolean, ContentObserver) 在針對 Android 8.0 的應用中的行為方式。
現在,這些 API 需要在所有 URI 中為頒發機構定義一個有效的 ContentProvider。使用相關權限定義一個有效的 ContentProvider 可幫助您的應用防范來自惡意應用的內容變更,并防止將可能的私密數據泄露給惡意應用。
#### **視圖焦點**
可點擊的 View 對象現在默認也可以成為焦點。如果您希望 View 對象可點擊但不可成為焦點,請在包含 View 的布局 XML 文件中將 android:focusable 屬性設置為 false,或者將 false 傳遞至應用界面邏輯中的 setFocusable()。
#### **安全性**
如果您的應用的網絡安全性配置選擇退出對明文流量的支持,那么您的應用的 WebView 對象無法通過 HTTP 訪問網站。每個 WebView 對象必須轉而使用 HTTPS。
有關提升應用安全性的其他準則,請參閱面向 Android 開發者的安全性。
#### **帳號訪問和可檢測性**
除非身份驗證器擁有用戶帳號或用戶授予訪問權限,否則,應用將無法再訪問用戶帳號。僅擁有 GET_ACCOUNTS 權限尚不足以訪問用戶帳號。要獲得帳號訪問權限,應用應使用 AccountManager.newChooseAccountIntent() 或特定于身份驗證器的函數。獲得帳號訪問權限后,應用可以調用 AccountManager.getAccounts() 來訪問帳號。
Android 8.0 已棄用 LOGIN_ACCOUNTS_CHANGED_ACTION。相反,應用在運行時應使用 addOnAccountsUpdatedListener() 獲取帳號更新信息。
有關新增 API 和增加的帳號訪問和可檢測性函數的信息,請參閱此文檔的“新增 API”部分中的帳號訪問和可檢測性。
#### **隱私性**
以下變更影響 Android 8.0 的隱私性。
* 系統屬性 net.dns1、net.dns2、net.dns3 和 net.dns4 不再可用,此項變更可加強平臺的隱私性。
* 要獲取 DNS 服務器之類的網絡連接信息,具有 ACCESS_NETWORK_STATE 權限的應用可以注冊 NetworkRequest 或 NetworkCallback 對象。這些類在 Android 5.0(API 級別 21)及更高版本中提供。
* Build.SERIAL 已棄用。需要知道硬件序列號的應用應改為使用新的 Build.getSerial() 函數,該函數要求具有 READ_PHONE_STATE 權限。
* LauncherApps API 不再允許工作資料應用獲取有關主個人資料的信息。當某個用戶在托管配置文件中時,LauncherApps API 的行為就像同一配置文件組的其他配置文件中未安裝任何應用一樣。和之前一樣,嘗試訪問無關聯的個人資料會引發 SecurityExceptions。
#### **權限**
在 Android 8.0 之前,如果應用在運行時請求權限并且被授予該權限,系統會錯誤地將屬于同一權限組并且在清單中注冊的其他權限也一起授予應用。
對于針對 Android 8.0 的應用,此行為已被糾正。系統只會授予應用明確請求的權限。然而,一旦用戶為應用授予某個權限,則所有后續對該權限組中權限的請求都將被自動批準。
例如,假設某個應用在其清單中列出 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE。應用請求 READ_EXTERNAL_STORAGE,并且用戶授予了該權限。如果該應用針對的是 API 級別 24 或更低級別,系統還會同時授予 WRITE_EXTERNAL_STORAGE,因為該權限也屬于同一 STORAGE 權限組并且也在清單中注冊過。如果該應用針對的是 Android 8.0,則系統此時僅會授予 READ_EXTERNAL_STORAGE;不過,如果該應用后來又請求 WRITE_EXTERNAL_STORAGE,則系統會立即授予該權限,而不會提示用戶。
#### **媒體**
* 框架會執行音頻閃避。進行 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 時,應用不會失去焦點。新的 API 適用于需要暫停而不是閃避的應用。請注意,此行為無法在 Android 8.0 1 版本中實現。
* 當用戶打電話時,活動的媒體流將在通話期間靜音。
* 所有與音頻相關的 API 都應使用 AudioAttributes 而不是音頻流類型來說明音頻播放用例。僅為音量控制繼續使用音頻流類型。流類型(例如,已棄用的 AudioTrack constructor)的其他用途仍然有效,但是系統會將其記錄為錯誤。
* 使用 AudioTrack 時,如果應用請求了足夠大的音頻緩沖區,則框架將嘗試使用深度緩沖區輸出(如果可用)。
* 在 Android 8.0 中,媒體按鈕事件的處理有所不同:
1. 在界面操作組件中處理媒體按鈕未發生變化:前臺操作組件在處理媒體按鈕時仍然優先。
2. 如果前臺操作組件不處理媒體按鈕,系統會將媒體按鈕路由到最近在本地播放音頻的應用。在確定哪些應用接收媒體按鈕事件時,不再考慮活動狀態、標志和媒體會話的播放狀態。即使在應用調用 setActive(false) 后,媒體會話仍然可以接收媒體按鈕事件。
3. 如果應用的媒體會話已經釋放,系統會將媒體按鈕事件發送到應用的 MediaButtonReceiver(如果有)。
4. 對于任何其他情況,系統都會舍棄媒體按鈕事件。與其開始播放錯誤的應用,不如不播放任何東西。
下圖匯總了新的媒體按鈕路由邏輯。
媒體按鈕路由
#### **原生庫**
在針對 Android 8.0 的應用中,如果原生庫包含任何可寫且可執行的加載代碼段,則不會再加載原生庫。倘若某些應用的原生庫包含不正確的加載代碼段,則此變更可能會導致這些應用停止工作。這是一種安全加強措施。
如需了解詳細信息,請參閱可寫且可執行的代碼段。
與早期的開發者預覽版相同,Android 8.0 還有助于更輕松地發現所有與鏈接器有關的問題。鏈接器的變更綁定到應用的目標 API 級別。如果應用的目標 API 級別發生鏈接器變更,則該應用無法加載該庫。如果您的目標 API 級別低于發生鏈接器變更的 API 級別,則 logcat 會顯示一條警告消息。在預覽版期間,與鏈接器有關的問題不僅會顯示在 logcat 中,也會以 toast 的形式顯示。對于特定的 API 級別,警告可能會變成錯誤,此變更有助于提前發現此類問題。
#### **集合的處理**
在 Android 8.0 中,Collections.sort() 是在 List.sort() 的基礎上實現的。在 Android 7.x(API 級別 24 和 25)中,則恰恰相反。在過去,List.sort() 的默認實現會調用 Collections.sort()。
此項變更使 Collections.sort() 可以利用優化的 List.sort() 實現,但具有以下限制:
* List.sort() 的實現不能調用 Collections.sort(),因為這會導致堆棧因無限遞歸而溢出。相反,如果您需要 List 實現的默認行為,應避免重寫 sort()。
如果父類以不適當的方法實現 sort() ,通常最好使用在 List.toArray()、Arrays.sort() 和 ListIterator.set() 的基礎上構建的實現重寫 List.sort()。例如:
~~~
@Override
public void sort(Comparator<? super E> c) {
Object[] elements = toArray();
Arrays.sort(elements, c);
ListIterator<E> iterator = (ListIterator<Object>) listIterator();
for (Object element : elements) {
iterator.next();
iterator.set((E) element);
}
}
~~~
在大多數情況下,您也可以使用根據 API 級別委托給其他默認實現的實現重寫 List.sort()。例如:
~~~
@Override
public void sort(Comparator<? super E> comparator) {
if (Build.VERSION.SDK_INT <= 25) {
Collections.sort(this);
} else {
super.sort(comparator);
}
}
~~~
如果您選擇后者只是因為您希望開發一種適用于所有 API 級別的 sort() 函數,可以考慮賦予其一個唯一的名稱,例如 sortCompat(),而不是重寫 sort()。
* 現在,Collections.sort() 只是對調用 sort() 的 List 實現進行的一項結構性修改。例如,在 Android 8.0 之前的平臺版本中,如果通過調用 List.sort() 進行排序,則當迭代處理 ArrayList 以及在迭代過程中調用 sort() 時,會引發 ConcurrentModificationException。而 Collections.sort() 則不會引發異常。
此項變更使平臺行為更加一致:現在,兩種方法都會引發 ConcurrentModificationException。
#### **類加載行為**
Android 8.0 檢查確保類加載器在加載新類時不會違反運行時假設條件。不論類引用自 Java(來自 forName())、Dalvik 字節碼還是 JNI,都會執行這些檢查。平臺不會攔截 Java 對 loadClass() 函數的直接調用,也不會檢查此類調用的結果。此行為不應影響運行良好的類加載器的正常運行。
平臺將檢查類加載器返回的類描述符是否與預期的描述符一致。如果返回的描述符與預期不符,平臺會引發 NoClassDefFoundError 錯誤,并在異常日志中存儲一條注明不一致之處的詳細錯誤消息。
平臺還檢查請求的類描述符是否有效。此檢查捕獲間接加載諸如 GetFieldID() 等類的 JNI 調用,向這些類傳遞無效的描述符。例如,找不到包含 java/lang/String 簽名的字段,是因為此簽名無效;它應為 Ljava/lang/String;。
這與 JNI 對 FindClass() 的調用不同,其中 java/lang/String 是一個有效的完全限定名稱。
Android 8.0 不支持多個類加載器同時嘗試使用相同的 DexFile 對象來定義類。嘗試進行此操作,會導致 Android 運行時引發 InternalError 錯誤,同時顯示消息“Attempt to register dex file <filename> with multiple class loaders”。
DexFile API 現已棄用,強烈建議您改為使用此平臺的類加載器之一,包括 PathClassLoader 或 BaseDexClassLoader。
> 注: 您可以創建多個引用文件系統中同一個 APK 或 JAR 文件容器的類加載器。這樣做通常不會占用大量內存:如果存儲而不壓縮容器中的 DEX 文件,平臺可以對此類文件執行 mmap 操作,而不直接提取它們。但是,如果平臺必須從容器中提取 DEX 文件,以這種方式引用 DEX 文件可能占用大量內存。
在 Android 中,所有類加載器都被視為支持并行運行。當多個線程爭用同一個類加載器加載相同的類時,第一個完成此操作的線程勝出,而操作結果將用于其他線程。無論類加載器是返回同一個類、返回不同的類還是引發異常,都將發生此行為。該平臺靜默忽略此類異常。
> 注意: 在低于 Android 8.0 的平臺版本中,違反這些假設條件可能導致多次定義同一個類、由于類混淆造成堆損壞和其他不良影響。
- 前言
- Google官網對Android API各版本的介紹
- jelly Bean(果凍豆)Android 4.1、4.2、4.3
- Android 4.1
- Android 4.2
- Android 4.3
- KitKat(Android 4.4.*)巧克力
- Android 4.4 APIS
- Lollipop(棒棒糖)Android 5.*
- Android 5.0 APIs
- Android 5.0 Changes(變更)
- Android 5.1APIs
- Marshmallow(棉花糖)Android 6.0
- Android 6.0 APIs
- Android 6.0 Changes(變更)
- Android 6.0 Samples
- Android 6.0 Testing
- Nougat(牛扎塘)Android 7.*
- Android 7.0
- API
- 行為變更
- 示例
- Android 7.1
- 開發者API
- 示例Sample
- Oreo(奧利奧)8.*
- Android 8.0
- 功能和 API
- Android 8.0 行為變更
- 向 Android 8.0 遷移應用
- Android 8.0 示例
- Android 8.1
- 后臺執行限制
- 后臺位置限制
- API指南
- Android 簡介
- 應用基礎知識
- 設備兼容性
- 系統權限
- 請求權限
- 定義權限
- 平臺架構
- Java8 概覽
- 在ART上驗證應用行為
- 應用組件
- Intent 和 Intent 過濾器(Google官網介紹)
- 通用intent
- Activity
- 任務和返回棧(官網譯文)
- 概覽屏幕
- 活動簡介
- 活動生命周期
- 活動狀態更改
- 進程和應用程序生命周期
- 包裹和捆綁
- 最近的屏幕
- 片段
- 加載器
- 服務Service
- 綁定服務
- AIDL
- 內容提供程序
- 內容提供程序基礎知識
- 創建內容提供程序
- 日歷提供程序
- 聯系人提供程序
- 存儲訪問框架
- 使用存儲訪問框架打開文件
- 創建自定義文檔提供程序
- 應用小部件
- 應用小部件主機
- 進程和線程
- 應用資源
- 概覽
- 提供資源
- 訪問資源
- 處理運行時變更
- 本地化
- ICU4J Android框架API
- Android上的國際化
- 語言和語言區域
- 復雜的XML資源
- 資源類型
- 動畫
- 顏色狀態列表
- 可繪制對象
- 布局
- 菜單
- 字符串
- 樣式
- 其他類型
- 應用清單
- <action>
- <activity>
- <activity-alias>
- <application>
- <category>
- <compatiable-screens>
- <data>
- <grant-uri-permission>
- <intent-filter>
- <manifest>
- <meta-data>
- <path-permission>
- <permission>
- <permission-group>
- <permission-tree>
- <provider>
- <receiver>
- <service>
- <supporte-gl-texture>
- <supports-screens>
- <uses-configuration>
- <uses-feature>
- <uses-library>
- <uses-permission>
- <uses-permission-sdk-23>
- <uses-sdk>
- 用戶界面
- 界面概覽
- 界面布局
- 線性布局
- 相對布局
- 列表視圖
- 網格視圖
- 回收站視圖
- 外觀和感覺
- 可下載的字體
- XML中的字體
- 表情符號兼容性
- 自動調整TextView
- 樣式和主題-
- 輸入控件
- 按鈕
- 文本字段
- 復選框
- 單選按鈕
- 切換按鈕
- 微調框
- 選取器
- 輸入事件
- 菜單Menu
- 設置
- 對話框
- 通知
- Toast
- 自適應圖標
- 應用快捷方式
- 搜索
- 創建搜索界面
- 添加近期查詢建議
- 添加自定義建議
- 可搜索配置
- 多窗口支持
- 拖放
- 無障礙功能
- 為應用設置無障礙功能
- 無障礙功能開發者檢查單
- 構建無障礙服務
- 讓應用更容易訪問
- 使用節點樹調試
- 構建可訪問自定義視圖
- 樣式和主題
- 自定義組件
- 動畫和圖形
- 概覽介紹
- 屬性動畫
- 視圖動畫
- 可繪制動畫
- 畫布和可繪制對象
- 基于物理的動畫
- Spring Animation
- Fling Animation
- OpenGL ES
- 硬件加速
- 計算
- RenderScript
- 高級RenderScript
- Runtime API Reference(參考)
- Numerical Types(數字類型)
- Object Types(對象類型)
- Conversion Functions(轉換函數)
- Mathematical Constants and Functions(數學常量和函數)
- Vector Math Functions(矢量數學函數)
- Matrix Functions(矩陣函數)
- Quaternion Functions(四元數函數)
- Atomic Update Functions(原子更新函數)
- Time Functions and Types(時間函數和類型)
- Allocation Data Access Functions(分配數據訪問函數)
- Object Characteristics Functions(對象特性函數)
- Kernel Invocation Functions and Types(內核調用函數和類型)
- Input/Output Functions(輸入輸出函數)
- Debugging Functions(調試函數)
- Graphics Functions and Types(圖形函數和類型)
- Index(索引)
- Media Apps(媒體應用)
- Media Apps Overview(媒體應用程序概述)
- Working with a Media Session(使用媒體會話)
- Building an Audio App(建立一個音頻應用)
- Building a Media Browser Service(構建媒體瀏覽器服務)
- Building a Media Browser Client(構建媒體瀏覽器客戶端)
- Media Session Callbacks(媒體會話回調)
- Building a Video App(建立一個視頻應用)
- Building a Video Player Activity(建立一個視頻播放器Activity)
- Media Session Callbacks-(媒體會話回調)
- Responding to Media Buttons(響應媒體按鈕)
- Handling Changes in Audio Output(處理音頻輸出的變化)
- Managing Audio Focus(管理音頻焦點)
- The Google Assistant and Media Apps(Google智能助理和媒體應用)
- 媒體和相機
- Supported Media Formats(支持的媒體格式)
- MediaPlayer(媒體播放器)
- MediaRecorder
- ExoPlayer
- Controller Amplitude with VolumeShaper(VolumeShaper控制器振幅)
- Media Routing(媒體路由)
- MediaRouter API
- MediaRouteProvider API
- Camera API(相機API)
- 位置和傳感器
- Location and Maps(位置和地圖)
- Location Strategies(位置策略)
- Sensors Overview(傳感器概覽)
- Motion Sensors(運動傳感器)
- Position Sensors(位置傳感器)
- Environment Sensors(環境傳感器)
- Raw GNSS Measurements(原始的GNSS測量)
- 連接
- Bluetooth
- Bluetooth Low Energy(藍牙低功耗)
- NFC
- NFC Basics(NFC基礎知識)
- Advanced NFC(高級NFC)
- Host-based Card Emulation(基于主機的卡模擬)
- Telecom(電信)
- Self-Managed ConnectionServices(自我管理的連接服務)
- Wi-Fi P2P
- Wi-Fi Aware
- Companion Device Pairing
- USB
- Accessory(配件)
- Host(主機)
- SIP
- 文本和輸入
- Autofill Framework(自動填充框架)
- Test your app with autofill(使用自動填充測試你的應用)
- Building autofill services(構建自動填充服務)
- Copy and Paste(復制和粘貼)
- Creating an IME(創建IME)
- Image Keyboard(圖像鍵盤)
- Spelling Checker(拼寫檢查程序)
- 數據存儲
- Storage Options(存儲選項)
- Data Backup(數據備份)
- Account Transfer API(賬戶轉移API)
- Auto Backup(自動備份)
- Key/Value Backup(鍵值備份)
- Testing Backup and Restore(測試備份和還原)
- App Install Location(應用安裝位置)
- 庫
- 支持庫
- 功能
- 修訂歷史記錄
- 庫設置
- 數據綁定庫
- 測試支持庫
- 管理
- 設備策略
- 網絡應用
- Supporting Different Screens in Web Apps(在網絡應用中支持不同屏幕)
- Building Web Apps in WebView(在WebView中構建網絡應用)
- Managing WebViews
- Migrating to WebView in Android 4.4(遷移到Android4.4中的WebView)
- Debugging Web Apps(調試網絡應用)
- Best Practices for Web Apps(網絡應用最佳做法)
- 最佳實踐
- Supporting Multiple Screens(支持多種屏幕)
- Distributing to Specific Screens(分配到特定屏幕)
- Screen Compatibility Mode(屏幕兼容性模式)
- Designing for Seamlessness
- Supporting Tablets and Handsets
- 培訓