Android 8.0 行為變更

除了新功能以外,Android 8.0 (API 級別 26) 還包含各種系統和 API 行為變更。本文重點介紹應用程式中應瞭解並考量的一些重要異動。

無論應用程式指定的 Android 版本為何,這些變更都會影響所有應用程式。不過,幾項變更只會影響指定 Android 8.0 為目標版本的應用程式。為盡可能清楚起見,本頁分為兩個部分:「所有應用程式的變更」和「指定 Android 8.0 版本的應用程式變更」。

針對所有應用程式的變更

在 Android 8.0 (API 級別 26) 平台上執行時,無論指定的 API 級別為何,這些行為變更都會套用至所有應用程式。所有開發人員都應檢查這些變更,並修改應用程式,以便在適當情況下提供支援。

背景執行限制

當 Android 8.0 (API 級別 26) 為提高電池續航力而引進的其中一項變更是,當應用程式進入快取狀態,且沒有使用中的元件時,系統就會釋放應用程式保留的所有 Wake Lock。

此外,為了改善裝置效能,系統會限制未在前景執行的應用程式的特定行為。具體違規事項如下:

  • 在背景執行的應用程式現在對背景服務的自由存取方式設有限制。
  • 應用程式無法使用自己的資訊清單註冊大部分的隱式廣播 (也就是未指定於應用程式指定的廣播訊息)。

根據預設,這些限制只會套用至指定 O 的應用程式。不過,即使應用程式未指定 O,使用者還是可以透過「設定」畫面為任何應用程式啟用這些限制。

Android 8.0 (API 級別 26) 還針對特定方法包含下列變更:

  • 如果指定 Android 8.0 版本的應用程式嘗試在不允許建立背景服務的情況下嘗試使用該方法,startService() 方法現在會擲回 IllegalStateException
  • 新的 Context.startForegroundService() 方法會啟動前景服務。系統允許應用程式在背景執行時呼叫 Context.startForegroundService()。不過,應用程式必須在服務建立後的五秒內呼叫服務的 startForeground() 方法。

詳情請參閱「背景執行限制」。

Android 背景位置資訊限制

為節省電力、使用者體驗和系統健康狀態,在搭載 Android 8.0 的裝置上,背景應用程式接收位置更新的頻率降低了。這項行為變更會影響所有接收位置更新的應用程式,包括 Google Play 服務。

這些變更會影響以下 API:

  • 整合式位置預測提供工具 (FLP)
  • 地理圍欄
  • GNSS 測量
  • 位置管理工具
  • Wi-Fi 管理員

為確保應用程式如預期執行,請完成下列步驟:

  • 檢查應用程式的邏輯,並確保您使用的是最新的 Location API。
  • 測試應用程式是否展現您預期的各種用途行為。
  • 建議您使用整合式位置預測提供工具 (FLP) 或地理圍欄,處理需要使用者目前位置的用途。

如要進一步瞭解這些變更,請參閱「背景位置資訊限制」。

應用程式捷徑

Android 8.0 (API 級別 26) 包含下列應用程式捷徑變更:

  • com.android.launcher.action.INSTALL_SHORTCUT 廣播現在是私人的隱式廣播,因此對應用程式沒有任何影響。您應該改為使用 ShortcutManager 類別的 requestPinShortcut() 方法建立應用程式捷徑。
  • ACTION_CREATE_SHORTCUT 意圖現在可以建立您使用 ShortcutManager 類別管理的應用程式捷徑。此意圖也可以建立不會與 ShortcutManager 互動的舊版啟動器捷徑。這個意圖先前只能建立舊版啟動器捷徑。
  • 透過 requestPinShortcut() 建立的捷徑,以及在處理 ACTION_CREATE_SHORTCUT 意圖的活動中建立的捷徑,現已全面成熟的應用程式捷徑。因此,應用程式現在可以使用 ShortcutManager 中的方法進行更新。
  • 舊版捷徑會保留舊版 Android 的功能,但您必須在應用程式中手動將其轉換為應用程式捷徑。

如要進一步瞭解應用程式捷徑的異動,請參閱「固定捷徑和小工具」功能指南。

語言代碼和國際化

