Android O 行为变更(一)

文章摘要
1、后台执行限制
2、Android 后台位置限制
3、应用快捷键
4、语言区域和国际化
5、提醒窗口
6、输入和导航
7、网页表单自动填充
8、无障碍功能
9、网络连接和 HTTP(S) 连接
10、蓝牙
11、无缝连接
12、安全性
13、隐私性
14、记录未捕获的异常
15、联系人提供程序使用情况统计方法的变更
16、集合的处理
17、Android 企业版


英文文献

Android O 除了提供诸多新特性和功能外,还对系统和 API 行为做出了各种变更。本文重点介绍您应该了解并在开发应用时加以考虑的一些主要变更。

其中大部分变更会影响所有应用,而不论应用针对的是何种版本的 Android。不过,有几项变更仅影响针对 Android O 的应用。为清楚起见,本页面分为两个部分:针对所有 API 级别的应用针对 Android O 的应用

一、针对所有 API 级别的应用

这些行为变更适用于 在 Android O 平台上运行的 所有应用,无论这些应用是针对哪个 API 级别构建。所有开发者都应查看这些变更,并修改其应用以正确支持这些变更(如果适用)。

1.1、后台执行限制

Android O 为提高电池续航时间而引入的变更之一是,当您的应用进入已缓存状态时,如果没有活动的组件,系统将解除应用具有的所有唤醒锁。
此外,为提高设备性能,系统会限制未在前台运行的应用的某些行为。具体而言:

  • 现在,在后台运行的应用对后台服务的访问受到限制。
  • 应用无法使用其清单注册大部分隐式广播(即,并非专门针对此应用的广播)。

默认情况下,这些限制仅适用于针对 O 的应用。不过,用户可以从 Settings --> Display 为任意应用启用这些限制,即使应用并不是以 O 为目标平台。

Android O 还对特定函数做出了以下变更:

  • 如果针对 Android O 的应用尝试在不允许其创建后台服务的情况下使用 startService() 函数,则该函数将引发一个 IllegalStateException
  • 新的 Context.startForegroundService() 函数将启动一个前台服务。现在,即使应用在后台运行,系统也允许其调用 Context.startForegroundService()。不过,应用必须在创建服务后的五秒内调用该服务的 startForeground() 函数。

1.2、Android 后台位置限制

为节约电池电量、保持良好的用户体验和确保系统健康运行,在运行 Android O 的设备上使用后台应用时,降低了后台应用接收位置更新的频率。此行为变更会影响包括 Google Play 服务在内的所有接收位置更新的应用。

此类变更会影响以下 API:

  • Fused Location Provider (FLP)
  • Geofencing
  • GNSS Measurements
  • Location Manager
  • Wi-Fi Manager

为确保您的应用按预期方式运行,请完成以下步骤:

  • 查看您的应用的逻辑,并确保您使用的是最新的位置 API。
  • 测试您的应用是否在每个用例中都表现出预期行为。
  • 考虑使用 Fused Location Provider (FLP) 或地理围栏来处理依赖于用户当前位置的用例。

1.3、应用快捷键

Android O 对应用快捷方式做出了以下变更:

  • com.android.launcher.action.INSTALL_SHORTCUT 广播不再会对您的应用有任何影响,因为它现在是私有的隐式广播。相反,您应使用 ShortcutManager 类中的 requestPinShortcut() 函数创建应用快捷方式。
  • 现在,ACTION_CREATE_SHORTCUT Intent 可以创建可使用 ShortcutManager 类进行管理的应用快捷方式。此 Intent 还可以创建不与 ShortcutManager 交互的旧版启动器快捷方式。在以前,此 Intent 只能创建旧版启动器快捷方式。
  • 现在,使用 requestPinShortcut() 创建的快捷方式和在处理 ACTION_CREATE_SHORTCUT Intent 的操作组件中创建的快捷方式均已转换为功能齐全的应用快捷方式。因此,应用现在可以使用 ShortcutManager 中的函数来更新这些快捷方式。

1.4、语言区域和国际化

