Android O 功能和 API
(文章内容均来Google开发者官网,有需要可自行FQ查看更多资料)
Android O 为用户和开发者引入多种新功能。本文重点介绍面向开发者的新功能。请务必查阅 android O 行为变更以了解平台变更可能影响您的应用的领域。
通知
在 Android O 中,我们已重新设计通知,以便为管理通知行为和设置提供更轻松和更统一的方式。这些变更包括:
通知渠道:Android O 引入了通知渠道,其允许您为要显示的每种通知类型创建用户可自定义的渠道。用户界面将通知渠道称之为通知类别。要了解如何实现通知渠道的信息,请参阅通知渠道指南。
休眠:用户可以将通知置于休眠状态,以便稍后重新显示它。重新显示时通知的重要程度与首次显示时相同。应用可以移除或更新已休眠的通知,但更新休眠的通知并不会使其重新显示。
通知超时:现在,使用 Notification.Builder.setTimeout() 创建通知时您可以设置超时。您可以使用此方法指定一个持续时间,过了该持续时间后取消通知。如果需要,您可以在指定的超时持续时间之前取消通知。
通知清除:系统现在可区分通知是由用户清除,还是由应用移除。要查看清除通知的方式,您应实现 NotificationListenerService 类的新 onNotificationRemoved() 方法。
背景颜色:您现在可以设置和启用通知的背景颜色。只能在用户必须一眼就能看到的持续任务的通知中使用此功能。例如,您可以为与驾车路线或正在进行的通话有关的通知设置背景颜色。您还可以使用 Notification.Builder.setColor() 设置所需的背景颜色。这样做将允许您使用 Notification.Builder.setColorized() 启用通知的背景颜色设置。
消息样式:现在,使用 MessagingStyle 类的通知可在其折叠形式中显示更多内容。对于与消息有关的通知,您应使用 MessagingStyle 类。您还可以使用新的 addHistoricMessage() 方法,通过向与消息相关的通知添加历史消息为会话提供上下文。
图 1. Android N(5.0) 中的通知栏。
图 2. Android O(8.0) 中通知栏的嵌入式控件。
自动填充框架
帐号创建、登录和信用卡交易需要时间并且容易出错。在使用要求执行此类重复性任务的应用时,用户很容易遭受挫折。
Android O 通过引入自动填充框架,简化了登录和信用卡表单之类表单的填写工作。在用户选择接受自动填充之后,新老应用都可使用自动填充框架。
您可以采取某些措施,优化您的应用使用此框架的方式。如需了解详细信息,请参阅自动填充框架概览。
画中画模式
Android O 允许以画中画 (PIP) 模式启动 Activity。PIP 是一种特殊的多窗口模式,最常用于视频播放。目前,PIP 模式可用于 Android TV,而 Android O 则让该功能可进一步用于其他 Android 设备。
当某个 Activity 处于 PIP 模式时,它会处于暂停状态,但仍应继续显示内容。因此,您应确保您的应用在 onPause() 处理程序中进行处理时不会暂停播放。相反,您应在 onStop() 中暂停播放视频,并在 onStart() 中继续播放。如需了解详细信息,请参阅多窗口生命周期。
要指定您的 Activity 可以使用 PIP 模式,请在清单中将 android:supportsPictureInPicture 设置为 true。(从 Android O 开始,如果您打算在 Android TV 或其他 Android 设备上支持 PIP 模式,则无需将 android:resizeableActivity 设置为 true;只有在您的 Activity 支持其他多窗口模式时,才需要设置 android:resizeableActivity。)
API 变更
Android O 引入一种新的对象 android.app.PictureInPictureArgs,您可以将该对象传递给 PIP 方法来指定某个 Activity 在其处于 PIP 模式时的行为。此对象还指定了各种属性,例如 Activity 的首选纵横比。
现在,在添加画中画中介绍的现有 PIP 方法可用于所有 Android 设备,而不仅限于 Android TV。此外,Android O 还提供以下方法来支持 PIP 模式:
Activity.enterPictureInPictureMode(PictureInPictureArgs args):将 Activity 置于画中画模式。Activity 的纵横比和其他配置设置均由 args 指定。如果 args 中的任何字段为空,系统将使用您上次调用 Activity.setPictureInPictureArgs() 时所设置的值。
指定的 Activity 被置于屏幕的一角,屏幕剩余部分则被屏幕之前显示的上一 Activity 所填满。进入 PIP 模式的 Activity 将进入暂停状态,但仍保持已启动状态。如果用户点按此 PIP Activity,系统将显示一个菜单供用户操作,而在 Activity 处于 PIP 状态期间,不会理会任何触摸事件。
Activity.setPictureInPictureArgs() 更新 Activity 的 PIP 配置设置。如果 Activity 目前处于 PIP 模式,则会更新此设置;如果 Activity 的纵横比发生变化,这非常有用。如果 Activity 不处于 PIP 模式,则会使用这些配置设置,而不会考虑您调用的 enterPictureInPictureMode() 方法。
处理字体
Android O 推出一项新功能,即 XML 中的字体,允许您使用字体作为资源。这意味着,不再需要以资产的形式捆绑字体。字体在 R 文件中编译,并且作为一种资源,可自动用于系统。然后,您可以利用一种新的资源类型 font 来访问这些字体。Android O 还提供了一种机制,可用来检索与系统字体有关的信息并提供文件描述符。如需了解有关以资源形式使用字体以及检索系统字体有关的详细信息,请参阅处理字体。
自适应图标
Android O 引入自适应启动器图标。自适应图标支持视觉效果,可在不同设备型号上显示为各种不同的形状。要了解如何创建自适应图标,请参阅自适应图标预览功能指南。
颜色管理
图像应用的 Android 开发者现在可以利用支持广色域彩色显示的新设备。要显示广色域图像,应用需要在其清单(每个 Activity)中启用一个标志,并加载具有嵌入的广域彩色配置文件(AdobeRGB、Pro Photo RGB、DCI-P3 等)的位图。
WLAN 感知
Android O 新增了对 WLAN 感知的支持,此技术基于周边感知联网 (NAN) 规范。在具有相应 WLAN 感知硬件的设备上,应用和附近设备可以通过 WLAN 进行搜索和通信,无需依赖互联网接入点。我们正在与硬件合作伙伴合作,以尽快将 WLAN 感知技术应用于设备。有关如何将 WLAN 感知集成到您的应用中的详细信息,请参阅 WLAN 感知。
配套设备配对
在尝试通过蓝牙、BLE 和 WLAN 与配套设备配对时,Android O 提供的 API 允许您自定义配对请求对话框。如需了解详细信息,请参阅配套设备配对。
如需了解有关在 Android 上使用蓝牙的详细信息,请参阅蓝牙指南。有关对蓝牙所作的特定于 Android O 的变更,请参阅 Android O 行为变更页面的蓝牙部分。
WebView API
Android O 提供多种 API,帮助您管理在应用中显示网页内容的 WebView 对象。这些 API 可增强应用的稳定性和安全性,它们包括:
Version API
Google SafeBrowsing API
Termination Handle API
Renderer Importance API
如需了解有关这些 API 用法的更多信息,请参阅管理 WebView。
固定快捷方式和小部件
Android O 引入了快捷方式和小部件的应用内固定功能。在您的应用中,您可以根据用户权限为支持的启动器创建固定的快捷方式和小部件。
如需了解详细信息,请参阅固定快捷方式和小部件预览功能指南。
无障碍功能
Android O 支持开发者使用以下无障碍功能创建自己的无障碍服务:
语言检测
要识别“文本到语音转换 (TTS)”工具已在某个文本范围内识别的语言,请使用 TextClassificationManager.detectLanguages()。此方法出现在 Android O 中引入的TextClassificationManager 类中。您可以使用生成的 android.view.textclassifier.TextLanguage 对象列表来确定哪些文本区域已被指定为某种特定的语言以及 TTS 有多大把握为某个特定的文本子集指定语言。
无障碍功能按钮
通过在 android:accessibilityFlags 属性中设置 FLAG_REQUEST_ACCESSIBILITY_BUTTON 标志,您的服务可以请求在系统的导航区域显示无障碍功能按钮。此按钮为用户从设备上的任何屏幕启动服务功能提供了一种快速的方式。您的服务可以使用 registerAccessibilityButtonCallback() 注册按钮交互回调。
注:此功能仅适用于提供软件渲染导航区域的设备。使用 isAccessibilityButtonAvailable() 方法和 onAvailabilityChanged() 回调可跟踪无障碍功能按钮的可用性。请确保您的服务的用户在无障碍功能按钮不可用时可以通过其他方式访问相关功能。
指纹手势
您的无障碍服务也可以响应替代的输入机制,即沿设备的指纹传感器按特定方向滑动(上、下、左和右)。要获取有关这些交互的回调,请完成以下顺序步骤:
声明 USE_FINGERPRINT 权限和 AccessibilityServiceInfo.CAPABILITY_CAN_CAPTURE_FINGERPRINT_GESTURES 功能。
在 android:accessibilityFlags 属性中设置 AccessibilityServiceInfo.FLAG_CAPTURE_FINGERPRINT_GESTURES 标志。
使用 registerFingerprintGestureCallback() 注册回调。
请记住,并非所有设备都包含指纹传感器。您可以使用 isHardwareDetected() 方法识别设备是否支持此传感器。即使对于包含指纹传感器的设备,您的服务也只有在指纹传感器不用于身份验证目的时才可使用它。要识别此传感器何时可用,请调用 isGestureDetectionAvailable() 方法并实现 onGestureDetectionAvailabilityChanged() 回调。
字词级突出显示
要确定 TextView 对象中可见字符的位置,您可以在 EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY 中将其作为第一个参数传递到 refreshWithExtraData() 中。随后会更新您为 refreshWithExtraData() 提供的作为第二个参数的 Bundle 对象,使之包含一个可打包的 Rect 对象数组。每个 Rect 对象代表某个特定字符的边界框。
如果您的服务使用 TextToSpeech 对象朗读屏幕上出现的内容,您可以获取文本到语音转换引擎开始朗读单个合成字词时的准确时间信息。当引擎即将开始播放特定范围文本的音频时,Text-to-Speech API 会通知您的服务,将使用 UtteranceProgressListener.onUtteranceRangeStart() 回调开始朗读此范围的文本。
如果您创建自己的 TextToSpeechService 实现,您可以使用 rangeStart() 方法支持这一新功能。
提示文本
您的服务可以使用 AccessibilityNodeInfo 类中的 getHintText() 方法,获取 EditText 对象的提示文本。即使某个特定的 EditText 对象当前未显示提示文本,getHintText() 方法仍将为您的服务提供提示文本。
连续的手势分派
您的服务现在可以使用 GestureDescription.StrokeDescription 构造函数中的最后一个参数 isContinued,指定属于同一设定手势的笔划的顺序。
如需了解有关如何让您的应用更便于访问的更多信息,请参阅无障碍功能。
权限
引入新权限
Android O 中引入了一项新权限android.permission.ANSWER_PHONE_CALLS,使用此权限,应用可按设定的方式接听拨入的电话。此权限被划分为危险类别,属于 PHONE 权限组。
要在应用中处理拨入的电话,您可以使用 TelecomManager 类中的 acceptRingingCall() 方法。
内容提供程序分页
我们已更新内容提供程序以支持加载大型数据集,每次加载一页。例如,一个具有大量图像的照片应用可查询要在页面中显示的数据的子集。内容提供程序返回的每个结果页面由一个 Cursor 对象表示。客户端和提供程序必须实现分页才能利用此功能。
如需了解有关内容提供程序变更的详细信息,请参阅 ContentProvider 和 ContentProviderClient。
媒体增强功能
媒体指标
新的 getMetrics() 方法将返回一个包含配置和性能信息的 Bundle 对象,用一个包含属性和值的地图表示。为以下媒体类定义 getMetrics() 方法:
MediaPlayer.getMetrics()
MediaRecorder.getMetrics()
MediaCodec.getMetrics()
MediaExtractor.getMetrics()
为每个实例单独收集指标,并持续到实例的生命周期结束为止。如果没有可用的指标,则此方法将返回 null。返回的实际指标取决于类。
MediaPlayer
Android O 为 MediaPlayer 类添加了多种新方法。这些方法可以从多个方面增强您的应用处理媒体播放的能力:
通过控制缓冲行为改进性能的功能。
在搜索帧时进行精细控制。
播放受数字版权管理保护的材料的功能。
音频录制器
音频录制器现在支持对流式传输有用的 MPEG2_TS 格式:
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS);
请参阅 MediaRecorder.OutputFormat
MediaMuxer 现在可以处理任意数量的音频和视频流,而不再仅限于一个音频曲目和/或一个视频曲目。使用 addTrack() 可混录所需的任意数量的曲目。
MediaMuxer 还可以添加一个或多个包含用户定义的每帧信息的元数据曲目。元数据的格式由您的应用定义。仅对 MP4 容器支持元数据曲目。
元数据可以用于离线处理。例如,传感器的陀螺仪信号可以用于执行视频稳定操作。
在添加元数据曲目时,曲目的 MIME 格式必须以前缀“application/”开头。除了数据不是来源于 MediaCodec 以外,写入元数据的操作与写入视频/音频数据相同。相反,应用将包含相关时间戳的 ByteBuffer 传递给 writeSampleData() 方法。时间戳必须和视频及音频曲目处于相同的时基。
生成的 MP4 文件使用 ISOBMFF 的 12.3.3.2 部分定义的 TextMetaDataSampleEntry,指示元数据的 MIME 格式。在使用 MediaExtractor 提取包含元数据曲目的文件时,元数据的 MIME 格式将提取到 MediaFormat 中。
多显示器支持
从 Android O 开始,此平台为多显示器提供增强的支持。如果 Activity 支持多窗口模式,并且在具有多显示器的设备上运行,则用户可以将 Activity 从一个显示器移动到另一个显示器。当应用启动 Activity 时,此应用可指定 Activity 应在哪个显示器上运行。
注:如果 Activity 支持多窗口模式,则 Android O 将为该 Activity 自动启用多显示器支持。您应测试您的应用,确保它在多显示器环境下可正常运行。
每次只有一个 Activity 可以处于继续状态,即使此应用具有多个显示器。具有焦点的 Activity 将处于继续状态,所有其他可见的 Activity 均暂停,但不会停止。如需了解有关当多个 Activity 可见时活动生命周期的详细信息,请参阅多窗口生命周期。
当用户将 Activity 从一个显示器移动到另一个显示器时,系统将调整 Activity 大小,并根据需要发起运行时变更。您的 Activity 可以自行处理配置变更,或允许系统销毁包含该 Activity 的进程,并以新的尺寸重新创建它。如需了解详细信息,请参阅处理配置变更。
API 变更
ActivityOptions 提供两个新方法以支持多个显示器:
setLaunchDisplayId()
指定 Activity 在启动后应显示在哪个显示器上。
getLaunchDisplayId()
返回 Activity 的当前启动显示器。
工具更新
对 adb shell 进行了扩展,以支持多个显示器。shell start 命令现在可用于启动 Activity,并指定 Activity 的目标显示器:
adb shell start –display
新的帐号访问和 Discovery API
Android O 对应用访问用户帐号的方式引入多项改进。对于由身份验证器管理的帐号,身份验证器在决定对应用隐藏帐号还是显示帐号时可以使用自己的政策。Android 系统跟踪可以访问特定帐号的应用。
在以前的 Android 版本中,想要跟踪用户帐号列表的应用必须获取有关所有帐号的更新,包括具有不相关类型的帐号。Android O 添加了 addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean,Java.lang.String[]) 方法,其允许应用指定应接收帐号变更的帐号类型列表。
AccountManager 提供六个新方法以帮助身份验证器管理哪些应用可以查看帐号:
setAccountVisibility(android.accounts.Account, java.lang.String, int):针对特定用户帐号和软件包组合设置可见性级别。
getAccountVisibility(android.accounts.Account, java.lang.String):获取特定用户帐号和软件包组合的可见性级别。
getAccountsAndVisibilityForPackage(java.lang.String, java.lang.String):允许身份验证器获取帐号和给定软件包的可见性级别。
getPackagesAndVisibilityForAccount(android.accounts.Account):允许身份验证器获取存储的给定帐号的可见性值。
addAccountExplicitly(android.accounts.Account, java.lang.String, android.os.Bundle, java.util.Map<java.lang.String, java.lang.Integer>):允许身份验证器初始化帐号的可见性值。
addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean, java.lang.String[]):将 OnAccountsUpdateListener 侦听器添加到 AccountManager 对象。无论设备上的帐号列表何时发生变化,系统都将调用此侦听器。
Android O 引入两个特殊的软件包名称值,以使用 setAccountVisibility(android.accounts.Account, java.lang.String, int) 方法指定未设置的应用的可见性级别。PACKAGE_NAME_KEY_LEGACY_VISIBLE 可见性值应用于具有 GET_ACCOUNTS 权限的应用,并且其目标 Android 版本低于 Android O,或其签名与针对任意 Android 版本的身份验证器匹配。PACKAGE_NAME_KEY_LEGACY_NOT_VISIBLE 为之前未设置的应用提供默认的可见性值,对于此类应用,PACKAGE_NAME_KEY_LEGACY_VISIBLE 不适用。
如需了解有关新的帐号访问和 Discovery API 的详细信息,请参阅对 AccountManager 和 OnAccountsUpdateListener 的参考。
AnimatorSet
从 Android O 开始,AnimatorSet API 现在支持寻道和倒播功能。寻道功能允许您将动画的位置设置为指定的时间点处。如果您的应用包含可撤消的动作的动画,倒播功能会很有用。现在,您不必定义两组独立的动画,而只需反向播放同一组动画。
自动调整 TextView 的大小
Android O 允许您根据 TextView 的大小自动设置文本展开或收缩的大小。这意味着,在不同屏幕上优化文本大小或者优化包含动态内容的文本大小比以往简单多了。如需了解有关如何在 Android O 中自动调整 TextView 的大小的详细信息,请参阅自动调整 TextView 的大小。
应用类别
在适当的情况下,Android O 允许每个应用声明它们所属的类别。这些类别用于将应用呈现给用户的用途或功能相同的应用归类在一起,例如按流量消耗、电池消耗和存储消耗将应用归类。您可以在 清单标记中设置 android:appCategory 属性,定义应用的类别。
新的 StrictMode 检测程序
Android O 添加了三个新的 StrictMode 检测程序,帮助识别应用可能出现的错误:
detectUnbufferedIo() 将检测您的应用何时读取或写入未缓冲的数据,这可能极大影响性能。
detectContentUriWithoutPermission() 将检测您的应用在其外部启动 Activity 时何时意外忘记向其他应用授予权限。
detectUntaggedSockets() 将检测您的应用何时使用网络流量,而不使用 setThreadStatsTag(int) 将流量标记用于调试目的。
缓存数据
Android O 围绕缓存数据提供更加出色的导航和性能。现在,每个应用均获得一定的磁盘空间配额,用于存储 getCacheQuotaBytes(UUID) 返回的缓存数据。
当系统需要释放磁盘空间时,将开始从超过配额最多的应用中删除缓存文件。因此,如果将您的缓存数据量始终保持低于配额的水平,则在必须清除系统中的某些文件时,您的缓存文件将能坚持到最后。系统在决定删除您的应用中的哪些缓存文件时,将首先考虑删除最旧的文件(由修改时间决定)。
您还可以针对每个目录启用两种新行为,以控制系统如何释放缓存数据:
StorageManager.setCacheBehaviorAtomic() 可用于指示某个目录及其所有内容应作为一个不可分割的整体进行删除。
setCacheBehaviorTombstone(File, boolean) 可用于指示不应删除某个目录内的文件,而应将它们截断到 0 字节长度,使空文件保持完好。
最后,在需要为大文件分配磁盘空间时,可考虑使用新的 allocateBytes(FileDescriptor, long) API,它将自动清除属于其他应用的缓存文件(根据需要),以满足您的请求。在确定设备是否有足够的磁盘空间保存您的新数据时,请调用 getAllocatableBytes(UUID) 而不要使用 getUsableSpace(),因为前者会考虑系统要为您清除的任何缓存数据。
增强的媒体文件访问功能
存储访问框架 (SAF) 允许应用显示自定义 DocumentsProvider,后者可以为其他应用提供访问数据源中的文件的权限。事实上,文档提供程序甚至可以提供驻留在网络存储区或使用媒体传输协议 (MTP) 等协议的文件的访问权限。
但是,访问远程数据源中的大媒体文件面临一些挑战。媒体播放器需要以寻址方式访问来自文档提供程序的文件。当大媒体文件驻留在远程数据源上时,文档提供程序必须事先提取所有数据,并创建快照文件描述符。媒体播放器无法播放没有文件描述符的文件,因此在文档提供程序完成文件下载前,无法开始播放。
从 Android O 开始,存储访问框架允许自定义文档提供程序为驻留在远程数据源中的文件创建可寻址的文件描述符。SAF 可打开文件,获取原生可寻址的文件描述符。然后 SAF 向文档提供程序提交离散字节请求。此功能使文档提供程序可以返回媒体播放器应用请求的准确字节范围,而不必事先缓存整个文件。
要使用此功能,您需要调用新的 StorageManager.openProxyFileDescriptor() 方法。openProxyFileDescriptor() 方法可接受 ProxyFileDescriptorCallback 对象作为回调。任何时候,当客户端应用对文档提供程序返回的文件描述符执行文件操作时,SAF 都会调用回调。
企业中的 Android
Android 企业版为运行 Android O 的设备引入多种新功能和 API。我们改进了个人资料所有者和设备所有者管理模式,使其比以往任何时候都更强大、高效,也更容易设置。我们还启用了全新的部署方案。
一些值得关注的突出功能包括:
在企业拥有的设备上使用托管个人资料的功能。
大刀阔斧地改进了工作资料,采用易于使用的设置流程,显著缩短设置时间。
基于文件的企业级加密管理。
应用管理 API 委派。
如需了解有关 Android O 中面向 Android 企业版的新 API 和新功能的更多信息,请参阅企业中的 Android 页面。
Java 编程语言更新
在 Android O 中,我们将为 Android 添加 OpenJDK Java 语言功能。我们将添加来自 OpenJDK 8 的 java.time 以及 java.nio.file 和 java.lang.invoke,包括来自 OpenJDK 7 的 MethodHandle。请查阅新程序包中的 API 差异报告。
Android O 行为变更
Android O 除了提供诸多新特性和功能外,还对系统和 API 行为做出了各种变更。本文重点介绍您应该了解并在开发应用时加以考虑的一些主要变更。
其中大部分变更会影响所有应用,而不论应用针对的是何种版本的 Android。不过,有几项变更仅影响针对 Android O 的应用。为清楚起见,本页面分为两个部分:针对所有 API 级别的应用和针对 Android O 的应用。
针对所有 API 级别的应用
这些行为变更适用于 在 Android O 平台上运行的 所有应用,无论这些应用是针对哪个 API 级别构建。所有开发者都应查看这些变更,并修改其应用以正确支持这些变更(如果适用)。
后台执行限制
Android O 为提高电池寿命而引入的变更之一是,当您的应用进入已缓存状态时,如果没有活动的组件,系统将解除应用具有的所有唤醒锁。
Android 后台位置限制
为节约电池电量,保持良好的用户体验和确保系统健康运行,在运行 Android O 的设备上使用后台应用时,降低了后台应用接收位置更新的频率。此行为变更会影响包括 Google Play 服务在内的所有接收位置更新的应用。
此类变更会影响以下 API:
Fused Location Provider (FLP)
Geofencing
GNSS Measurements
Location Manager
Android O 还对特定方法做出了以下变更:
NotificationManager.startServiceInForeground() 方法将启动一个前台服务。启动前台服务的老办法将不再奏效。
现在,如果针对 Android O 的应用尝试在限制服务的情况下使用 startService() 方法,则该方法将引发一个 IllegalStateException。
为确保您的应用按预期方式运行,请完成以下步骤:
查看您的应用的逻辑,并确保您使用的是最新的位置 API。
测试您的应用是否在每个用例中都表现出预期行为。
考虑使用 Fused Location Provider (FLP) 或地理围栏来处理依赖于用户当前位置的用例。
如需了解此类变更的详细信息,请参阅后台位置限制。
蓝牙
Android O 对 ScanRecord.getBytes() 方法检索的数据长度做出了以下变更:
getBytes() 方法对于所接收的字节数不作任何假定。因此,应用不应受所返回的任何最小或最大字节数的影响。相反,应用应当计算所返回数组的长度。
兼容蓝牙 5 的设备返回的数据长度可能会超出之前最大 60 个字节的限制。
如果远程设备未提供扫描响应,则也可能返回少于 60 个字节的数据。
输入和导航
随着 Android 应用出现在 Chrome 操作系统和平板电脑等其他大尺寸设备上,我们看到,用户在 Android 应用中又重新开始使用键盘导航。在 Android O 中,我们又再次使用键盘作为导航输入设备,从而为基于箭头和标签的导航构建了一种更可靠并且可预测的模型。
如需了解如何在您的应用中改善对键盘导航的支持,请阅读支持键盘导航指南。
无障碍功能
现在,无障碍服务可识别应用的 TextView 对象内部的所有 ClickableSpan 实例。
如需了解有关如何让您的应用更便于访问的更多信息,请参阅无障碍功能。
安全性
Android O 包含以下与安全性有关的变更:
此平台不再支持 SSLv3。
在与未正确实现 TLS 协议版本协商的服务器建立 HTTPS 连接时,HttpsURLConnection 不再尝试回退到之前的 TLS 协议版本并重试的权宜方法。
Android O 将使用安全计算 (SECCOMP) 过滤器来过滤所有应用。允许的系统调用列表仅限于通过 bionic 公开的系统调用。此外,还提供了其他几个后向兼容的系统调用,但我们不建议使用这些系统调用。
现在,您的应用的 WebView 对象将在多进程模式下运行。网页内容在独立的进程中处理,此进程与包含应用的进程相隔离,以提高安全性。
如需了解与使用原生库有关的安全性增强的信息,请参阅原生库。
有关提升应用安全性的其他准则,请参阅面向 Android 开发者的安全性。
隐私性
Android O 对平台做出了以下与隐私性有关的变更。
现在,平台改变了标识符的处理方式。
现在,根据应用(而不是根据用户)来确定 ANDROID_ID 的值范围。应用软件包名称、签名、用户和设备的每个组合都具有唯一的 ANDROID_ID 值。同一个设备上运行的两个应用不再出现 Android ID 相同的情况,因此不可能建立关联。
只要软件包名称和签名密钥相同,在软件包卸载或重新安装时 ANDROID_ID 的值不会改变。
如果软件包签名密钥是因为更新而发生改变,那么,ANDROID_ID 的值不会改变。
Widevine ID 的范围根据应用来确定。
对于在 OTA 之前安装的应用,除非卸载并重新安装,否则,ANDROID_ID 的值将保持不变。
如果您要继续将 Android ID 用于与设备绑定的免费试用保护,您可以这么做。请确保软件包名称和签名相一致。
要借助一个简单的标准系统实现应用获利,请使用广告 ID。广告 ID 是 Google Play 服务针对广告服务提供的唯一 ID,此 ID 可由用户重置。
查询 net.hostname 系统属性返回的结果为空。
您无法再假定 APK 驻留在名称以 -1 或 -2 结尾的目录中。应用应使用 sourceDir 获取此目录,而不能直接使用目录格式。
网络连接和 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 协议,现在也许能够访问它们。
对于确实无法访问的主机,此项变更意味着调用需要两倍的时间才能返回结果。
记录未捕获的异常
如果某个应用安装的 Thread.UncaughtExceptionHandler 未移交给默认的 Thread.UncaughtExceptionHandler,则当出现未捕获的异常时,系统不会终止应用。从 Android O 开始,在此情况下系统将记录异常堆栈跟踪情况;在之前的平台版本中,系统不会记录异常堆栈跟踪情况。
我们建议,自定义 Thread.UncaughtExceptionHandler 实现始终移交给默认处理程序处理;遵循此建议的应用不受 Android O 此项变更的影响。
集合的处理
现在,AbstractCollection.removeAll() 和 AbstractCollection.retainAll() 始终引发 NullPointerException;之前,当集合为空时不会引发 NullPointerException。此项变更使行为符合文档要求。
语言区域和国际化
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。
联系人提供程序使用情况统计方法的变更
在之前版本的 Android 中,联系人提供程序组件允许开发者获取每个联系人的使用情况数据。此使用情况数据揭示了与某个联系人相关联的每个电子邮件地址和每个电话号码的信息,包括与该联系人联系的次数以及上次联系该联系人的时间。请求 READ_CONTACTS 权限的应用可以读取此数据。
如果应用请求 READ_CONTACTS 权限,它们仍可以读取此数据。从 Android O 开始,使用情况数据查询会返回近似值,而不是精确值。不过,Android 系统内部仍然会保留精确值,因此,此变更不会影响 auto-complete API。
此行为变更会影响以下查询参数:
TIMES_CONTACTED
TIMES_USED
LAST_TIME_CONTACTED
LAST_TIME_USED
应用快捷键
Android O 对应用快捷键做出了以下变更:
com.android.launcher.action.INSTALL_SHORTCUT 广播不再会对您的应用有任何影响,因为它现在是私有的隐式广播。相反,您应使用 ShortcutManager 类中的 requestPinShortcut() 方法创建应用快捷键。
现在,ACTION_CREATE_SHORTCUT Intent 可以创建可使用 ShortcutManager 类进行管理的应用快捷键。此 Intent 还可以创建不与 ShortcutManager 交互的旧版启动器快捷键。在以前,此 Intent 只能创建旧版启动器快捷键。
现在,使用 requestPinShortcut() 创建的快捷键和在处理 ACTION_CREATE_SHORTCUT Intent 的 Activity 中创建的快捷键均已转换为功能齐全的应用快捷键。因此,应用现在可以使用 ShortcutManager 中的方法来更新这些快捷键。
旧版快捷键仍然保留了它们在旧版 Android 中的功能,但您必须在应用中手动将它们转换成应用快捷键。
如需了解有关应用快捷键变更的更多信息,请参阅固定快捷键和小部件预览功能指南。
提醒窗口
如果应用使用 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 窗口类型来显示提醒窗口。
如需了解详细信息,请参阅针对 Android O 的应用的行为变更内的提醒窗口的常用窗口类型部分。
企业中的 Android
Android O 包含会影响企业应用的变更。如果您正在为企业构建应用,包括 DPC(设备规范控制器),您应查阅企业中的 Android 页面中介绍的变更,并相应修改您的应用。
针对 Android O 的应用
这些行为变更专门应用于针对 O 平台或更高平台版本的应用。针对 Android O 或更高平台版本进行编译,或将 targetSdkVersion 设为 Android O 或更高版本的应用开发者必须修改其应用以正确支持这些行为(如果适用)。
后台执行限制
为提高设备性能,系统会限制未在前台运行的应用的某些行为。具体而言:
现在,在后台运行的应用对后台服务的访问受到限制。
应用无法使用其清单注册大部分隐式广播(即,并非专门针对此应用的广播)。
如需了解详细信息,请参阅后台执行限制。
安全性
如果您的应用的网络安全性配置选择退出对明文流量的支持,那么,您的应用的 WebView 对象无法通过 HTTP 访问网站。每个 WebView 对象必须转而使用 HTTPS。
有关提升应用安全性的其他准则,请参阅面向 Android 开发者的安全性。
隐私性
以下变更影响 Android O 的隐私性。
系统属性 net.dns1、net.dns2、net.dns3 和 net.dns4 不再可用,此项变更可加强平台的隐私性。
要获取 DNS 服务器之类的网络连接信息,具有 ACCESS_NETWORK_STATE 权限的应用可以注册 NetworkRequest 或 NetworkCallback 对象。这些类在 Android 5.0(API 级别 21)及更高版本中提供。
从 Android O 开始,不再支持 Build.SERIAL,此字段将返回一个未定义的值。需要知道硬件序列号的应用应改为使用新的 Build.getSerial() 方法,该方法要求具有 READ_PHONE_STATE 权限。
LauncherApps API 不再允许托管配置文件应用获取有关主配置文件的信息。当某个用户在托管配置文件中时,LauncherApps API 的行为就像同一配置文件组的其他配置文件中未安装任何应用一样。和之前一样,尝试访问无关联的配置文件会引发 SecurityExceptions。
权限
在 Android O 之前,如果应用在运行时请求权限并且被授予该权限,系统会错误地将属于同一权限组并且在清单中注册的其他权限也一起授予应用。
对于针对 Android O 的应用,此行为已被纠正。系统只会授予应用明确请求的权限。然而,一旦用户为应用授予某个权限,则所有后续对该权限组中权限的请求都将被自动批准。
例如,假设某个应用在其清单中列出 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE。应用请求 READ_EXTERNAL_STORAGE,并且用户授予了该权限。如果该应用针对的是 API 级别 24 或更低级别,系统还会同时授予 WRITE_EXTERNAL_STORAGE,因为该权限也属于同一 STORAGE 权限组并且也在清单中注册过。如果该应用针对的是 Android O,则系统此时仅会授予 READ_EXTERNAL_STORAGE;不过,如果该应用后来又请求 WRITE_EXTERNAL_STORAGE,则系统会立即授予该权限,而不会提示用户。
媒体
框架会执行音频闪避。进行 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 时,应用不会失去焦点。新的 API 适用于需要暂停而不是闪避的应用。不过,Android O 中未提供此行为。
当用户打电话时,活动的媒体流将在通话期间静音。
音频流类型应仅用于音量控制;所有其他流类型的使用(例如 AudioTrack 构造函数)仍有效,但系统会将其作为错误记录下来。
所有与音频相关的 API 均使用 AudioAttributes 来描述音频播放用例。
使用 AudioTrack 时,如果应用请求了足够大的音频缓冲区,则框架将尝试使用深度缓冲区输出(如果可用)。
原生库
在针对 Android O 的应用中,如果原生库包含任何可写且可执行的加载代码段,则不会再加载原生库。倘若某些应用的原生库包含不正确的加载代码段,则此变更可能会导致这些应用停止工作。这是一种安全加强措施。
如需了解详细信息,请参阅可写且可执行的代码段。
与早期的开发者预览版相同,Android O 还有助于更轻松地发现所有与链接器有关的问题。链接器的变更绑定到应用的目标 API 级别。如果应用的目标 API 级别发生链接器变更,则该应用无法加载该库。如果您的目标 API 级别低于发生链接器变更的 API 级别,则 logcat 会显示一条警告消息。在预览版期间,与链接器有关的问题不仅会显示在 logcat 中,也会以 toast 的形式显示。对于特定的 API 级别,警告可能会变成错误,此变更有助于提前发现此类问题。
集合的处理
在 Android O 中,Collections.sort() 是在 List.sort() 的基础上实现的。在 Android 7.x(API 级别 24 和 25)中,则恰恰相反。在过去,List.sort() 的默认实现会调用 Collections.sort()。
此项变更使 Collections.sort() 可以利用优化的 List.sort() 实现,但具有以下限制:
List.sort() 的实现不能调用 Collections.sort(),因为这会导致堆栈因无限递归而溢出。相反,如果您需要 List 实现的默认行为,应避免重写 sort()。
如果父类以不适当的方法实现 sort() ,通常最好使用在 List.toArray()、Arrays.sort() 和 ListIterator.set() 的基础上构建的实现重写 List.sort()。例如:
@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);
}
}
在大多数情况下,您也可以使用根据 API 级别委托给其他默认实现的实现重写 List.sort()。例如:
@Override
public void sort(Comparator<? super E> comparator) {
if (Build.VERSION.SDK_INT <= 25) {
Collections.sort(this);
} else {
super.sort(comparator);
}
}
如果您选择后者只是因为您希望开发一种适用于所有 API 级别的 sort() 方法,可以考虑赋予其一个唯一的名称,例如 sortCompat(),而不是重写 sort()。
现在,Collections.sort() 只是对调用 sort() 的 List 实现进行的一项结构性修改。例如,在 Android O 之前的平台版本中,如果通过调用 List.sort() 进行排序,则当迭代处理 ArrayList 以及在迭代过程中调用 sort() 时,会引发 ConcurrentModificationException。而 Collections.sort() 则不会引发异常。
此项变更使平台行为更加一致:现在,两种方法都会引发 ConcurrentModificationException。
帐号访问和可检测性
除非身份验证器拥有用户帐号或用户授予访问权限,否则,应用将无法再访问用户帐号。仅拥有 GET_ACCOUNTS 权限尚不足以访问用户帐号。要获得帐号访问权限,应用应使用 AccountManager.newChooseAccountIntent() 或特定于身份验证器的方法。获得帐号访问权限后,应用可以调用 AccountManager.getAccounts() 来访问帐号。
Android O 已弃用 LOGIN_ACCOUNTS_CHANGED_ACTION。相反,应用在运行时应使用 addOnAccountsUpdatedListener() 获取帐号更新信息。
有关新增 API 和增加的帐号访问和可检测性方法的信息,请参阅此文档的“新增 API”部分中的帐号访问和可检测性。
提醒窗口
使用 SYSTEM_ALERT_WINDOW 权限的应用无法再使用以下窗口类型来在其他应用和系统窗口上方显示提醒窗口:
TYPE_PHONE
TYPE_PRIORITY_PHONE
TYPE_SYSTEM_ALERT
TYPE_SYSTEM_OVERLAY
TYPE_SYSTEM_ERROR
相反,应用必须使用名为 TYPE_APPLICATION_OVERLAY 的新窗口类型。
使用 TYPE_APPLICATION_OVERLAY 窗口类型显示应用的提醒窗口时,请记住新窗口类型的以下特性:
应用的提醒窗口始终显示在状态栏和输入法等关键系统窗口的下面。
系统可以移动使用 TYPE_APPLICATION_OVERLAY 窗口类型的窗口或调整其大小,以改善屏幕显示效果。
通过打开通知栏,用户可以访问设置来阻止应用显示使用 TYPE_APPLICATION_OVERLAY 窗口类型显示的提醒窗口。
内容变更通知
Android O 更改了 ContentResolver.notifyChange() 和 registerContentObserver(Uri, boolean, ContentObserver) 在面向针对 Android O 的应用中的行为方式。
现在,这些 API 需要在所有 URI 中为颁发机构定义一个有效的 ContentProvider。使用相关权限定义一个有效的 ContentProvider 可帮助您的应用防范来自恶意应用的内容变更,并防止将可能的私密数据泄露给恶意应用。