Android 7.0 (API 級別 24) 導入了能夠指定預設類別語言代碼的概念,但部分 API 會繼續使用一般 Locale.getDefault() 方法 (不含引數),但應改用預設的 DISPLAY 類別語言代碼。在 Android 8.0 (API 級別 26) 中,下列方法現在使用 Locale.getDefault(Category.DISPLAY),而非 Locale.getDefault()

無法使用 Locale 引數指定的 displayScript 值時,Locale.getDisplayScript(Locale) 也會改回 Locale.getDefault()

其他語言代碼和國際相關變更如下:

  • 呼叫 Currency.getDisplayName(null) 會擲回 NullPointerException,並依照記錄的行為。
  • 時區名稱剖析功能已變更。先前 Android 裝置會使用開機時取樣的系統時鐘值,快取剖析日期時間使用的時區名稱。因此,如果系統時鐘在開機時間 (或在其他極少數的情況下) 有誤,剖析作業可能會受到負面影響。

    現在,剖析邏輯在剖析時區名稱時,通常會使用 ICU 和目前的系統時鐘值。這項變更可提供更正確的結果,且當應用程式使用 SimpleDateFormat 等類別時,這些結果可能會與舊版 Android 不同。

  • Android 8.0 (API 級別 26) 將 ICU 版本更新為 58 版。

快訊視窗

如果應用程式使用 SYSTEM_ALERT_WINDOW 權限,並使用下列任一視窗類型嘗試將快訊視窗顯示在其他應用程式和系統視窗上方:

...然後,這些視窗一律會顯示在使用 TYPE_APPLICATION_OVERLAY 視窗類型的視窗下方。如果應用程式指定 Android 8.0 (API 級別 26) 版本,應用程式會使用 TYPE_APPLICATION_OVERLAY 視窗類型顯示快訊視窗。

詳情請參閱「指定 Android 8.0 版本的應用程式」行為變更中的「快訊視窗的常見視窗類型」一節。

輸入和瀏覽

隨著 Android 應用程式在 ChromeOS 和其他大型板型規格 (例如平板電腦) 上出現,我們再度在 Android 應用程式中使用鍵盤瀏覽功能。在 Android 8.0 (API 級別 26) 中,我們重新設計了使用鍵盤做為導覽輸入裝置的問題,使得以箭頭和分頁瀏覽方式打造出更可靠、能夠預測的模型。

具體來說,我們已對元素焦點行為做出下列變更:

  • 如果您尚未為 View 物件定義任何焦點狀態顏色 (其前景或背景可繪項目),架構現在會為 View 設定預設的焦點醒目顯示顏色。焦點醒目顯示是根據活動主題建立的漣漪可繪項目。

    如果您不希望 View 物件在收到焦點時使用此預設醒目顯示,請在包含 View 的版面配置 XML 檔案中將 android:defaultFocusHighlightEnabled 屬性設為 false,或將 false 傳入應用程式的使用者介面邏輯中的 setDefaultFocusHighlightEnabled()

  • 如要測試鍵盤輸入對 UI 元素焦點的影響,您可以啟用「Drawing」>「Show layout bounds」開發人員選項。在 Android 8.0 中,這個選項會在目前聚焦的元素上顯示「X」圖示。

此外,Android 8.0 中的所有工具列元素都是自動鍵盤導覽叢集,讓使用者更容易在各個工具列中來回移動。

如要進一步瞭解如何改善應用程式內的鍵盤導覽功能,請參閱支援鍵盤導覽功能指南。

網路表單自動填入

現在 Android 自動填入架構已提供自動填入功能的內建支援,針對安裝在 Android 8.0 (API 級別 26) 裝置上的應用程式,變更了下列與 WebView 物件相關的方法:

WebSettings
WebViewDatabase
  • 呼叫 clearFormData() 已失效。
  • hasFormData() 方法現在會傳回 false。先前,這個方法會在表單包含資料時傳回 true

無障礙功能

Android 8.0 (API 級別 26) 包含下列無障礙功能變更:

  • 無障礙架構現在會將所有輕觸兩下手勢轉換為 ACTION_CLICK 動作。這項變更可讓 TalkBack 的運作方式與其他無障礙服務更類似。

    如果應用程式的 View 物件使用自訂觸控處理功能,請確認這些物件仍可搭配 TalkBack 使用。您只需要註冊 View 物件使用的點擊處理常式。如果 TalkBack 仍無法辨識對這些 View 物件執行的手勢,請覆寫 performAccessibilityAction()

  • 無障礙服務現在可得知應用程式 TextView 物件中的所有 ClickableSpan 執行個體。

