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
权限。
限制了对 WLAN 位置信息和连接信息的访问
在 Android 9 中,应用执行 Wi-Fi 扫描的权限要求比以前的版本更严格。如需了解详情,请参阅 Wi-Fi 扫描限制。
类似的限制也适用于 getConnectionInfo()
方法,该方法会返回描述当前 Wi-Fi 连接的 WifiInfo
对象。仅当发起调用的应用具有以下权限时,才能使用此对象的方法来检索 SSID 和 BSSID 值:
- ACCESS_FINE_LOCATION或 ACCESS_COARSE_LOCATION
- ACCESS_WIFI_STATE
如要检索 SSID 或 BSSID,还需要在设备上启用位置信息服务(在设置 > 位置信息下)。
从 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 接口的限制
为了帮助确保应用的稳定性和兼容性,Android 平台会限制使用某些非 SDK 方法和字段;无论您是尝试直接访问这些方法和字段,还是通过反射或 JNI 进行访问,这些限制都适用。在 Android 9 中,您的应用可以继续访问这些受限接口;平台会使用消息框和日志条目提醒您注意这些接口。如果您的应用显示此类消息框,请务必寻求受限接口以外的实现策略。如果您认为没有替代策略可行,可以提交 bug 请求重新审核限制。
针对非 SDK 接口的限制包含更多重要信息。您应查看该准则,确保您的应用继续正常运行。
安全行为变更
设备安全性变更
无论应用的目标平台版本如何,Android 9 增加了若干功能,它们都可以提升应用的安全性。
TLS 实现变更
系统的 TLS 实现在 Android 9 中经历了多次更改:
- 如果
SSLSocket
的实例在创建时连接失败,系统会抛出IOException
,而不是NullPointerException
。 SSLEngine
类会正常处理发生的所有close_notify
提醒。
如需详细了解如何在 Android 应用中发出安全的 Web 请求,请参阅 HTTPS 示例。
更严格的 SECCOMP 过滤器
Android 9 进一步限制了可供应用使用的系统调用。此行为是 Android 8.0(API 级别 26)包含的 SECCOMP 过滤器的扩展。
加密变更
Android 9 针对加密算法的实现和处理引入了几项变更。
参数和算法的 Conscrypt 实现
Android 9 在 Conscrypt 中提供了算法参数的其他实现。这些参数包括:AES、DESEDE、OAEP 和 EC。从 Android 9 开始,这些参数的 Bouncy Castle 版本和许多算法都已被废弃。
如果您的应用以 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)中,Android 平台引入了可合并的存储设备技术,开发者可以使用该技术来代替 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”、“UTC”、“Etc/UTC”和“Zulu”等区域的
android.icu
格式和解析行为。 java.text.SimpleDateFormat
现在使用 ICU 提供 UTC /GMT 的显示名称,这意味着:- 设置
zzzz
格式会为许多语言区域生成本地化的长字符串。以前,对于 UTC 时区,它会生成“UTC”,而对于 GMT,则会生成“GMT+00:00”之类的字符串。 - 解析
zzzz
可识别“通用协调时间”和“格林尼治标准时间”等字符串。 - 如果应用在所有语言中都假定
zzzz
的输出为“UTC”或“GMT+00:00”,则可能会遇到兼容性问题。
- 设置
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 Test 设置项目。
测试套件 build 变更
移除了 TestSuiteBuilder
类中的 addRequirements()
方法,废弃了 TestSuiteBuilder
类本身。addRequirements()
方法要求开发者提供类型为隐藏 API 的参数,从而导致 API 无效。
Java UTF 解码器
UTF-8 是 Android 中的默认字符集。UTF-8 字节序列可由 String
构造函数(如 String(byte[] bytes)
)解码。
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 IPC 将其发送到另一个进程时,套接字会被取消标记。
在 Android 9 及更高版本中,使用 Binder IPC 将其发送到另一个进程时,系统会保留套接字标记。此更改可能会影响网络流量统计信息,例如,使用 queryDetailsForUidTag()
方法时。
如果您想保留之前版本的行为(即对发送到其他进程的套接字取消标记),您可以在发送套接字之前调用 untagSocket()
。
报告的套接字中的可用字节数
如果在调用 shutdownInput()
方法后调用 available()
方法,该方法会返回 0
。
更详细的 VPN 网络功能报告
在 Android 8.1(API 级别 27)及更低版本中,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 中,除非您传递 intent 标志 FLAG_ACTIVITY_NEW_TASK
,否则无法从非 activity 上下文启动 activity。如果您尝试在不传递此标志的情况下启动 activity,则该 activity 不会启动,并且系统会在日志中输出一条消息。
屏幕旋转更改
从 Android 9 开始,纵向旋转模式发生了重大变化。在 Android 8.0(API 级别 26)中,用户可以使用“快捷设置”图块或“显示”设置在自动屏幕旋转和纵向旋转模式之间切换。纵向模式已重命名为旋转锁定,在自动屏幕旋转处于关闭状态时,它会处于启用状态。自动屏幕旋转模式没有任何变化。
当设备处于旋转锁定模式时,用户可以将其屏幕锁定到顶层可见 activity 支持的任何旋转方式。activity 不应假定它始终以纵向呈现。如果顶层 activity 可在自动屏幕旋转模式下以多种旋转方式呈现,则应在旋转锁定模式下提供相同的选项,但会因 activity 的 screenOrientation
设置而有所不同(见下表)。
请求特定屏幕方向(例如,screenOrientation=landscape
)的 activity 会忽略用户锁定偏好设置,并且行为与 Android 8.0 中相同。
您可以在 Android 清单中的 activity 级别设置屏幕方向偏好设置,也可以使用 setRequestedOrientation() 以编程方式设置。
旋转锁定模式通过设置 WindowManager 在处理 activity 旋转时使用的用户旋转偏好设置来发挥作用。在以下情况下,用户旋转偏好设置可能会发生变化。请注意,恢复设备的自然旋转是存在偏差的,对于外形规格为手机的设备,自然旋转通常是纵向:
- 当用户接受旋转建议时,旋转偏好设置将更改为建议。
- 当用户切换到强制纵向应用(包括锁定屏幕或启动器)时,旋转偏好设置将更改为纵向。
下表总结了常见屏幕方向的旋转行为:
屏幕方向 | 行为 |
---|---|
未指定, 用户 | 在自动屏幕旋转和旋转锁定下,activity 可以纵向或横向(以及反向)呈现。预期同时支持纵向和横向布局。 |
用户景观 | 在自动屏幕旋转和旋转锁定下,activity 可以横向或反向横向呈现。预期只支持横向布局。 |
用户肖像 | 在自动屏幕旋转和旋转锁定下,activity 可以纵向或反向纵向呈现。预期只支持纵向布局。 |
完整用户 | 在自动屏幕旋转和旋转锁定下,activity 可以纵向或横向(以及反向)呈现。预期同时支持纵向和横向布局。 旋转锁定用户将可以选择锁定为反向纵向,通常为 180o。 |
传感器、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()
发现每个可用的摄像头。应用不应假设设备只有一个后置摄像头或只有一个前置摄像头。
例如,如果您的应用具有用于在前置摄像头和后置摄像头之间切换的按钮,则可能会有多个前置或后置摄像头可供选择。您应浏览摄像头列表,检查每个摄像头的特性,并决定向用户显示哪些摄像头。