Android 7.0(API 级别 24)引入能指定默认类别语言区域的概念,但是某些 API 在本应使用默认 DISPLAY 类别语言区域时,仍然使用不带参数的通用 Locale.getDefault() 函数。现在,在 Android O 中,以下函数使用 Locale.getDefault(Category.DISPLAY) 来代替:

  • Locale.getDefault():
  • Currency.getDisplayName()
  • Currency.getSymbol()
  • Locale.getDisplayScript()

当为 Locale 参数指定的 displayScript 值不可用时,Locale.getDisplayScript(Locale) 同样回退到 Locale.getDefault()。

与语言区域和国际化有关的其他变更如下:

  • 调用 Currency.getDisplayName(null) 会引发 NullPointerException,以与文档规定的行为保持一致。

  • 时区名称的分析方法发生变化。之前,Android设备使用在启动时取样的系统时钟值,缓存用于分析日期时间的时区名称。因此,如果在启动时或其他较为罕见的情况下系统时钟出错,可能对分析产生负面影响。
    现在,一般情况下,在分析时区名称时分析逻辑将使用 ICU 和当前系统时钟值。此项变更可提供更加准确的结果,如果您的应用使用 SimpleDateFormat 等类,此结果可能与之前的 Android 版本不同。

  • Android O 将 ICU 的版本更新至版本 58。

1.5、提醒窗口

如果应用使用 SYSTEM_ALERT_WINDOW 权限并且尝试使用以下窗口类型之一来在其他应用和系统窗口上方显示提醒窗口:

  • TYPE_PHONE
  • TYPE_PRIORITY_PHONE
  • TYPE_SYSTEM_ALERT
  • TYPE_SYSTEM_OVERLAY
  • TYPE_SYSTEM_ERROR

...那么,这些窗口将始终显示在使用 TYPE_APPLICATION_OVERLAY 窗口类型的窗口下方。如果应用针对的是 Android O,则应用会使用 TYPE_APPLICATION_OVERLAY 窗口类型来显示提醒窗口。

1.6、输入和导航

随着 Android 应用出现在 Chrome 操作系统和平板电脑等其他大尺寸设备上,我们看到,用户在 Android 应用中又重新开始使用键盘导航。在 Android O 中,我们又再次使用键盘作为导航输入设备,从而为基于箭头键和 Tab 键的导航构建了一种更可靠并且可预测的模型。

尤其要指出的是,我们对元素焦点行为做出以下变更:

  • 现在,如果您没有为 View 对象(前景或背景图片)定义任何焦点状态颜色,框架会为 View 设置默认的焦点突出显示颜色。此焦点突出显示标志是基于操作组件主题背景的涟漪图片。
    如果您不希望 View 对象在接收焦点时使用此默认突出显示标志,请在包含 View 的布局 XML 文件中将 android:defaultFocusHighlightEnabled 属性设置为 false,或者将 false 传递至应用界面逻辑中的 setDefaultFocusHighlightEnabled()。

  • 要测试键盘输入对界面元素焦点有何影响,您可以启用 Drawing > Show layout bounds 开发者选项。在 Android O 中,此选项在当前具有焦点的元素上显示一个“X”图标。

另外,Android O 中的所有工具栏元素自动组成键盘导航键区,用户可以更加轻松地导航进入和离开每个作为一个整体的工具栏。

1.7、网页表单自动填充

现在,Android 自动填充框架提供对自动填充功能的内置支持,对于安装到运行 Android O 的设备上的应用,与 WebView 对象相关的下列函数已经发生变化:

WebSettings

  • getSaveFormData() 函数现在返回 false。之前,此函数返回 true。
    调用 setSaveFormData() 不再有任何效果。

WebViewDatabase

  • 调用 clearFormData() 不再有任何效果。
    hasFormData() 函数现在返回 false。之前,当表单包含数据时,此函数返回 true。

1.8、无障碍功能

现在,无障碍服务可识别应用的 TextView 对象内部的所有 ClickableSpan 实例。

1.9、网络连接和 HTTP(S) 连接