如要進一步瞭解如何打造無障礙應用程式,請參閱「無障礙設計」。

網路和 HTTP(S) 連線

Android 8.0 (API 級別 26) 包含下列網路和 HTTP(S) 連線行為變更:

  • 沒有主體的 OPTIONS 要求會含有 Content-Length: 0 標頭。先前沒有 Content-Length 標頭。
  • HttpURLConnection 會在主機或授權機構名稱後方加上斜線,將包含空白路徑的網址正規化。例如將 http://example.com 轉換為 http://example.com/
  • 透過 ProxySelector.setDefault() 設定的自訂 Proxy 選取器只會指定要求網址的位址 (配置、主機和通訊埠)。 因此,您只能根據這些值來選擇 Proxy。傳遞至自訂 Proxy 選取器的網址不含要求的網址路徑、查詢參數或片段。
  • URI 不得含有空白標籤。

    平台之前支援變通方法,以便接受主機名稱中包含空白標籤,這是 URI 的非法使用。這個解決方法是與舊版 libcore 相容。如果開發人員誤用 API,就會看到 ADB 訊息:「URI example..com 的主機名稱有空白標籤。這項資訊格式錯誤,日後的 Android 版本將不再接受此格式。」Android 8.0 會移除此解決方法;系統會針對格式錯誤的 URI 傳回空值。

  • Android 8.0 的 HttpsURLConnection 不會執行不安全的 TLS/SSL 通訊協定版本備用作業。
  • 通道 HTTP(S) 連線的處理方式已變更如下:
    • 透過連線通道 HTTPS 連線時,系統將這項資訊傳送至中繼伺服器時,會將通訊埠號碼 (:443) 正確放在主機行。先前,通訊埠編號只會出現在 CONNECT 行中。
    • 系統不會再將通道要求中的使用者代理程式和 Proxy 授權標頭傳送至 Proxy 伺服器。

      設定通道時,系統不再將通道 Http(s)URLConnection 的 Proxy 授權標頭傳送至 Proxy。而是產生 Proxy 授權標頭,並在該 Proxy 傳送 HTTP 407 以回應初始要求時,將其傳送至 Proxy。

      同樣地,系統不會再將通道要求中的使用者代理程式標頭複製到設定通道的 Proxy 要求。反之,程式庫會針對該要求產生使用者代理程式標頭。

  • 如果先前執行的 connect() 方法失敗,send(java.net.DatagramPacket) 方法會擲回 SocketException。
    • 如果發生內部錯誤,DatagramSocket.connect() 會設定 pendingSocketException。在 Android 8.0 之前,即使 send() 呼叫成功,後續的 recv() 呼叫也會擲回 SocketException。為了一致性,兩個呼叫現在會擲回 SocketException。
  • InetAddress.isReachable() 先嘗試 ICMP,再改回使用 TCP Echo 通訊協定。
    • 現在,某些封鎖通訊埠 7 (TCP Echo) 的主機 (例如 google.com) 可能會在接受 ICMP Echo 通訊協定的情況下建立連線。
    • 對於真正無法連線的主機,這項變更表示在呼叫傳回之前,所花費的時間是原本的兩倍。

藍牙

Android 8.0 (API 級別 26) 會對 ScanRecord.getBytes() 方法擷取的資料長度進行下列變更:

  • getBytes() 方法不會對收到的位元組數做出假設。因此,應用程式不應仰賴任何傳回的位元組數下限或上限。而是應評估產生的陣列長度。
  • 與藍牙 5 相容的裝置回傳的資料長度可能會超過前一個位元組上限 (約 60 個位元組)。
  • 如果遠端裝置未提供掃描回應,可能也會傳回少於 60 個位元組。

流暢的連線

Android 8.0 (API 級別 26) 改善了 Wi-Fi 設定,讓您輕鬆選擇提供最佳使用者體驗的 Wi-Fi 網路。具體變更包括:

  • 提升穩定性與可靠性。
  • 更直覺易讀的 UI。
  • 整合式 Wi-Fi 偏好設定選單。
  • 在相容裝置上,當已儲存的高品質網路位於附近時,自動啟用 Wi-Fi。

安全性

