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)引入的更改之一 延长电池续航时间 已缓存 状态,没有活跃状态 组件, 系统会释放应用保持的所有唤醒锁定。

此外,为了提高设备性能,系统会限制 未在前台运行的应用的行为。具体而言:

  • 在后台运行的应用现在在自由度方面会受到限制 才能访问后台服务。
  • 应用无法使用其清单注册大多数隐式广播 (即,并非专门针对该应用的广播)。

默认情况下,这些限制仅适用于针对 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
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)引入了以下与安装 来源不明的未知应用:

如需进一步了解如何安装未知应用,请参阅 未知应用 安装权限指南。

如需了解提高应用安全性的其他准则,请参阅 面向 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

  • 查询 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.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)中,媒体按钮事件的处理有所不同: <ph type="x-smartling-placeholder">
      </ph>
    1. 媒体按钮的处理 未发生变化:前台 activity 在处理时仍享有优先权 媒体按钮事件。
    2. 如果前台 activity 不处理媒体按钮事件,系统会路由该事件 最近在本地播放音频的应用。活动状态、标记和播放 在确定哪个应用接收媒体时不考虑媒体会话的状态 按钮事件。
    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)中是 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 现已弃用,强烈建议您使用 其中一个平台类加载器,包括 PathClassLoaderBaseDexClassLoader

注意 :您可以创建多个引用 相同的 APK 或 JAR 文件容器。这样做通常不会 会产生很多内存开销:如果存储的是容器中的 DEX 文件,而不是 那么平台可以对其执行 mmap 操作,而不是 直接提取它们。不过,如果平台必须从容器中提取 DEX 文件, 以这种方式引用 DEX 文件可能会消耗大量内存。

在 Android 中,所有类加载器都被视为支持并行运行。 多个线程竞态加载包含同一类的同一类 加载器,第一个完成此操作的线程胜出,而结果将用于 其他线程。无论类加载器是 返回了相同的类,返回了不同的类,或者抛出了异常。 平台会以静默方式忽略此类异常。

注意 :在各版本的平台中 低于 Android 8.0(API 级别 26),那么打破这些假设可以定义相同的 类多次损坏、由于类混淆而导致的堆损坏、 和其他不良后果。