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 版本的應用程式」行為變更中的「快訊視窗的常見視窗類型」一節。

輸入與導覽

隨著 ChromeOS 和平板電腦等其他大型板型規格問世,Android 應用程式內的鍵盤操作功能再度大增。在 Android 8.0 (API 級別 26) 中,我們已以鍵盤做為瀏覽輸入裝置進行重置,針對箭頭和分頁式導覽設計提供更穩定且可預測的模型。

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

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

    如果不希望 View 物件在收到焦點時使用這個預設醒目顯示方式,請在包含 View 的版面配置 XML 檔案中將 android:defaultFocusHighlightEnabled 屬性設為 false,或將 false 傳入應用程式的 UI 邏輯中的 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 網路。具體變更包括:

  • 提升穩定性與可靠性。
  • 更直覺易懂的使用者介面。
  • 採用單一整合式 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) 版本之前,除非解除安裝,然後在 OTA 後重新安裝,否則其 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,透過應用程式營利。由 Google Play 服務提供。

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

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

記錄未偵測到的例外狀況

如果應用程式安裝時未呼叫預設 Thread.UncaughtExceptionHandlerThread.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 企業版」。

指定 Android 8.0 為目標的應用程式

這些行為變更僅適用於指定 Android 8.0 (API 級別 26) 以上版本的應用程式。若是針對 Android 8.0 編譯,或者將 targetSdkVersion 設為 Android 8.0 或以上版本的應用程式,則必須根據該應用程式修改應用程式,以適當支援這些行為。

快訊視窗

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

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

使用 TYPE_APPLICATION_OVERLAY 視窗類型顯示應用程式的快訊視窗時,請記住以下新視窗類型的特性:

  • 應用程式的快訊視窗一律顯示在重要系統視窗 (例如狀態列和輸入法編輯器) 下方。
  • 系統可以移動或調整使用 TYPE_APPLICATION_OVERLAY 視窗類型的視窗大小,改善螢幕畫面的呈現方式。
  • 開啟通知欄後,使用者就能存取設定,禁止應用程式顯示使用 TYPE_APPLICATION_OVERLAY 視窗類型顯示的快訊視窗。

內容異動通知

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

這些 API 現在要求在所有 Uris 中為主機名稱定義有效的 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 的行為會像是沒有在同一個設定檔群組內其他設定檔中安裝任何應用程式。和先前一樣,嘗試存取不相關的設定檔會導致 SecurityExceptions。

權限

在 Android 8.0 (API 級別 26) 之前,如果應用程式在執行階段要求權限並授予權限,系統也會誤將其他屬於相同權限群組的權限,以及已在資訊清單中註冊的權限傳送給應用程式。

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

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

媒體

  • 這個架構可自行執行自動降低音訊降噪功能。在這種情況下,當其他應用程式透過 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 要求聚焦時,聚焦的應用程式會降低音量,但通常不會收到 onAudioFocusChange() 回呼,也不會失去音訊焦點。新的 API 可以針對需要暫停 (而非執行中) 的應用程式覆寫此行為。
  • 使用者接聽來電時,進行中的媒體串流會在通話期間為靜音狀態。
  • 所有音訊相關的 API 應使用 AudioAttributes (而非音訊串流類型) 來說明音訊播放用途。請繼續只將音訊串流類型用於音量控制。其他串流類型的用途仍可使用 (例如將 streamType 引數用於已淘汰的 AudioTrack 建構函式),但系統會將此記錄為錯誤。
  • 使用 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);
      }
    }
    

    在大多數情況下,您也可以使用實作項目來覆寫 List.sort(),實作作業會依照 API 級別委派至不同的預設實作項目。例如:

    @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()

  • 現在,在呼叫 sort() 的清單實作中,Collections.sort() 會計為結構修改。舉例來說,在 Android 8.0 (API 級別 26) 之前的平台版本中,對 ArrayList 進行疊代,並在疊代中呼叫 sort(),如果透過呼叫 List.sort() 完成排序,就會擲回 ConcurrentModificationExceptionCollections.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 錯誤,並顯示「嘗試透過多個類別載入器註冊 dex 檔案 <filename>」訊息。

DexFile API 現已淘汰,我們強烈建議改用其中一個平台類別載入器,包括 PathClassLoaderBaseDexClassLoader

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

在 Android 中,所有類別載入器都視為具有平行處理能力。當多個執行緒爭用相同的類別載入器載入同一類別時,則會由第一個完成作業的執行緒獲勝,並將結果用於其他執行緒。無論類別載入器是否傳回相同的類別、傳回其他類別或擲回例外狀況,都會發生此行為。平台會自動忽略這類例外狀況。

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