Android 8.0 提供下列安全性相關變更:

  • 該平台不再支援 SSLv3。
  • 與錯誤實作 TLS 通訊協定版本交涉的伺服器建立 HTTPS 連線時,HttpsURLConnection 不會再嘗試設法改回使用舊版 TLS 通訊協定並重試。
  • Android 8.0 (API 級別 26) 會將安全運算 (SECCOMP) 篩選器套用至所有應用程式。允許的系統呼叫清單僅限透過生物接觸揭露的系統呼叫。雖然我們還提供其他幾種為了回溯相容性而提供的系統呼叫,但我們建議不要使用。
  • 應用程式的 WebView 物件現在以多程序模式執行。為提升安全性,系統會透過獨立的獨立程序處理網頁內容,與所含應用程式的程序不同。
  • 您無法再假設 APK 位於名稱結尾為 -1 或 -2 的目錄中。應用程式應使用 sourceDir 取得目錄,而不是直接依賴目錄格式。
  • 如要瞭解與使用原生資料庫相關的安全性強化項目,請參閱原生資料庫相關說明。

此外,Android 8.0 (API 級別 26) 也導入了從不明來源安裝不明應用程式的相關變更:

如要進一步瞭解如何安裝不明應用程式,請參閱「不明應用程式安裝權限」指南。

如需提高應用程式安全性的其他指南,請參閱「Android 開發人員的安全性」。

隱私權

Android 8.0 (API 級別 26) 會對平台進行下列隱私權相關變更。

  • 平台現在以不同的方式處理 ID。
    • 若是在 OTA 升級到 Android 8.0 (API 級別 26) (API 級別 26) 版本之前安裝的應用程式,除非先解除安裝再重新安裝,否則 ANDROID_ID 的值將維持不變。如要在 OTA 後在解除安裝後保留值,開發人員可以使用 鍵/值備份,將舊值與新值建立關聯。
    • 對於安裝在 Android 8.0 裝置上的應用程式,ANDROID_ID 的值現在範圍涵蓋每個應用程式簽署金鑰和每位使用者。針對每個應用程式簽署金鑰、使用者和裝置的組合,ANDROID_ID 的值不得重複。因此,如果應用程式在相同裝置上執行不同的簽署金鑰,就不會再看到相同的 Android ID,即使是同一名使用者也一樣。
    • 只要簽署金鑰相同 (且應用程式未在 OTA 至 Android 8.0 版本之前安裝),ANDROID_ID 的值在套件解除安裝或重新安裝時不會變更。
    • 即使系統更新導致套件簽署金鑰變更,ANDROID_ID 的值也不會改變。
    • 在提供 Google Play 服務和廣告 ID 的裝置上,您必須使用 廣告 ID。廣告 ID 是簡易且可透過應用程式營利的標準系統,也是可由使用者重設的專屬 ID,可用來放送廣告。這個 ID 是由 Google Play 服務提供。

      其他裝置製造商應繼續提供 ANDROID_ID

  • 查詢 net.hostname 系統屬性會產生空值結果。

記錄未偵測到的例外狀況

如果應用程式安裝的 Thread.UncaughtExceptionHandler 不會呼叫預設的 Thread.UncaughtExceptionHandler,系統不會在發生未偵測到的例外狀況時終止應用程式。從 Android 8.0 (API 級別 26) 開始,系統會在此情況下記錄例外狀況堆疊追蹤;在舊版平台中,系統不會記錄例外狀況堆疊追蹤。

我們建議一律透過預設處理常式呼叫自訂 Thread.UncaughtExceptionHandler 實作;採用這項建議的應用程式不會受到 Android 8.0 的變更影響。

findViewById() 簽名變更

findViewById() 方法的所有例項現在會傳回 <T extends View> T,而非 View。這項變更會產生下列影響:

  • 這可能會導致現有程式碼現在的傳回類型模稜兩可,例如,如果 someMethod(View)someMethod(TextView) 都會傳回對 findViewById() 的呼叫。
  • 使用 Java 8 來源語言時,必須在傳回類型不受限制的情況下 (例如 assertNotNull(findViewById(...)).someViewMethod())),明確轉換至 View
  • 非最終 findViewById() 方法的覆寫作業 (例如 Activity.findViewById()) 需要更新傳回類型。

聯絡人供應程式使用統計資料變更

