Android 8.0(API 级别 26)以及其他新功能 包括对系统和 API 行为的各种变更。此文档 会重点介绍您应该了解并加以考虑的一些关键变更 。
这些变更大多会影响所有应用,无论应用的 目标 Android 设备不过,有几项变更仅影响应用定位 Android 8.0。为清楚起见, 页面分为两个部分:针对所有应用的变更和针对应用定位的变更 Android 8.0。
针对所有应用的变更
这些行为变更将应用于
后台执行限制
作为 Android 8.0(API 级别 26)引入的更改之一 延长电池续航时间 已缓存 状态,没有活跃状态 组件, 系统会释放应用保持的所有唤醒锁定。
此外,为了提高设备性能,系统会限制 未在前台运行的应用的行为。具体而言:
- 在后台运行的应用现在在自由度方面会受到限制 才能访问后台服务。
- 应用无法使用其清单注册大多数隐式广播 (即,并非专门针对该应用的广播)。
默认情况下,这些限制仅适用于针对 O 的应用。不过, 用户可以通过设置屏幕为任何应用启用这些限制, 即使应用并非以 O 为目标平台。
Android 8.0(API 级别 26)还包含对特定方法的以下更改:
startService()
方法现在会抛出IllegalStateException
(如果应用) 以 Android 8.0 为目标平台的组件尝试使用此方法 在不允许创建后台服务的情况下使用。- 新的
Context.startForegroundService()
方法会启动 前台服务。系统允许应用 调用Context.startForegroundService()
,即使应用正在运行 不过,应用必须在五内调用该服务的startForeground()
方法 秒。
如需了解详情,请参阅 后台执行限制。
Android 后台位置限制
为了节约电池电量、保证用户体验和确保系统健康运行, 在设备上使用后台应用接收位置信息更新的频率较低 搭载 Android 8.0。此行为变更会影响所有应用 接收位置信息更新的应用,包括 Google Play 服务。
这些更改会影响以下 API:
- Fused Location Provider (FLP)
- 地理围栏
- GNSS Measurements
- Location Manager
- Wi-Fi Manager
为确保您的应用按预期方式运行,请完成以下步骤:
- 查看应用的逻辑,确保您使用的是最新位置信息 API。
- 测试应用在每次使用时表现出的行为是否符合预期 这种情况。
- 您可以考虑使用 融合 位置信息提供程序 (FLP) 或地理围栏来处理依赖于 用户的当前位置。
如需详细了解这些变化,请参阅 后台位置信息 限制。
应用快捷方式
Android 8.0(API 级别 26)包含对应用快捷方式的以下更改:
com.android.launcher.action.INSTALL_SHORTCUT
广播编号 因为现在它是私有的隐式 广播。您应改为使用requestPinShortcut()
方法中ShortcutManager
类。ACTION_CREATE_SHORTCUT
intent 现在可以创建应用快捷方式,您可以使用ShortcutManager
类。此 intent 还可以 不与ShortcutManager
。以前,此 intent 可以 仅创建旧版启动器快捷方式。- 使用 创建的快捷指令
requestPinShortcut()
在负责处理ACTION_CREATE_SHORTCUT
intent 现已成为成熟的应用快捷方式。因此,应用现在可以更新它们 使用ShortcutManager
中的方法。 - 旧版快捷方式保留了其在旧版 API 中的功能 Android 版,但您必须在应用中手动将它们转换为应用快捷方式。
如需详细了解应用快捷方式的变化,请参阅 固定快捷方式和 微件功能指南。
语言区域和国际化
Android 7.0(API 级别 24)引入了能够
但某些 API 继续使用
通用 Locale.getDefault()
方法,而不使用参数。DISPLAY
在 Android 8.0(API 级别 26)中,
以下方法现在使用 Locale.getDefault(Category.DISPLAY)
而不是 Locale.getDefault()
:
还有Locale.getDisplayScript(Locale)
当Locale.getDefault()
为 Locale
指定 displayScript 值
参数不可用。
与语言区域和国际化相关的其他更改如下: 如下:
- 调用
Currency.getDisplayName(null)
会抛出NullPointerException
, 与所载述的行为相符。 - 时区名称解析已更改。以前,
Android 设备使用了启动时采样的系统时钟值
缓存用于解析日期的时区名称的时间
次。因此,如果系统运行多个请求,
时钟在启动时或其他较为罕见的情况下出错。
现在,在一般情况下,解析逻辑使用 ICU, 当前系统时钟值。这个 提供的结果更准确,可能与之前的结果不同 Android 版本(如果您的应用使用
SimpleDateFormat
。 - 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
。此焦点突出显示标志是一个涟漪图, 根据 activity 的主题确定。如果您不希望
View
对象使用此默认值 突出显示,将 将“android:defaultFocusHighlightEnabled
”属性设为false
(包含View
,或将false
传入setDefaultFocusHighlightEnabled()
。 - 要测试键盘输入对界面元素焦点有何影响,您可以启用 绘图 >显示布局边界开发者选项。在 Android 中 8.0,此选项会显示“X”图标。 关注度。
此外,Android 8.0 中的所有工具栏元素都会自动 键盘导航键区, 用户可以更轻松地以列表形式 整体。
详细了解如何改进对 Google Workspace 中的键盘导航功能的支持 请阅读支持 键盘导航指南。
网页表单自动填充
现在,Android 自动填充
框架内置了对自动填充功能、
与 WebView
对象相关的以下方法已更改
对于安装在搭载 Android 8.0(API 级别 26)的设备上的应用:
WebSettings
-
- 通过
getSaveFormData()
方法现在会返回false
。以前,此方法会返回true
。 - 正在呼叫
setSaveFormData()
人已拒绝 不会产生任何影响
- 通过
WebViewDatabase
-
- 正在呼叫
clearFormData()
人已拒绝 不会产生任何影响 - 通过
hasFormData()
种方式 现在返回false
。以前,此方法会返回 如果表单包含数据,则返回true
。
- 正在呼叫
无障碍
Android 8.0(API 级别 26)对无障碍功能进行了以下更改:
-
无障碍功能框架现在会将所有点按两次手势转换为
ACTION_CLICK
操作。经过此次变更,TalkBack 可以更像其他产品 无障碍服务。如果应用的
View
对象使用自定义触控功能 处理时,您应验证它们是否仍可与 TalkBack 配合使用。您可能会 只需注册View
的点击处理程序 对象使用的对象。如果 TalkBack 仍无法识别对这些设备执行的手势View
对象,覆盖performAccessibilityAction()
。 - 无障碍服务如今
ClickableSpan
个实例TextView
对象。
如需详细了解如何让您的应用使用起来更没有障碍,请参阅 无障碍功能。
网络连接和 HTTP(S) 连接
Android 8.0(API 级别 26)包含对 网络和 HTTP(S) 连接:
- 没有正文的 OPTIONS 请求具有
Content-Length: 0
标头。之前,它们没有Content-Length
标头。 - Http网址Connection 通过将
主机或授权机构名称后面带有斜杠。例如,
将
http://example.com
转换为http://example.com/
。 - 通过 ProxySelector.setDefault() 设置的自定义代理选择器 仅定位所请求网址的地址(架构、主机和端口)。 因此,只能根据这些值选择代理。网址 不包含所请求网址的 查询参数或片段。
- URI 不能包含空标签。
以前,该平台支持一种临时解决方法,可在 主机名,而这是非法的 URI 使用。此解决方法适用于 与旧版 libcore 的兼容性。使用 API 的开发者 不正确地看到 ADB 消息:“URI example..com 在 主机名此格式不正确,在未来的 Android 中将不再接受 发布。” Android 8.0 移除了这一权宜解决方法;系统会返回 null。
- Android 8.0 的 Https网址Connection 实现 不会执行不安全的 TLS/SSL 协议版本回退。
- 隧道 HTTP(S) 连接的处理方式已发生以下变化:
<ph type="x-smartling-placeholder">
- </ph>
- 在通过连接建立 HTTPS 连接时,系统 在发送 将这些信息发送给中间服务器以前,端口 数字只出现在 CONNECT 行中。
- 系统不再发送用户代理和代理授权
标头。
系统不再在 建立连结到代理的 隧道。系统会改为生成一个代理授权标头 并在代理发送 响应初始请求的 HTTP 407。
同样,系统也不再复制用户代理标头 从隧道式请求传送到设置 隧道。相反,库会生成一个用户代理标头 请求。
send(java.net.DatagramPacket)
如果之前执行的 connect() 函数,该方法将抛出 SocketException 方法失败。- 如果存在超时,DatagramSocket.connect() 会设置 pendingSocketException 内部错误。对于 Android 8.0 之前的版本,后续的 recv() 调用抛出 SocketException,即使 send() 调用会成功也是如此。 为确保一致性,现在这两个调用均会引发 SocketException。
- InetAddress.isReachable() 会在回退到 TCP Echo 之前尝试 ICMP
协议。
- 某些屏蔽端口 7 (TCP Echo) 的主机(如 google.com) 如果它们接受 ICMP Echo 协议,它们现在就可以访问。
- 对于确实无法访问的主机,这一变化意味着 调用返回前所用的时间。
蓝牙
Android 8.0(API 级别 26)对
ScanRecord.getBytes()
方法会检索以下内容:
getBytes()
方法 接收的字节数。因此,应用不应依赖于 返回的字节数下限或上限。相反,他们应评估 所得数组的长度。- 兼容蓝牙 5 的设备返回的数据长度可能会超出 长度上限为约 60 个字节。
- 如果远程设备不提供扫描响应,则少于 60 个字节 可能也会返回
无缝连接
Android 8.0(API 级别 26)对 WLAN 设置进行了多项改进,以便更轻松地选择 可提供最佳用户体验的 Wi-Fi 网络。具体更改包括:
- 稳定性和可靠性改进。
- 更加直观的界面。
- 一个合并的 WLAN 首选项菜单。
- 在兼容的设备上,当保存的高品质网络时自动激活 WLAN 就在附近。
安全
Android 8.0 包含以下与安全相关的 更改:
- 此平台不再支持 SSLv3。
- 当建立与错误服务器之间的 HTTPS 连接时,
实施 TLS 协议版本协商
HttpsURLConnection
不再尝试此解决方法 或回退到早期的 TLS 协议版本并重试。 - Android 8.0(API 级别 26)采用安全计算 (SECCOMP) 过滤出所有应用。允许的系统调用列表仅限于 暴露的环境。虽然还有一些其他系统调用, 以实现向后兼容性,我们建议不要使用此类 ID。
- 您应用的
WebView
对象现在会在多进程中运行 模式。Web 内容在独立的进程中处理,该进程与 包含应用的增强安全性流程。 -
您无法再假定 APK 位于名称结尾的目录中
以 -1 或 -2 表示。应用应使用
sourceDir
,以获取 目录,而不是直接依赖于目录格式。 - 想要了解与使用原生代码相关的 库,请参阅原生库。
此外,Android 8.0(API 级别 26)引入了以下与安装 来源不明的未知应用:
- 旧版设置的值
“
INSTALL_NON_MARKET_APPS
”现为 始终为 1。要确定未知来源能否使用 您应改为使用canRequestPackageInstalls()
。 - 如果您尝试将
INSTALL_NON_MARKET_APPS
正在使用setSecureSetting()
,一个UnsupportedOperationException
。防止用户使用未知方式安装未知应用 来源,您应该将DISALLOW_INSTALL_UNKNOWN_SOURCES
位用户 限制。 -
在搭载 Android 8.0(API 级别 26)的设备上创建的受管理个人资料会自动拥有
DISALLOW_INSTALL_UNKNOWN_SOURCES
位用户 限制。对于符合以下条件的设备上现有的受管理个人资料: 升级到 Android 8.0DISALLOW_INSTALL_UNKNOWN_SOURCES
位用户 系统会自动启用限制,除非商家资料所有者明确 通过设置 将值从INSTALL_NON_MARKET_APPS
更改为 1。
如需进一步了解如何安装未知应用,请参阅 未知应用 安装权限指南。
如需了解提高应用安全性的其他准则,请参阅 面向 Android 开发者的安全功能。
隐私设置
Android 8.0(API 级别 26)提供了以下与隐私相关的 对平台所做的更改
- 平台现在采用不同的方式处理标识符。
-
对于在 OTA 之前安装的某个版本
Android 8.0(API 级别 26)
(API 级别 26),
ANDROID_ID
保持不变 除非卸载,然后在 OTA 后重新安装。要保留 因此开发者可以使用 <ph type="x-smartling-placeholder"></ph> 键值对备份。 - 对于安装在搭载 Android 8.0 的设备上的应用,
ANDROID_ID
现已限定为 应用签名密钥和每位用户的费用 “ANDROID_ID
”是唯一的 应用签名密钥、用户和设备的每个组合。 因此,具有不同签名密钥的应用在同一设备上运行。 无法再看到相同的 Android ID(即使对于同一用户)。 ANDROID_ID
的值 安装或重新安装软件包时,只要 签名密钥相同(并且应用在 OTA 之前未安装 Android 8.0 版)。ANDROID_ID
的值 不会更改,即使系统更新导致软件包签名密钥更改也是如此。- 在附带 Google Play 服务和广告 ID 的设备上,
必须使用
<ph type="x-smartling-placeholder"></ph>
广告 ID。通过简单的标准系统来利用应用变现
广告 ID 是针对广告服务提供的唯一 ID,可由用户重置。它已提供
由 Google Play 服务提供
其他设备制造商应继续 以提供
ANDROID_ID
。
-
对于在 OTA 之前安装的某个版本
Android 8.0(API 级别 26)
(API 级别 26),
- 查询
net.hostname
系统属性会生成 null 结果。
记录未捕获的异常
如果应用安装的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 源语言时,需要显式转换为
View
,当返回值类型不受限制(例如,assertNotNull(findViewById(...)).someViewMethod())
。 - 替换非最终
findViewById()
方法(适用于 (例如Activity.findViewById()
)需要其退货 类型已更新。
联系人提供程序使用情况统计方法的变更
在之前的 Android 版本中,联系人提供程序组件
让开发者能够获取每个联系人的使用情况数据。这些使用情况数据
显示每个电子邮件地址以及与其关联的每个电话号码的信息
包括该联系人与其联系的次数
以及上次联系该联系人的时间会请求
READ_CONTACTS
权限可以读取此数据。
应用仍可在请求获取此数据后读取这些数据
READ_CONTACTS
权限。在 Android 8.0(API 级别 26)及更高版本中,对使用情况数据的查询会返回
近似值,而不是精确值。Android 系统会维护
因此,此更改不会影响
自动完成 API。
此行为变更会影响以下查询参数:
集合的处理
AbstractCollection.removeAll()
和AbstractCollection.retainAll()
现在始终抛出 NullPointerException
;之前,
对该集合执行下列操作时未抛出 NullPointerException
:
为空。此更改使行为与文档保持一致。
Android 企业版
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 现在要求有一个有效的 ContentProvider
为所有 URI 中的授权方定义。使用相关权限定义有效的 ContentProvider
有助于保护您的应用免受恶意应用的内容变更的危害,并防止您
避免将潜在的私密数据泄露给恶意应用
视图焦点
可点击的 View
对象现在也可由以下对象聚焦:
默认值。如果您希望 View
对象可点击但不可点击
可聚焦,请设置
<ph type="x-smartling-placeholder"></ph>
在布局中将 android:focusable
属性设为 false
包含 View
的 XML 文件,或传入 false
添加到应用界面中的 setFocusable()
逻辑。
浏览器检测中的用户代理匹配
Android 8.0(API 级别 26)及更高版本包含
build 标识符字符串 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)中的安全性:
- 如果您的应用的网络安全配置
Opts
支持明文流量
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 和新增的账号访问和方法的信息, 请参阅账号访问权限 和可检测性一文。
隐私设置
以下变更会影响 Android 8.0(API 级别 26)中的隐私权。
-
系统属性
net.dns1
、net.dns2
、net.dns3
和net.dns4
已不再 推出这项变更,旨在加强平台上的隐私保护力度。 -
为了获取网络信息,例如 DNS 服务器、
ACCESS_NETWORK_STATE
权限可以注册NetworkRequest
或NetworkCallback
对象。 这些类可在 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_STORAGE
和
WRITE_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)中,媒体按钮事件的处理有所不同:
<ph type="x-smartling-placeholder">
- </ph>
- 媒体按钮的处理 未发生变化:前台 activity 在处理时仍享有优先权 媒体按钮事件。
- 如果前台 activity 不处理媒体按钮事件,系统会路由该事件 最近在本地播放音频的应用。活动状态、标记和播放 在确定哪个应用接收媒体时不考虑媒体会话的状态 按钮事件。
- 如果应用的媒体会话已释放,
系统会将媒体按钮事件发送到应用的
MediaButtonReceiver
(如果有)。 - 对于任何其他情况,系统都会舍弃媒体按钮事件。
原生库
在以 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)中是 true:
List.sort()
的默认实现
名为 Collections.sort()
。
这项变更允许Collections.sort()
以充分利用经过优化的List.sort()
但具有以下限制:
List.sort()
的实现 不得调用Collections.sort()
, 因为这样做会导致堆栈溢出 无限循环。相反,如果您希望默认行为 在List
实现中,应避免重写sort()
。如果父类以不当的方式实现
sort()
, 通常可以替换List.sort()
其实现方式基于List.toArray()
,Arrays.sort()
和ListIterator.set()
。例如:@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); } }
如果你只因为你想要
sort()
而选择后者 方法,请考虑为其指定一个唯一的名称, 例如sortCompat()
,而不是替换sort()
。-
Collections.sort()
现在计为 结构调整 列出调用sort()
的实现。例如,在 Android 8.0(API 级别 26)之前的平台,迭代 一个ArrayList
并对其调用sort()
在迭代过程中 则会抛出ConcurrentModificationException
如果排序已完成 调用List.sort()
。Collections.sort()
不会抛出异常。此更改使平台行为更加一致: 方法现在会导致
ConcurrentModificationException
。
类加载行为
Android 8.0(API 级别 26)检查以确保类加载器不会
打破了加载新类时对运行时的假设。这些检查
是否从 Java(从
forName()
)、
Dalvik 字节码,简称 JNI。平台不会拦截从 Java 到
loadClass()
方法,也不会检查
此类调用的结果。此行为不应影响正常运转的运作
类加载器。
平台会检查类加载器返回的类的描述符是否
与预期的描述符一致。如果返回的描述符与所返回的描述符不匹配,
平台会抛出 NoClassDefFoundError
错误,并存储到
异常消息,说明其中存在的差异。
平台还会检查所请求类的描述符是否有效。这个
可捕获间接加载 GetFieldID()
、
向这些类传递无效的描述符。例如,带有签名的字段
找不到 java/lang/String
,因为该签名无效;
它应为 Ljava/lang/String;
。
这与 JNI 对 FindClass()
的调用不同
其中 java/lang/String
是有效的完全限定名称。
Android 8.0(API 级别 26)不支持使用多个类加载器尝试定义类
同一个 DexFile 对象。尝试这样做会导致 Android 运行时抛出
InternalError
错误消息“尝试注册 dex 文件 <filename>
”
“使用多个类加载器”。
DexFile API 现已弃用,强烈建议您使用
其中一个平台类加载器,包括 PathClassLoader
或
BaseDexClassLoader
。
注意 :您可以创建多个引用
相同的 APK 或 JAR 文件容器。这样做通常不会
会产生很多内存开销:如果存储的是容器中的 DEX 文件,而不是
那么平台可以对其执行 mmap
操作,而不是
直接提取它们。不过,如果平台必须从容器中提取 DEX 文件,
以这种方式引用 DEX 文件可能会消耗大量内存。
在 Android 中,所有类加载器都被视为支持并行运行。 多个线程竞态加载包含同一类的同一类 加载器,第一个完成此操作的线程胜出,而结果将用于 其他线程。无论类加载器是 返回了相同的类,返回了不同的类,或者抛出了异常。 平台会以静默方式忽略此类异常。
注意 :在各版本的平台中 低于 Android 8.0(API 级别 26),那么打破这些假设可以定义相同的 类多次损坏、由于类混淆而导致的堆损坏、 和其他不良后果。