Android 9 行為變更:所有應用
Android 9(API 級別 28)向 Android 系統引入了多項變更。 當應用在 Android 9 平臺上執行時,以下行為變更將影響所有應用,無論這些應用以哪個 API 級別為目標。 所有開發者都應檢視這些變更,並修改其應用以正確支援這些變更(如果適用)。
如需瞭解僅影響以 API 28 或更高級別為目標的應用的變更,請參閱行為變更:以 API 級別 28+ 為目標的應用。
電源管理
Android 9 引入了新功能以改善裝置電源管理。 這些變更連同 Android 9 之前已存在的功能可幫助確保系統資源被提供給最需要它們的應用。
詳情請參閱電源管理。
隱私權變更
為了增強使用者隱私,Android 9 引入了若干行為變更,如限制後臺應用訪問裝置感測器、限制通過 Wi-Fi 掃描檢索到的資訊,以及與通話、手機狀態和 Wi-Fi 掃描相關的新許可權規則和許可權組。
無論採用哪一種目標 SDK 版本,這些變更都會影響運行於 Android 9 上的所有應用。
後臺對感測器的訪問受限
Android 9 限制後臺應用訪問使用者輸入和感測器資料的能力。 如果您的應用在執行 Android 9 裝置的後臺執行,系統將對您的應用採取以下限制:
如果您的應用需要在執行 Android 9 的裝置上檢測感測器事件,請使用前臺服務。
限制訪問通話記錄
Android 9 引入 CALL_LOG
許可權組並將 READ_CALL_LOG
、WRITE_CALL_LOG
和 PROCESS_OUTGOING_CALLS
許可權移入該組。 在之前的 Android 版本中,這些許可權位於 PHONE
許可權組。
對於需要訪問通話敏感資訊(如讀取通話記錄和識別電話號碼)的應用,該 CALL_LOG
如果您的應用需要訪問通話記錄或者需要處理去電,則您必須向 CALL_LOG
許可權組明確請求這些許可權。 否則會發生 SecurityException
。
注:因為這些許可權已變更組並在執行時授予,使用者可以拒絕您的應用訪問通話記錄資訊。 在這種情況下,您的應用應該能夠妥善處理無法訪問資訊的狀況。
如果您的應用已經遵循執行時許可權最佳做法,則可以處理許可權組的變更。
限制訪問電話號碼
在未首先獲得 READ_CALL_LOG
許可權的情況下,除了應用的用例需要的其他許可權之外,運行於 Android 9 上的應用無法讀取電話號碼或手機狀態。
與來電和去電關聯的電話號碼可在手機狀態廣播(比如來電和去電的手機狀態廣播)中看到,並可通過 PhoneStateListener
類訪問。 但是,如果沒有 READ_CALL_LOG
許可權,則 PHONE_STATE_CHANGED
廣播和 PhoneStateListener
提供的電話號碼欄位為空。
要從手機狀態中讀取電話號碼,請根據您的用例更新應用以請求必要的許可權:
- 要通過
PHONE_STATE
Intent 操作讀取電話號碼,同時需要READ_CALL_LOG
許可權和READ_PHONE_STATE
許可權。 - 要從
onCallStateChanged()
中讀取電話號碼,只需要READ_CALL_LOG
許可權。 不需要READ_PHONE_STATE
許可權。
限制訪問 Wi-Fi 位置和連線資訊
在 Android 9 中,應用進行 Wi-Fi 掃描的許可權要求比之前的版本更嚴格。 詳情請參閱 Wi-Fi 掃描限制。
類似的限制也適用於 getConnectionInfo()
函式,該函式返回描述當前 Wi-Fi 連線的 WifiInfo
物件。 如果呼叫應用具有以下許可權,則只能使用該物件的函式來檢索 SSID 和 BSSID 值:
- ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION
- ACCESS_WIFI_STATE
檢索 SSID 或 BSSID 還需要在裝置上啟用位置服務(在 Settings > Location 下)。
從 Wi-Fi 服務函式中移除的資訊
在 Android 9 中,下列事件和廣播不接收使用者位置或個人可識別資料方面的資訊:
WifiManager
中的getScanResults()
和getConnectionInfo()
函式。WifiP2pManager
中的discoverServices()
和addServiceRequest()
函式。NETWORK_STATE_CHANGED_ACTION
廣播。
Wi-Fi 的 NETWORK_STATE_CHANGED_ACTION
系統廣播不再包含 SSID(之前為 EXTRA_SSID)、BSSID(之前為 EXTRA_BSSID)或連線資訊(之前為 EXTRA_NETWORK_INFO)。 如果應用需要此資訊,請改為呼叫getConnectionInfo()
。
電話資訊現在依賴裝置位置設定
如果使用者在執行 Android 9 的裝置上停用裝置定位,則以下函式不提供結果:
對使用非 SDK 介面的限制
為幫助確保應用穩定性和相容性,此平臺對某些非 SDK 函式和欄位的使用進行了限制;無論您是直接訪問這些函式和欄位,還是通過反射或 JNI 訪問,這些限制均適用。 在 Android 9 中,您的應用可以繼續訪問這些受限的介面;該平臺通過 toast 和日誌條目提醒您注意這些介面。 如果您的應用顯示這樣的 toast,則必須尋求受限介面之外的其他實現策略。 如果您認為沒有可行的替代策略,您可以提交錯誤以請求重新考慮此限制。
對非 SDK 介面的限制包含了更多重要資訊。 您應查閱該資訊以確保您的應用繼續正常工作。
安全行為變更
裝置安全性變更
無論應用的目標平臺版本如何,Android 9 新增的若干功能均可令應用的安全性得到改善。
傳輸層安全協議 (TLS) 實現變更
系統的傳輸層安全協議 (TLS) 實現在 Android 9 中經歷了若干次變更:
- 如果
SSLSocket
的例項在建立時連線失敗,系統會引發IOException
而非NullPointerException
。 SSLEngine
類可正常處理出現的任何close_notify
提醒。
如需瞭解有關在 Android 應用中進行安全網路請求的更多資訊,請參閱一個 HTTPS 示例。
更嚴格的 SECCOMP 過濾器
Android 9 對可供應用使用的系統呼叫做了進一步限制。 此行為是 Android 8.0(API 級別 26)包含的 SECCOMP 過濾器的擴充套件。
注:此更改僅影響使用授權的系統呼叫的應用。
加密變更
Android 9 針對加密演算法的實現和處理引入了幾項變更。
引數和演算法的 Conscrypt 實現
Android 9 在 Conscrypt 中實現了更多的演算法引數。 這些引數包括: AES、DESEDE、OAEP 和 EC。 這些引數和許多演算法的 Bouncy Castle 版本自 Android 9 起已被棄用。
注:EC 引數的 Conscrypt 實現僅支援已命名的曲線。
如果您的應用以 Android 8.1(API 級別 27)或更低版本為目標,則在請求這些已棄用演算法之一的 Bouncy Castle 實現時,您將收到一條警告訊息。 然而,如果您以 Android 9 為目標平臺,則這些請求會各自引發 NoSuchAlgorithmException
。
其他變更
Android 9 引入了多項與加密有關的其他變更:
- 使用 PBE 金鑰時,如果 Bouncy Castle 需要初始化向量 (IV),而您的應用未提供 IV,則會收到一條警告訊息。
- ARC4 加密的 Conscrypt 實現允許您指定 ARC4/ECB/NoPadding 或 ARC4/NONE/NoPadding。
- Crypto Java 加密架構 (JCA) 提供程式現已被移除。 因此,如果您的應用呼叫
SecureRandom.getInstance("SHA1PRNG", "Crypto")
,將會發生NoSuchProviderException
。 - 如果您的應用從大於金鑰結構的緩衝區中解析 RSA 金鑰,將不會再發生異常。
如需瞭解有關使用 Android 的加密功能的更多資訊,請參閱加密。
不再支援 Android 安全加密檔案
Android 9 完全取消了對 Android 安全加密檔案 (ASEC) 的支援。
在 Android 2.2(API 級別 8)中,Android 引入了 ASEC 以支援 SD 卡載入應用功能。 在 Android 6.0(API 級別 23)上,平臺引入了一個可採用的儲存裝置 技術,開發者可用它來代替 ASEC。
ICU 庫更新
Android 9 使用 ICU 庫版本 60。 Android 8.0(API 級別 26)和 Android 8.1(API 級別 27)使用 ICU 58。
ICU 用於提供 android.icu package
下的公開 API, 供 Android 平臺內部用來提供國際化支援。 例如,它用於實現 java.util
、java.text
和 android.text.format
格式的 Android 類。
對 ICU 60 進行的更新包含許多細微但很有用的變更,這包括 Emoji 5.0 資料支援,改進了日期/時間格式,詳見 ICU 59 和 ICU 60 版本說明中的介紹。
本次更新中的顯著變更:
- 平臺處理時區的方式已發生更改。
- 平臺能夠更好地處理 GMT 和 UTC,不再將 UTC 與 GMT 混為一談。
ICU 現在提供 GMT 和 UTC 的翻譯版時區名稱。 此變更會影響“GMT”、“Etc/GMT”、“Etc/UTC”、“UTC”和“Zulu”之類時區的
android.icu
格式和解析行為。 java.text.SimpleDateFormat
現在使用 ICU 提供 UTC /GMT 的顯示名稱,這意味著:- 對於許多語言區域而言,設定
zzzz
的格式將會生成很長的本地化字串。之前,對於 UTC 時區,它會生成“UTC”,而對於 GMT,則會生成“GMT+00:00”之類的字串。 - 解析
zzzz
可識別“Universal Coordinated Time”和“Greenwich Mean Time”之類的字串。 - 在所有語言裡,如果應用接受“UTC”或“GMT+00:00”作為
zzzz
的輸出,則可能會遇到相容性問題。
- 對於許多語言區域而言,設定
java.text.DateFormatSymbols.getZoneStrings()
的行為已變更:- 與
SimpleDateFormat
類似,現在,UTC 和 GMT 也有長名稱。對於 UTC 時區,DST 型別的時區名稱(例如“UTC”、“Etc/UTC”和“Zulu”)變為 GMT+00:00(而不是硬編碼字串UTC
),這是在沒有其他名稱可用時的標準回退。 - 某些時區 ID 被正確地識別為其他地區的同義詞,因此,Android 能夠查詢過時時區 ID(例如
Eire
)對應的字串,而之前無法解決此問題。
- 與
- 亞洲/河內不再是可識別的時區。 因此,
java.util.TimeZones.getAvailableIds()
不再返回此值,java.util.TimeZone.getTimeZone()
也不再識別它。此行為與現有的android.icu
行為相符。
- 平臺能夠更好地處理 GMT 和 UTC,不再將 UTC 與 GMT 混為一談。
android.icu.text.NumberFormat.getInstance(ULocale, PLURALCURRENCYSTYLE).parse(String)
函式甚至在解析合法相應幣種文字時也會引發ParseException
。 通過對PLURALCURRENCYSTYLE
型別的相應幣種文字使用自 Android 7.0(API 級別 24)以來所提供的NumberFormat.parseCurrency
,可避免此問題。
Android Test 變更
Android 9 引入了多項針對 Android Test 框架庫和類結構的更改。 這些變更可幫助開發者使用支援框架的公共 API,此外,在使用第三方庫或自定義邏輯構建和執行測試時,這些變更還可提供更大的靈活性。
從框架移除的內容庫
Android 9 將基於 JUnit 的類重新整理成三個內容庫: android.test.base、android.test.runner 和 android.test.mock。 此變更允許您針對與您的專案依賴項搭配效果最好的 JUnit 版本執行測試。 此版本的 JUnit 可能不同於 android.jar
提供的版本。
如需瞭解有關如何將基於 JUnit 的類組織到這些內容庫中,以及如何準備您的應用專案以編寫和執行測試,請參閱針對 Android 測試設定專案。
測試套件版本號變更
移除了 TestSuiteBuilder
類中的 addRequirements()
函式,TestSuiteBuilder
類本身也已棄用。addRequirements()
函式要求開發者提供型別為隱藏 API 的引數,結果令 API 失效。
Java UTF 解碼器
UTF-8 是 Android 中的預設字符集。 UTF-8 位元組序列可由 String(byte[] bytes)
之類的 String
建構函式解碼。
Android 9 中的 UTF-8 解碼器遵循比以前版本中更嚴格的 Unicode 標準: 這些變更包括:
- 非最短形式的 UTF-8(例如
<C0, AF>
)被視為格式不正確。 - 替代形式的 UTF-8(例如
U+D800
..U+DFFF
)被視為格式不正確。 - 最大的子部分被單個
U+FFFD
取代。 例如,在位元組序列“41 C0 AF 41 F4 80 80 41
”中,最大子部分為“C0
”、“AF
”和“F4 80 80
”。其中“F4 80 80
”可以是“F4 80 80 80
”的初始子序列,但“C0
”不能是任何形式正確的程式碼單位序列的初始子序列。 因此,輸出應為“A\ufffd\ufffdA\ufffdA
”。 - 要在 Android 9 或更高版本中解碼修改後的 UTF-8/CESU-8 序列,請使用
DataInputStream.readUTF()
函式或NewStringUTF()
JNI 函式。
使用證書的主機名驗證
RFC 2818中介紹了兩種對照證書匹配域名的方法—使用 subjectAltName
(SAN
) 擴充套件程式中的可用名稱,或者在沒有SAN
擴充套件程式的情況下,回退到 commonName
(CN
)。
然而,在 RFC 2818 中,回退到 CN
已被棄用。因此,Android 不再回退到使用 CN
。 要驗證主機名,伺服器必須出示具有匹配 SAN
的證書。 不包含與主機名匹配的 SAN
的證書不再被信任。
網路地址查詢可能會導致網路違規
要求名稱解析的網路地址查詢可能會涉及網路 I/O,因此會被視為阻塞性操作。 對於主執行緒的阻塞性操作可能會導致停頓或卡頓。
StrictMode
類是一個有助於開發者檢測程式碼問題的開發工具。
在 Android 9 及更高版本中,StrictMode
可以檢測需要名稱解析的網路地址查詢所導致的網路違規。
您在交付應用時不應啟用 StrictMode
。 否則,您的應用可能會遭遇異常,例如,在使用 detectNetwork()
或 detectAll()
函式獲取用於檢測網路違規的政策時,會出現NetworkOnMainThreadException
。
解析數字 IP 地址不被視為阻塞性操作。 數字 IP 地址解析的工作方式與 Android 9 以前的版本中所採用的方式相同。
套接字標記
在低於 Android 9 的平臺版本上,如果使用 setThreadStatsTag()
函式標記某個套接字,則當使用帶 ParcelFileDescriptor
容器的 binder 程序間通訊將其傳送給其他程序時,套接字會被取消標記。
在 Android 9 及更高版本中,利用 binder 程序間通訊將套接字傳送至其他程序時,其標記將得到保留。 此變更可能影響網路流量統計,例如,使用 queryDetailsForUidTag()
函式時。
如果您要保留以前版本的行為,即取消已傳送至其他程序的套接字的標記,您可以在傳送此套接字之前呼叫 untagSocket()
。
報告的套接字中可用位元組數
在呼叫 shutdownInput()
函式後,available()
函式會在呼叫時返回 0
。
更詳盡的 VPN 網路功能報告
在 Android 8.1(API 級別 28)及更低版本中,NetworkCapabilities
類僅報告 VPN 的有限資訊,例如 TRANSPORT_VPN
,但會省略 NET_CAPABILITY_NOT_VPN
。 資訊有限導致難以確定使用 VPN 是否會導致對應用的使用者收費。 例如,檢查 NET_CAPABILITY_NOT_METERED
並不能確定底層網路是否按流量計費。
從 Android 9 及更高版本開始,當 VPN 呼叫 setUnderlyingNetworks()
函式時,Android 系統將會合並任何底層網路的傳輸和能力並返回 VPN 網路的有效網路能力作為結果。
在 Android 9 及更高版本中,已經檢查NET_CAPABILITY_NOT_METERED
的應用將收到關於 VPN 網路能力和底層網路的資訊。
應用不再能訪問 xt_qtaguid 資料夾中的檔案
從 Android 9 開始,不再允許應用直接讀取 /proc/net/xt_qtaguid
資料夾中的檔案。 這樣做是為了確保與某些根本不提供這些檔案的裝置保持一致。
依賴這些檔案的公開 API TrafficStats
和 NetworkStatsManager
繼續按照預期方式執行。 然而,不受支援的 cutils函式(例如 qtaguid_tagSocket()
)在不同裝置上可能不會按照預期方式執行 — 甚至根本不執行。
現在強制執行 FLAG_ACTIVITY_NEW_TASK 要求
在 Android 9 中,您不能從非 Activity 環境中啟動 Activity,除非您傳遞 Intent 標誌 FLAG_ACTIVITY_NEW_TASK
。 如果您嘗試在不傳遞此標誌的情況下啟動 Activity,則該 Activity 不會啟動,系統會在日誌中輸出一則訊息。
注:在 Android 7.0(API 級別 24)之前,標誌要求一直是期望的行為並被強制執行。 Android 7.0 中的一個錯誤會臨時阻止實施標誌要求。
螢幕旋轉變更
從 Android 9 開始,對縱向旋轉模式做出了重大變更。 在 Android 8.0(API 級別 26)中,使用者可以使用 Quicksettings 圖塊或 Display 設定在自動螢幕旋轉和縱向旋轉模式之間切換。 縱向模式已重新命名為旋轉鎖定,它會在自動螢幕旋轉關閉時啟用。 自動螢幕旋轉模式沒有任何變更。
當裝置處於旋轉鎖定模式時,使用者可將其螢幕鎖定到頂層可見 Activity 所支援的任何旋轉。 Activity 不應假定它將始終以縱向呈現。 如果頂層 Activity 可在自動螢幕旋轉模式下以多種旋轉呈現,則應在旋轉鎖定模式下提供相同的選項,根據 Activity 的 screenOrientation
設定,允許存在一些例外情況(見下表)。
請求特定螢幕方向(例如,screenOrientation=landscape
)的 Activity 會忽略使用者鎖定首選項,並且行為與 Android 8.0 中的行為相同。
可在 Android Manifest 中,或以程式設計方式通過 setRequestedOrientation() 在 Activity 級別設定螢幕方向首選項。
旋轉鎖定模式通過設定 WindowManager 在處理 Activity 旋轉時使用的使用者旋轉首選項來發揮作用。 使用者旋轉首選項可能在下列情況下發生變更。 請注意,恢復裝置的自然旋轉存在偏差,對於外形與手機類似的裝置通常設定為縱向:
- 當用戶接受旋轉建議時,旋轉首選項變為建議方向。
- 當用戶切換到強制縱向應用(包括鎖定螢幕或啟動器)時,旋轉首選項變為縱向。
下表總結了常見螢幕方向的旋轉行為:
螢幕方向 | 行為 |
---|---|
未指定、user | 在自動螢幕旋轉和旋轉鎖定下,Activity 可以縱向或橫向(以及顛倒縱向或橫向)呈現。 預期同時支援縱向和橫向佈局。 |
userLandscape | 在自動螢幕旋轉和旋轉鎖定下,Activity 可以橫向或顛倒橫向呈現。 預期只支援橫向佈局。 |
userPortrait | 在自動螢幕旋轉和旋轉鎖定下,Activity 可以縱向或顛倒縱向呈現。 預期只支援縱向佈局。 |
fullUser | 在自動螢幕旋轉和旋轉鎖定下,Activity 可以縱向或橫向(以及顛倒縱向或橫向)呈現。 預期同時支援縱向和橫向佈局。 旋轉鎖定使用者將可選擇鎖定到顛倒縱向,通常為 180º。 |
sensor、fullSensor、sensorPortrait、sensorLandscape | 忽略旋轉鎖定模式首選項,視為自動螢幕旋轉已啟用。 請僅在例外情況下並經過仔細的使用者體驗考量後再使用此項。 |
Apache HTTP 客戶端棄用影響採用非標準 ClassLoader 的應用
在 Android 6.0 中,我們取消了對 Apache HTTP 客戶端的支援。
此變更對大多數不以 Android 9 或更高版本為目標的應用沒有任何影響。 不過,此變更會影響使用非標準 ClassLoader
結構的某些應用,即使這些應用不以 Android 9 或更高版本為目標平臺。
如果應用使用顯式委託到系統 ClassLoader
的非標準 ClassLoader
,則應用會受到影響。 在 org.apache.http.*
中查詢類時,這些應用需要委託給應用 ClassLoader
。 如果它們委託給系統 ClassLoader
,則應用在 Android 9 或更高版本上將失敗並顯示 NoClassDefFoundError
,因為系統 ClassLoader
不再識別這些類。 為防止將來出現類似問題,一般情況下,應用應通過應用 ClassLoader
載入類,而不是直接訪問系統 ClassLoader
。
列舉相機
在 Android 9 裝置上執行的應用可以通過呼叫 getCameraIdList()
發現每個可用的攝像頭。 應用不應假定裝置只有一個後置攝像頭或只有一個前置攝像頭。
例如,如果您的應用有一個用來切換前置和後置攝像頭的按鈕,則裝置可能有多個前置或後置攝像頭可供選擇。 您應瀏覽一下攝像頭列表,檢查每個攝像頭的特徵,然後決定向使用者顯示哪些攝像頭。