在舊版 Android 中,聯絡人供應程式元件可讓開發人員取得每位聯絡人的使用資料。這些使用資料會顯示每個電子郵件地址和與聯絡人相關聯的每一組電話號碼等資訊,其中包括聯絡該聯絡人的次數,以及與聯絡人上次聯絡的時間。要求 READ_CONTACTS 權限的應用程式可以讀取這項資料。

如果應用程式要求 READ_CONTACTS 權限,仍然可以讀取這項資料。在 Android 8.0 (API 級別 26) 以上版本中,查詢使用資料會傳回概略值,而非確切值。Android 系統會在內部維護確切的值,因此這項變更不會影響自動完成 API。

這項行為變更會影響下列查詢參數:

收集處理

AbstractCollection.removeAll()AbstractCollection.retainAll() 現在一律擲回 NullPointerException;先前,如果集合為空白,並不會擲回 NullPointerException。這項變更讓行為與說明文件一致。

Android Enterprise

Android 8.0 (API 級別 26) 會變更企業應用程式的部分 API 和功能行為,包括裝置政策控制器 (DPC)。這些異動包括:

  • 協助應用程式在全代管裝置上支援工作資料夾的新行為。
  • 系統更新處理方式、應用程式驗證和驗證功能,提高裝置和系統完整性。
  • 改善佈建、通知、「最近使用」畫面和永久連線的 VPN 的使用者體驗。

如要查看 Android 8.0 (API 級別 26) 中的所有企業異動項目,並瞭解這些變更對應用程式有何影響,請參閱 Android Enterprise 企業版

指定 Android 8.0 版本的應用程式

這些行為變更僅適用於指定 Android 8.0 (API 級別 26) 以上版本的應用程式。如果應用程式針對 Android 8.0 進行編譯,或是將 targetSdkVersion 設為 Android 8.0 以上版本,就必須修改應用程式,才能在適用情況下正確支援這些行為。

快訊視窗

使用 SYSTEM_ALERT_WINDOW 權限的應用程式無法再使用下列視窗類型,將快訊視窗顯示在其他應用程式和系統視窗的上方:

應用程式必須改用名為 TYPE_APPLICATION_OVERLAY 的新視窗類型。

使用 TYPE_APPLICATION_OVERLAY 視窗類型顯示應用程式的快訊視窗時,請留意下列新視窗類型的特性:

  • 應用程式的快訊視窗一律會顯示在重要的系統視窗 (例如狀態列和 IME) 下方。
  • 系統可以移動或調整使用 TYPE_APPLICATION_OVERLAY 視窗類型的視窗,藉此改善螢幕畫面呈現的效果。
  • 開啟通知欄後,使用者即可存取設定,禁止應用程式使用 TYPE_APPLICATION_OVERLAY 視窗類型顯示快訊視窗。

內容變更通知

Android 8.0 (API 級別 26) 變更了指定 Android 8.0 目標版本的 ContentResolver.notifyChange()registerContentObserver(Uri, boolean, ContentObserver) 行為。

這些 API 現在要求為所有 URI 中的授權定義有效的 ContentProvider。以相關權限定義有效的 ContentProvider,可防止應用程式受到惡意應用程式內容變更的影響,並防止您的潛在私人資料外洩給惡意應用程式。

查看重點

根據預設,可點擊的 View 物件現在也能聚焦。如果想讓 View 物件可供點選,但無法聚焦,請在含有 View 的版面配置 XML 檔案中,將 android:focusable 屬性設為 false,或將 false 傳入應用程式 UI 邏輯中的 setFocusable()

瀏覽器偵測時比對使用者代理程式

Android 8.0 (API 級別 26) 以上版本包含建構 ID 字串 OPR。某些模式比對結果可能會導致瀏覽器偵測邏輯將非 Opera 瀏覽器誤認為 Opera。這類模式比對的範例如下:

if(p.match(/OPR/)){k="Opera";c=p.match(/OPR\/(\d+.\d+)/);n=new Ext.Version(c[1])}

為了避免因誤判而造成問題,請使用 OPR 以外的字串做為 Opera 瀏覽器的模式比對。

安全性

以下變更會影響 Android 8.0 (API 級別 26) 中的安全性:

  • 如果應用程式的網路安全性設定選擇不支援明文流量,應用程式的 WebView 物件就無法透過 HTTP 存取網站。每個 WebView 物件都必須使用 HTTPS。
  • 「允許不明來源」系統設定已移除。「安裝不明應用程式」權限會管理來源不明的應用程式安裝。如要進一步瞭解這項新權限,請參閱不明應用程式安裝權限指南。

