Android 8.0 行為變更

除了新功能和新能力之外,Android 8.0 (API 級別 26) 還包含各種系統和 API 行為變更。本文將說明一些您應瞭解並在應用程式中考量的重大變更。

無論應用程式指定的 Android 版本為何,這些變更大多會影響所有應用程式。不過,部分變更只會影響指定 Android 8.0 為目標版本的應用程式。為盡量清楚說明,這個頁面分為兩個部分:所有應用程式的變更指定 Android 8.0 為目標版本的應用程式變更

針對所有應用程式的變更

無論目標 API 級別為何,這些行為變更都會套用至在 Android 8.0 (API 級別 26) 平台上執行的所有應用程式。所有開發人員都應檢查這些變更,並根據應用程式適用的方式,適當地修改應用程式。

背景執行限制

為了延長電池續航力,Android 8.0 (API 級別 26) 引入了一些變更,其中之一是當應用程式進入「已快取」狀態,且沒有任何有效的「元件」時,系統會釋出應用程式持有的任何喚醒鎖。

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

  • 在背景執行的應用程式現在必須遵守限制,才能自由存取背景服務。
  • 應用程式無法使用資訊清單註冊大多數隱含廣播訊息 (也就是未明確指定應用程式的廣播訊息)。

根據預設,這些限制只會套用到指定 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。
  • 測試應用程式是否會在每個用途中展現預期的行為。
  • 建議您使用 FusedLocationProvider (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 應用程式中使用鍵盤瀏覽功能。在 Android 8.0 (API 級別 26) 中,我們重新調整了使用鍵盤做為導覽輸入裝置的做法,讓以箭頭和分頁為基礎的導覽功能,能提供更可靠、可預測的模型。

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

  • 如果您尚未為 View 物件定義任何焦點狀態顏色 (前景或背景可繪項目),架構就會為 View 設定預設的焦點醒目顯示顏色。這個焦點醒目顯示是根據活動的主題,以波紋可繪項目為基礎。

    如果您不希望 View 物件在收到焦點時使用這個預設醒目顯示效果,請在包含 View 的版面配置 XML 檔案中,將 android:defaultFocusHighlightEnabled 屬性設為 false,或是在應用程式的 UI 邏輯中,將 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 選取器的網址不包含要求的網址路徑、查詢參數或片段。
  • 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 回音通訊協定。
    • 部分封鎖 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.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 中,Contacts Provider 元件可讓開發人員取得每位聯絡人的使用資料。這類使用資料會揭露與聯絡人相關聯的每個電子郵件地址和電話號碼的資訊,包括聯絡人聯絡次數和上次聯絡時間。要求 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 視窗類型顯示應用程式的快訊視窗時,請留意新視窗類型的特性:

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

內容變更通知

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

這些 API 現在要求為所有 Uri 中的權威定義有效的 ContentProvider。定義具有相關權限的有效 ContentProvider,有助於防範應用程式遭惡意應用程式竄改內容,並防止將可能的私人資料洩漏給惡意應用程式。

查看重點

根據預設,可點選的 View 物件現在也能聚焦。如果想讓 View 物件可供點選,但無法聚焦,請在包含 View 的版面配置 XML 檔案中,將 android:focusable 屬性設為 false,或在應用程式 UI 邏輯中將 false 傳入 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,因為 WRITE_EXTERNAL_STORAGE 屬於相同的 STORAGE 權限群組,且也已在資訊清單中註冊。如果應用程式指定 Android 8.0 (API 級別 26),系統會在該時間點只授予 READ_EXTERNAL_STORAGE;不過,如果應用程式稍後要求 WRITE_EXTERNAL_STORAGE,系統會立即授予該特權,且不會提示使用者。

媒體

  • 此架構可以自行執行自動音訊調低。在這種情況下,當其他應用程式使用 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 要求焦點時,具有焦點的應用程式會降低音量,但通常不會收到 onAudioFocusChange() 回呼,也不會失去音訊焦點。對於需要暫停而非靜音的應用程式,您可以使用新版 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);
      }
    }

    在大多數情況下,您也可以使用實作來覆寫 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()

  • Collections.sort() 現在會視為呼叫 sort() 的清單實作項目中的結構修改。舉例來說,在 Android 8.0 (API 級別 26) 之前的平台版本中,如果排序是透過呼叫 List.sort() 完成,在迭代過程中迭代 ArrayList 並在其中呼叫 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 呼叫不同,FindClass() 中的 java/lang/String 是有效的完整名稱。

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

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

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

在 Android 中,所有類別載入器都會視為可並行處理。如果多個執行緒競爭以相同類別載入器載入同一類別,第一個完成作業的執行緒將勝出,而結果也會用於其他執行緒。無論類別載入器是否已傳回相同類別、傳回不同類別或擲回例外狀況,都會發生此行為。平台會在不發出通知的情況下忽略這類例外狀況。

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