Android O 对网络连接和 HTTP(S) 连接行为做出了以下变更:

  • 无正文的 OPTIONS 请求具有 Content-Length: 0 标头。之前,这些请求没有 Content-Length 标头。
  • HttpURLConnection 在包含斜线的主机或颁发机构名称后面附加一条斜线,使包含空路径的网址规范化。例如,它将 http://example.com 转化为 http://example.com/
  • 通过 ProxySelector.setDefault() 设置的自定义代理选择器仅针对所请求的网址(架构、主机和端口)。因此,仅可根据这些值选择代理。传递至自定义代理选择器的网址不包含所请求的网址的路径、查询参数或片段。
  • URI 不能包含空白标签。
    之前,平台支持一种权宜方法,即允许主机名称中包含空白标签,但这是对 URI 的非法使用。此权宜方法只是为了确保与旧版 libcore 兼容。开发者如果对 API 使用不当,将会看到一条 ADB 消息:“URI example..com 的主机名包含空白标签。此格式不正确,将不被未来的 Android 版本所接受。”Android O 废除了此权宜方法;系统对格式错误的 URI 会返回 null。
    Android O 在实现 HttpsURLConnection 时不会执行不安全的 TLS/SSL 协议版本回退。
  • 对隧道 HTTP(S) 连接处理进行了如下变更:
    • 在通过连接建立隧道 HTTP(S) 连接时,系统会在 Host 行中正确放置端口号 (:443) 并将此信息发送至中间服务器。之前,端口号仅出现在 CONNECT 行中。
    • 系统不再将隧道连接请求中的 user-agent 和 proxy-authorization 标头发送至代理服务器。
      在建立隧道时,系统不再将隧道 Http(s)URLConnection 中的 proxy-authorization 标头发送至代理。相反,由系统生成 proxy-authorization 标头,在代理响应初始请求发送 HTTP 407 后将其发送至此代理。
      同样地,系统不再将 user-agent 标头由隧道连接请求复制到建立隧道的代理请求。相反,库为此请求生成 user-agent 标头。
  • 如果之前执行的 connect() 函数失败,send(java.net.DatagramPacket) 函数将会引发 SocketException。
    • 如果存在内部错误,DatagramSocket.connect() 会引发 pendingSocketException。对于 Android O 之前的版本,即使 send() 调用成功,后续的 recv() 调用也会引发 SocketException。为确保一致性,现在这两个调用均会引发 SocketException。
  • 在回退到 TCP Echo 协议之前,InetAddress.isReachable() 会尝试执行 ICMP。
    • 对于某些屏蔽端口 7 (TCP Echo) 的主机(例如 google.com),如果它们接受 ICMP Echo 协议,现在也许能够访问它们。
    • 对于确实无法访问的主机,此项变更意味着调用需要两倍的时间才能返回结果。

1.10、蓝牙

Android O 对 ScanRecord.getBytes() 函数检索的数据长度做出以下变更:

  • getBytes() 函数对于所接收的字节数不作任何假定。因此,应用不应受所返回的任何最小或最大字节数的影响。相反,应用应当计算所返回数组的长度。
  • 兼容蓝牙 5 的设备返回的数据长度可能会超出之前最大约 60 个字节的限制。
  • 如果远程设备未提供扫描响应,则也可能返回少于 60 个字节的数据。

1.11、无缝连接

Android O 对 WLAN 设置进行了多项改进,这样可以更轻松地选择能够提供最佳用户体验的 WLAN 网络。具体变更包括:

  • 稳定性和可靠性改进。
  • 更加直观的界面。
  • 一个合并的 WLAN 首选项菜单。
  • 当附近存在优质的已保存网络时在兼容设备上自动激活 WLAN。

1.12、安全性