如需提高應用程式安全性的其他指南,請參閱「Android 開發人員的安全性」。

帳戶存取權和可偵測性

在 Android 8.0 (API 級別 26) 中,除非驗證者擁有帳戶或使用者授予存取權,否則應用程式無法再存取使用者帳戶。GET_ACCOUNTS 權限已不足。如要獲得帳戶存取權,應用程式應使用 AccountManager.newChooseAccountIntent() 或驗證器特定方法。取得帳戶存取權後,應用程式可以呼叫 AccountManager.getAccounts() 來存取這些帳戶。

Android 8.0 淘汰了 LOGIN_ACCOUNTS_CHANGED_ACTION。應用程式應改用 addOnAccountsUpdatedListener() 在執行階段取得帳戶更新。

如要瞭解新增的 API 和方法,以及如何存取及探索帳戶,請參閱本文「新 API」一節的帳戶存取權和可偵測性

隱私權

下列變更會影響 Android 8.0 (API 級別 26) 中的隱私設定。

  • net.dns1net.dns2net.dns3net.dns4 系統屬性已無法使用,其變更可加強平台的隱私權。
  • 如要取得 DNS 伺服器等網路資訊,具有 ACCESS_NETWORK_STATE 權限的應用程式可以註冊 NetworkRequestNetworkCallback 物件。這些類別適用於 Android 5.0 (API 級別 21) 以上版本。
  • Build.SERIAL 已淘汰,需要知道硬體序號的應用程式應改用新的 Build.getSerial() 方法,這需要 READ_PHONE_STATE 權限。
  • LauncherApps API 已無法再允許工作資料夾應用程式取得主要設定檔的相關資訊。當使用者位於工作資料夾時,LauncherApps API 的行為會假設同一設定檔群組內的其他設定檔中沒有安裝任何應用程式。和先前一樣,嘗試存取不相關的設定檔會導致 SecurityException。

權限

在 Android 8.0 (API 級別 26) 之前的版本中,如果應用程式在執行階段要求權限,且已獲得使用者授權,系統也會將其他屬於相同權限群組的其餘權限誤授予應用程式,該權限也已在資訊清單中註冊。

針對指定 Android 8.0 版本的應用程式,這項行為已修正。應用程式只會獲得其明確要求的權限。不過,在使用者授予應用程式權限後,系統便會自動授予該權限群組中所有後續對權限的後續要求。

舉例來說,假設應用程式在資訊清單中同時列出 READ_EXTERNAL_STORAGEWRITE_EXTERNAL_STORAGE。應用程式要求 READ_EXTERNAL_STORAGE,而使用者授予權限。如果應用程式指定 API 級別 25 以下,系統也會同時授予 WRITE_EXTERNAL_STORAGE,因為此 API 屬於相同的 STORAGE 權限群組,同時也在資訊清單中註冊。如果應用程式指定 Android 8.0 (API 級別 26) 為目標,則當下只會授予 READ_EXTERNAL_STORAGE;但是,如果應用程式之後要求 WRITE_EXTERNAL_STORAGE,系統會立即授予該權限,而不會提示使用者。

媒體

  • 架構可單獨執行自動降低音訊音量。在這種情況下,當其他應用程式要求聚焦 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 時,具有焦點的應用程式會降低音量,但通常不會收到 onAudioFocusChange() 回呼,也不會失去音訊焦點。新的 API 可以使用新的 API,針對需要暫停 (而非降低其他應用程式) 的應用程式覆寫這項行為。
  • 使用者接聽來電時,有效媒體串流在通話期間會靜音。
  • 所有音訊相關 API 都應使用 AudioAttributes,而非音訊串流類型來描述音訊播放的用途。請繼續使用音訊串流類型,只用於調整音量。其他串流類型仍可使用 (例如指向已淘汰 AudioTrack 建構函式的 streamType 引數),但系統會將此記錄為錯誤。
  • 使用 AudioTrack 時,如果應用程式要求較大的音訊緩衝區,架構就會嘗試使用可用的深度緩衝區輸出內容 (如果有的話)。
  • 在 Android 8.0 (API 級別 26) 中,媒體按鈕事件的處理方式不同:
    1. UI 活動中的媒體按鈕處理方式尚未改變:處理媒體按鈕事件時,前景活動仍優先處理。
    2. 如果前景活動沒有處理媒體按鈕事件,系統會將事件轉送至最近在本機播放音訊的應用程式。當系統判斷哪個應用程式收到媒體按鈕事件時,不會將媒體工作階段的有效狀態、標記和播放狀態納入考量。
    3. 如果應用程式的媒體工作階段已發布,系統就會將媒體按鈕事件傳送至應用程式的 MediaButtonReceiver (如果有的話)。
    4. 在其他情況下,系統會捨棄媒體按鈕事件。