Android O 包含以下与安全性有关的变更:

  • 此平台不再支持 SSLv3。
  • 在与未正确实现 TLS 协议版本协商的服务器建立 HTTPS 连接时,HttpsURLConnection 不再尝试回退到之前的 TLS 协议版本并重试的权宜方法。
  • Android O 将使用安全计算 (SECCOMP) 过滤器来过滤所有应用。允许的系统调用列表仅限于通过 bionic 公开的系统调用。此外,还提供了其他几个后向兼容的系统调用,但我们不建议使用这些系统调用。
  • 现在,您的应用的 WebView 对象将在多进程模式下运行。网页内容在独立的进程中处理,此进程与包含应用的进程相隔离,以提高安全性。
  • 您无法再假定 APK 驻留在名称以 -1 或 -2 结尾的目录中。应用应使用 sourceDir 获取此目录,而不能直接使用目录格式。

1.13、隐私性

Android O 对平台做出了以下与隐私性有关的变更。

  • 现在,平台改变了标识符的处理方式。
    • 对于在 OTA 之前安装到某个版本 Android O(API 级别 26)的应用,除非在 OTA 后卸载并重新安装,否则 ANDROID_ID 的值将保持不变。要在 OTA 后在卸载期间保留值,开发者可以使用密钥/值备份关联旧值和新值。
    • 对于安装在运行 Android O 的设备上的应用,ANDROID_ID 的值现在将根据应用签署密钥和用户确定作用域。应用签署密钥、用户和设备的每个组合都具有唯一的 ANDROID_ID 值。因此,在相同设备上运行但具有不同签署密钥的应用将不会再看到相同的 Android ID(即使对于同一用户来说,也是如此)。
    • 只要签署密钥相同(并且应用未在 OTA 之前安装到某个版本的 O),ANDROID_ID 的值在软件包卸载或重新安装时就不会发生变化。
    • 即使系统更新导致软件包签署密钥发生变化,ANDROID_ID 的值也不会变化。
    • 要借助一个简单的标准系统实现应用获利,请使用广告 ID。广告 ID 是 Google Play 服务针对广告服务提供的唯一 ID,此 ID 可由用户重置。
  • 查询 net.hostname 系统属性返回的结果为空。

1.14、记录未捕获的异常

如果某个应用安装的 Thread.UncaughtExceptionHandler 未移交给默认的 Thread.UncaughtExceptionHandler,则当出现未捕获的异常时,系统不会终止应用。从 Android O 开始,在此情况下系统将记录异常堆栈跟踪情况;在之前的平台版本中,系统不会记录异常堆栈跟踪情况。

我们建议,自定义 Thread.UncaughtExceptionHandler 实现始终移交给默认处理程序处理;遵循此建议的应用不受 Android O 此项变更的影响。

1.15、联系人提供程序使用情况统计方法的变更

在之前版本的Android中,联系人提供程序组件允许开发者获取每个联系人的使用情况数据。此使用情况数据揭示了与某个联系人相关联的每个电子邮件地址和每个电话号码的信息,包括与该联系人联系的次数以及上次联系该联系人的时间。请求 READ_CONTACTS 权限的应用可以读取此数据。

如果应用请求 READ_CONTACTS 权限,它们仍可以读取此数据。从 Android O 开始,使用情况数据查询会返回近似值,而不是精确值。不过,Android 系统内部仍然会保留精确值,因此,此变更不会影响 auto-complete API。

此行为变更会影响以下查询参数:

  • TIMES_CONTACTED
  • TIMES_USED
  • LAST_TIME_CONTACTED
  • LAST_TIME_USED

1.16、集合的处理

现在,AbstractCollection.removeAll() 和 AbstractCollection.retainAll() 始终引发 NullPointerException;之前,当集合为空时不会引发 NullPointerException。此项变更使行为符合文档要求。

1.17、Android 企业版

Android O 更改了企业应用(包括设备规范控制器 (DPC))的某些 API 和功能的行为。这些变更包括:

  • 新增多种行为,帮助应用支持完全托管设备中的工作资料。
  • 变更系统更新处理、应用验证和身份验证方式,以提高设备和系统的完整性。
  • 改进用户在配置、通知、“最近使用的应用”屏幕和 Always on VPN 方面的体验。

推荐阅读更多精彩内容