原生程式庫

在指定 Android 8.0 (API 級別 26) 為目標的應用程式中,如果原生程式庫包含可寫入和可執行的載入區隔,就無法再載入。某些應用程式若原生資料庫載入區隔有誤,可能會因此停止運作。這項安全強化措施。

詳情請參閱 可寫入區隔和可執行區隔一文。

連接器變更與應用程式目標 API 級別相連結。如果在目標 API 級別有變更連結器,應用程式就無法載入程式庫。如果您指定的 API 級別低於發生連結器變更的 API 級別,Logcat 就會顯示警告。

收集處理

在 Android 8.0 (API 級別 26) 中,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() 的清單實作中的結構修改。舉例來說,在 Android 8.0 (API 級別 26) 之前的平台版本中,如果對 ArrayList 進行疊代,並在疊代作業中呼叫 sort(),如果排序作業已完成,系統會擲回 ConcurrentModificationExceptionList.sort()Collections.sort() 未擲回例外狀況。

    這項變更會使平台行為更加一致:無論採取何種做法,現在都會導致 ConcurrentModificationException

類別載入行為

Android 8.0 (API 級別 26) 會執行檢查,確保類別載入器在載入新類別時不會破壞執行階段的假設。針對從 Java (來自 forName())、Dalvik 位元碼或 JNI 參照類別,系統會執行這些檢查。平台不會攔截從 Java 對 loadClass() 方法的直接呼叫,也不會檢查這類呼叫的結果。這個行為不會影響類別載入器的運作。

平台會檢查類別載入器傳回的類別的描述元是否與預期的描述元相符。如果傳回的描述元不相符,平台會擲回 NoClassDefFoundError 錯誤,並將該錯誤儲存在例外狀況中,詳細說明差異之處。

平台也會檢查要求類別的描述元是否有效。這項檢查會擷取間接載入類別 (例如 GetFieldID()) 的 JNI 呼叫,並將無效的描述元傳遞至這些類別。舉例來說,找不到簽名為 java/lang/String 的欄位,因為該簽名無效;該欄位應為 Ljava/lang/String;

這與對 FindClass() 的 JNI 呼叫不同,其中 java/lang/String 是有效的完整名稱。

Android 8.0 (API 級別 26) 不支援讓多個類別載入器嘗試使用同一個 DexFile 物件定義類別。嘗試這麼做會導致 Android 執行階段擲回 InternalError 錯誤,並顯示訊息「Try to registration dex file <filename> with multiple class loader」(嘗試註冊 dex 檔案 <filename>)。

DexFile API 現已淘汰,強烈建議您改用 PathClassLoaderBaseDexClassLoader 等平台類別載入器。

注意: 您可以建立多個類別載入器,從檔案系統參照同一個 APK 或 JAR 檔案容器。這樣做通常不會導致大量記憶體負擔:如果儲存容器中的 DEX 檔案而非壓縮,平台可以對這類檔案執行 mmap 作業,而不會直接擷取檔案。不過,如果平台必須從容器中擷取 DEX 檔案,以這種方式參照 DEX 檔案可能會耗用大量記憶體。

在 Android 中,所有類別載入器都視為平行功能。當多個執行緒爭用相同類別載入器載入同一類別時,會贏得第一個完成作業的執行緒,並將結果用於其他執行緒。無論類別載入器是否傳回相同類別、傳回其他類別或擲回例外狀況,都會發生這個行為。平台會在不通知使用者的情況下忽略這類例外狀況。

注意: 在 Android 8.0 (API 級別 26) 以下版本的平台版本中,破壞這些假設可能會導致多次定義相同的類別、因類別混淆而造成堆積毀損,以及其他不理想的效果。