Android 8.0 Oreo

Android Oreo 中提供哪些功能?

Android Oreo 为您提供许多新的方式,让您能有效扩展应用及提升开发效率。

专为 Android Oreo 构建

以 Android Oreo(API 26 或 27)为目标版本,并使用最新的平台功能和 API 扩展您的应用。

在 ANDROID STUDIO 中设置

Android 8.0 功能的亮点Android 8.0 功能

适用于您的应用 (API 26) 的功能及 API 的主要变化。

了解详情

Android 8.1 功能的亮点Android 8.1 功能

适用于您的应用 (API 27) 的功能及 API 的主要变化。

了解详情

针对 Android Oreo 应用的行为更改行为更改

针对 Android Oreo 应用的系统更改。

了解详情

最新资讯和视频

访问博客 访问 YOUTUBE 频道

BLOG

Double Stuffed Security in Android Oreo

Android Oreo is stuffed full of security enhancements. Over the past few months, we've covered how we've improved the security of the Android platform and its applications: from making it safer to get apps, dropping insecure network protocols,

Android Developers

2017年12月21日

BLOG

Welcoming Android 8.1 Oreo and Android Oreo (Go edition)

At Google for India this Monday, we announced the final release of Android 8.1 Oreo. Android 8.1 Oreo is another exciting step toward bringing to life our vision of an AI-first mobile platform, for everyone, everywhere. Android 8.1 introduces

Android Developers

2017年12月6日

 

Android 8.0 功能和 API

Android 8.0 为用户和开发者引入多种新功能。本文重点介绍面向开发者的新功能。

请务必查阅 Android 8.0 行为变更以了解平台变更可能影响您的应用的领域。

用户体验

通知

在 Android 8.0 中,我们已重新设计通知,以便为管理通知行为和设置提供更轻松和更统一的方式。这些变更包括:

Android 8.0 中的通知长按菜单。

图 1. 用户可以长按应用启动器图标以查看 Android 8.0 中的通知。

  • 通知渠道:Android 8.0 引入了通知渠道,其允许您为要显示的每种通知类型创建用户可自定义的渠道。用户界面将通知渠道称之为通知类别。要了解如何实现通知渠道的信息,请参阅通知渠道指南。
  • 通知标志:Android 8.0 引入了对在应用启动器图标上显示通知标志的支持。通知标志可反映某个应用是否存在与其关联、并且用户尚未予以清除也未对其采取行动的通知。通知标志也称为通知点。要了解如何调整通知标志,请参阅通知标志指南。
  • 休眠:用户可以将通知置于休眠状态,以便稍后重新显示它。重新显示时通知的重要程度与首次显示时相同。应用可以移除或更新已休眠的通知,但更新休眠的通知并不会使其重新显示。
  • 通知超时:现在,使用 setTimeoutAfter() 创建通知时您可以设置超时。您可以使用此函数指定一个持续时间,超过该持续时间后,通知应取消。如果需要,您可以在指定的超时持续时间之前取消通知。
  • 通知设置:当您使用 Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCESIntent 从通知创建指向应用通知设置的链接时,您可以调用 setSettingsText() 来设置要显示的文本。此系统可以提供以下 Extra 数据和 Intent,用于过滤应用必须向用户显示的设置:EXTRA_CHANNEL_IDNOTIFICATION_TAG 和 NOTIFICATION_ID
  • 通知清除:系统现在可区分通知是由用户清除,还是由应用移除。要查看清除通知的方式,您应实现 NotificationListenerService 类的新 onNotificationRemoved() 函数。
  • 背景颜色:您现在可以设置和启用通知的背景颜色。只能在用户必须一眼就能看到的持续任务的通知中使用此功能。例如,您可以为与驾车路线或正在进行的通话有关的通知设置背景颜色。您还可以使用 Notification.Builder.setColor() 设置所需的背景颜色。这样做将允许您使用 Notification.Builder.setColorized() 启用通知的背景颜色设置。
  • 消息样式:现在,使用 MessagingStyle 类的通知可在其折叠形式中显示更多内容。对于与消息有关的通知,您应使用 MessagingStyle 类。您还可以使用新的 addHistoricMessage() 函数,通过向与消息相关的通知添加历史消息为会话提供上下文。

自动填充框架

帐号创建、登录和信用卡交易需要时间并且容易出错。在使用要求执行此类重复性任务的应用时,用户很容易遭受挫折。

Android 8.0 通过引入自动填充框架,简化了登录和信用卡表单之类表单的填写工作。在用户选择接受自动填充之后,新老应用都可使用自动填充框架。

您可以采取某些措施,优化您的应用使用此框架的方式。如需了解详细信息,请参阅自动填充框架概览

画中画模式

Android 8.0 允许以画中画 (PIP) 模式启动操作组件。PIP 是一种特殊的多窗口模式,最常用于视频播放。目前,PIP 模式可用于 Android TV,而 Android 8.0 则让该功能可进一步用于其他 Android 设备。

当某个 Activity 处于 PIP 模式时,它会处于暂停状态,但仍应继续显示内容。因此,您应确保您的应用在 onPause() 处理程序中进行处理时不会暂停播放。相反,您应在 onStop() 中暂停播放视频,并在 onStart() 中继续播放。如需了解详细信息,请参阅多窗口生命周期

要指定您的 Activity 可以使用 PIP 模式,请在清单中将 android:supportsPictureInPicture 设置为 true。(从 Android 8.0 开始,如果您打算在 Android TV 或其他 Android 设备上支持 PIP 模式,则无需将 android:resizeableActivity 设置为 true;只有在您的 Activity 支持其他多窗口模式时,才需要设置 android:resizeableActivity。)

API 变更

Android 8.0 引入一种新的对象 PictureInPictureParams,您可以将该对象传递给 PIP 函数来指定某个 Activity 在其处于 PIP 模式时的行为。此对象还指定了各种属性,例如操作组件的首选纵横比。

现在,在添加画中画中介绍的现有 PIP 函数可用于所有 Android 设备,而不仅限于 Android TV。此外,Android 8.0 还提供以下函数来支持 PIP 模式:

  • Activity.enterPictureInPictureMode(PictureInPictureParams args):将操作组件置于画中画模式。操作组件的纵横比和其他配置设置均由 args 指定。如果 args 中的任何字段为空,系统将使用您上次调用 Activity.setPictureInPictureParams() 时所设置的值。

    指定的操作组件被置于屏幕的一角,屏幕剩余部分则被屏幕显示的上一个操作组件填满。进入 PIP 模式的 Activity 将进入暂停状态,但仍保持已启动状态。如果用户点按此 PIP 操作组件,系统将显示一个菜单供用户操作,而在操作组件处于 PIP 状态期间,不会理会任何触摸事件。

  • Activity.setPictureInPictureParams():更新操作组件的 PIP 配置设置。如果操作组件目前处于 PIP 模式,则会更新此设置;如果操作组件的纵横比发生变化,这非常有用。如果操作组件不处于 PIP 模式,则会使用这些配置设置,而不会考虑您调用的 enterPictureInPictureMode() 函数。

可下载字体

Android 8.0 和 Android 支持库 26 允许您从提供程序应用请求字体,而无需将字体绑定到 APK 中或让 APK 下载字体。此功能可减小 APK 大小,提高应用安装成功率,使多个应用可以共享同一种字体。

如需了解有关下载字体的详细信息,请参阅 可下载字体

XML 中的字体

Android 8.0 推出一项新功能,即 XML 中的字体,允许您使用字体作为资源。这意味着,不再需要以资产的形式捆绑字体。字体在 R 文件中编译,并且作为一种资源,可自动用于系统。然后,您可以利用一种新的资源类型 font 来访问这些字体。

在运行 API 版本 14 及更高版本的设备中,支持库 26 对此功能提供完全支持。

如需了解有关以资源形式使用字体以及检索系统字体有关的详细信息,请参阅 XML 中的字体

自动调整 TextView 的大小

Android 8.0 允许您根据 TextView 的大小自动设置文本展开或收缩的大小。这意味着,在不同屏幕上优化文本大小或者优化包含动态内容的文本大小比以往简单多了。如需了解有关如何在 Android 8.0 中自动调整 TextView 的大小的详细信息,请参阅自动调整 TextView 的大小

 

自适应图标

Android 8.0 引入自适应启动器图标。自适应图标支持视觉效果,可在不同设备型号上显示为各种不同的形状。要了解如何创建自适应图标,请参阅自适应图标预览功能指南。

颜色管理

图像应用的 Android 开发者现在可以利用支持广色域彩色显示的新设备。要显示广色域图像,应用需要在其清单(每个操作组件)中启用一个标志,并加载具有嵌入的广域彩色配置文件(AdobeRGB、Pro Photo RGB、DCI-P3 等)的位图。

WebView API

Android 8.0 提供多种 API,帮助您管理在应用中显示网页内容的 WebView 对象。这些 API 可增强应用的稳定性和安全性,它们包括:

  • Version API
  • Google SafeBrowsing API
  • Termination Handle API
  • Renderer Importance API

要详细了解如何这些 API,请参阅管理 WebView

固定快捷方式和小部件

Android 8.0 引入了快捷方式和微件的应用内固定功能。在您的应用中,您可以根据用户权限为支持的启动器创建固定的快捷方式和小部件。

如需了解详细信息,请参阅固定快捷方式和微件预览功能指南。

最大屏幕纵横比

以 Android 7.1(API 级别 25)或更低版本为目标平台的应用默认的最大屏幕纵横比为 1.86。针对 Android 8.0 或更高版本的应用没有默认的最大纵横比。如果您的应用需要设置最大纵横比,请使用定义您的操作组件的清单文件中的 maxAspectRatio 属性。

多显示器支持

从 Android 8.0 开始,此平台为多显示器提供增强的支持。如果 Activity 支持多窗口模式,并且在具有多显示器的设备上运行,则用户可以将 Activity 从一个显示器移动到另一个显示器。当应用启动 Activity 时,此应用可指定 Activity 应在哪个显示器上运行。

注:如果 Activity 支持多窗口模式,则 Android 8.0 将为该 Activity 自动启用多显示器支持。您应测试您的应用,确保它在多显示器环境下可正常运行。

每次只有一个 Activity 可以处于继续状态,即使此应用具有多个显示器。具有焦点的 Activity 将处于继续状态,所有其他可见的 Activity 均暂停,但不会停止。如需了解有关当多个 Activity 可见时活动生命周期的详细信息,请参阅多窗口生命周期

当用户将 Activity 从一个显示器移动到另一个显示器时,系统将调整 Activity 大小,并根据需要发起运行时变更。您的 Activity 可以自行处理配置变更,或允许系统销毁包含该 Activity 的进程,并以新的尺寸重新创建它。如需了解详细信息,请参阅处理配置变更

ActivityOptions 提供两个新函数以支持多个显示器:

setLaunchDisplayId()

指定 Activity 在启动后应显示在哪个显示器上。

getLaunchDisplayId()

返回操作组件的当前启动显示器。

对 adb shell 进行了扩展,以支持多个显示器。shell start 命令现在可用于启动操作组件,并指定操作组件的目标显示器:

adb shell start <activity_name> --display <display_id>

 

统一的布局外边距和内边距

Android 8.0 让您可以更轻松地指定 View 元素的对边使用相同外边距和内边距的情形。具体来说,您现在可以在布局 XML 文件中使用以下属性:

:如果您自定义应用逻辑以支持不同语言和文化(包括文本方向),请记住,这些属性不会影响 layout_marginStartlayout_marginEndpaddingStart 或 paddingEnd 的值。您可以自行设置这些值和新的垂直与水平布局属性来创建取决于文本方向的布局行为。

指针捕获

某些应用(例如游戏、远程桌面和虚拟化客户端)将大大受益于鼠标指针控制。指针捕获是 Android 8.0 中的一项新功能,可以通过将所有鼠标事件传递到您的应用中焦点视图的方式提供此类控制。

从 Android 8.0 开始,您的应用中的 View 可以请求指针捕获并定义一个侦听器来处理捕获的指针事件。鼠标指针在此模式下将隐藏。如果不再需要鼠标信息,该视图可以释放指针捕获。系统也可以在视图丢失焦点时(例如,当用户打开另一个应用时)释放指针捕获。

如需了解有关如何在您的应用中使用此功能的信息,请参阅指针捕获

应用类别

在适当的情况下,Android 8.0 允许每个应用声明其所属的类别。这些类别用于将应用呈现给用户的用途或功能类似的应用归类在一起,例如按流量消耗、电池消耗和存储消耗将应用归类。您可以在 <application> 清单标记中设置 android:appCategory 属性,定义应用的类别。

Android TV 启动器

Android 8.0 添加了一种以内容为中心的全新 Android TV 主屏幕体验,支持 Android TV 模拟器和 Nexus Player Android 8.0 设备映像。新的主屏幕在对应于频道的行中组织视频内容,这些频道在系统上通过应用填充各个节目。应用可以发布多个频道,用户可以配置他们希望在主屏幕上看到哪些频道。Android TV 也包含一个 Watch Next 行,此行根据用户的观看习惯从应用填充节目。应用也可以提供视频预览,这些预览会在用户聚焦到节目时自动播放。用于填充频道和节目的 API 属于 TvProvider API,这些 API 以 Android 支持库模块的形式随 Android 8.0 分发。

AnimatorSet

从 Android 8.0 开始,AnimatorSet API 现在支持寻道和倒播功能。寻道功能允许您将动画的位置设置为指定的时间点处。如果您的应用包含可撤消的操作的动画,倒播功能会很有用。现在,您不必定义两组独立的动画,而只需反向播放同一组动画。

输入和导航

键盘导航键区

如果您的应用中,某个操作组件使用一种复杂的视图层次结构(如图 2 所示),可考虑将多组界面元素组成一个键区,简化键盘导航这些元素的操作。用户可以在 Chromebook 设备上按 Meta+Tab 或 Search+Tab,在不同键区之间导航。键区的一些范例包括:侧面板、导航栏、主内容区域和可能包含多个子元素的元素。

以一个包含五个导航键区的操作组件为例,用户可以使用键盘导航键区快捷键进行导航。键区按以下布局显示:顶部面板、左侧面板、主内容区域、底部面板和浮动操作按钮。

图 2. 包含 5 个键区的操作组件

要将一个 View 或 ViewGroup 元素设置为一个键区,请在元素的布局 XML 文件中将 android:keyboardNavigationCluster 属性设置为 true,或者将 true 传递至应用界面逻辑中的 setKeyboardNavigationCluster()

:键区不能嵌套,不过,非嵌套键区可以显示在层次结构的不同层级。如果您尝试嵌套键区,框架仅会将最顶层的 ViewGroup 元素视为键区。

在具有触摸屏的设备中,您可以将某个键区指定的 ViewGroup 对象的 android:touchscreenBlocksFocus 元素设置为 true,仅允许从键区导航进入和离开此键区。如果您将此配置应用于某个键区,用户将无法使用 Tab 键或箭头键导航进入或离开此键区,而是必须按键区导航键盘组合键。

视图默认焦点

在 Android 8.0 中,您可以指定在(重新)创建的操作组件继续运行并且用户按下键盘导航键(例如 Tab 键)之后应接收焦点的 View。要应用“设为默认焦点”设置,请在包含界面元素的布局 XML 文件中将 View 元素的 android:focusedByDefault 属性设置为 true,或者将 true 传递至应用界面逻辑中的 setFocusedByDefault()

系统

新的 StrictMode 检测程序

Android 8.0 添加了三个新的 StrictMode 检测程序,帮助识别应用可能出现的错误:

缓存数据

Android 8.0 优化了缓存数据的导航和行为。现在,每个应用均获得一定的磁盘空间配额,用于存储 getCacheQuotaBytes(UUID) 返回的缓存数据。

当系统需要释放磁盘空间时,将开始从超过配额最多的应用中删除缓存文件。因此,如果将您的缓存数据量始终保持低于配额的水平,则在必须清除系统中的某些文件时,您的缓存文件将能坚持到最后。系统在决定删除您的应用中的哪些缓存文件时,将首先考虑删除最旧的文件(由修改时间确定)。

您还可以针对每个目录启用两种新行为,以控制系统如何释放缓存数据:

  • StorageManager.setCacheBehaviorAtomic() 可用于指示某个目录及其所有内容应作为一个不可分割的整体进行删除。
  • setCacheBehaviorTombstone(File, boolean) 可用于指示不应删除某个目录内的文件,而应将它们截断到 0 字节长度,使空文件保持完好。

最后,在需要为大文件分配磁盘空间时,可考虑使用新的 allocateBytes(FileDescriptor, long) API,它将自动清除属于其他应用的缓存文件(根据需要),以满足您的请求。在确定设备是否有足够的磁盘空间保存您的新数据时,请调用 getAllocatableBytes(UUID) 而不要使用 getUsableSpace(),因为前者会考虑系统要为您清除的任何缓存数据。

内容提供程序分页

我们已更新内容提供程序以支持加载大型数据集,每次加载一页。例如,一个具有大量图像的照片应用可查询要在页面中显示的数据的子集。内容提供程序返回的每个结果页面由一个 Cursor 对象表示。客户端和提供程序必须实现分页才能利用此功能。

如需了解有关内容提供程序变更的详细信息,请参阅 ContentProvider 和 ContentProviderClient

内容刷新请求

现在,ContentProvider 和 ContentResolver 类均包含 refresh() 函数,这样,客户端可以更轻松地知道所请求的信息是否为最新信息。

您可以扩展 ContentProvider 以添加自定义的内容刷新逻辑。请务必重写 refresh() 函数,以返回 true,告知提供程序的客户端您已尝试自行刷新数据。

您的客户端应用可通过调用另一个函数(又称 refresh()),显式请求已刷新的内容。在调用此函数时,传入待刷新数据的 URI。

:由于您可能通过网络不断请求数据,您应仅在有明显迹象表明内容确已过时时才从客户端调用 refresh()。执行此类内容刷新最常见的原因是响应滑动刷新手势,该手势显式请求当前界面显示最新内容。

JobScheduler 改进

Android 8.0 引入了对 JobScheduler 的多项改进。由于您通常可以使用计划作业替代现在受限的后台服务或隐式广播接收器,这些改进可以让您的应用更轻松地符合新的后台执行限制

JobScheduler 的更新包括:

  • 您现在可以将工作队列与计划作业关联。要将一个工作项添加到作业的队列中,请调用 JobScheduler.enqueue()。当作业运行时,它可以将待定工作从队列中剥离并进行处理。这种功能可以处理之前需要启动后台服务(尤其是实现 IntentService 的服务)的许多用例。
  • 您现在可以通过调用 JobInfo.Builder.setClipData() 的方式将 ClipData 与作业关联。利用此选项,您可以将 URI 权限授予与作业关联,类似于这些权限传递到 Context.startService() 的方式。您也可以将 URI 权限授予用于工作队列上的 intent。
  • 计划作业现在支持多个新的约束条件:

    JobInfo.isRequireStorageNotLow()

    如果设备的可用存储空间非常低,作业将不会运行。

    JobInfo.isRequireBatteryNotLow()

    如果电池电量等于或低于临界阈值,作业将不会运行;临界阈值是指设备显示 Low battery warning 系统对话框的电量。

    NETWORK_TYPE_METERED

    作业需要一个按流量计费的网络连接,比如大多数移动数据网络数据套餐。

自定义数据存储

Android 8.0 允许您为首选项提供自定义数据存储,如果您的应用将首选项存储在云或本地数据库中,或者如果首选项特定于某个设备,此功能会非常有用。如需了解有关实现数据存储的详细信息,请参阅自定义数据存储

findViewById() 签名变更

现在,findViewById() 函数的全部实例均返回 <T extends View> T,而不是 View。此变更会带来以下影响:

  • 例如,如果 someMethod(View) 和 someMethod(TextView) 均接受调用 findViewById() 的结果,这可能导致现有代码的返回类型不确定。
  • 在使用 Java 8 源语言时,这需要在返回类型不受限制时(例如,assertNotNull(findViewById(...)).someViewMethod()))显式转换为 View
  • 重写非最终的 findViewById() 函数(例如,Activity.findViewById())将需要更新其返回类型。

媒体增强功能

VolumeShaper

有一个新的 VolumeShaper 类。您可以用它来执行简短的自动音量转换,例如淡入、淡出和交叉淡入淡出。

音频焦点增强功能

音频应用通过请求和舍弃音频焦点的方式在设备上共享音频输出。应用通过启动或停止播放或者闪避音量的方式处理处于聚焦状态的变更。有一个新的 AudioFocusRequest 类。对于此类,应用在处理音频焦点变化时会使用新功能自动闪避延迟聚焦

媒体指标

新的 getMetrics() 函数将返回一个包含配置和性能信息的 PersistableBundle 对象,用一个包含属性和值的地图表示。为以下媒体类定义 getMetrics() 函数:

为每个实例单独收集指标,并持续到实例的生命周期结束为止。如果没有可用的指标,则此函数将返回 null。返回的实际指标取决于类。

MediaPlayer

Android 8.0 为 MediaPlayer 类添加了多种新函数。这些函数可以从多个方面增强您的应用处理媒体播放的能力:

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 8.0 允许您查询和请求设备产生声音的方式。对音频播放的以下控制将让您的服务更轻松地仅在有利的设备条件下产生声音。

Google 智能助理的新音频使用类型

AudioAttributes 类包含一种新的声音类型,即 USAGE_ASSISTANT,对应于 Google 智能助理在设备上的回答。

设备音频播放的变更

如果您希望自己的服务仅在特定的设备音频配置处于活动状态时开始产生声音,您可以使用 AudioManager 类注册一个 AudioManager.AudioPlaybackCallback 实例,后者的onPlaybackConfigChanged() 函数可以帮助您确定当前活动的音频属性集。

显式请求音频焦点

您的服务可以使用 requestAudioFocus() 函数提交一个更精细的设备级音频焦点接收请求。传入一个 AudioFocusRequest 对象,您可以使用 AudioFocusRequest.Builder 创建这个对象。在这个构建类中,您可以指定以下选项:

  • 您希望获得的焦点类型,例如 AUDIOFOCUS_GAIN_TRANSIENT 或 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
  • 当另一个音频服务获得设备焦点时,您的服务应以更安静的方式继续,还是完全暂停。
  • 您的服务能否等待获得焦点,直至设备就绪。

:构建您的 AudioFocusRequest 实例时,如果您通过调用 setAcceptsDelayedFocusGain() 指示您的服务可以等待产生声音,您也必须调用 setOnAudioFocusChangeListener(),以便您的服务了解它何时可以开始产生声音。

增强的媒体文件访问功能

存储访问框架 (SAF) 允许应用显示自定义 DocumentsProvider,后者可以为其他应用提供访问数据源中的文件的权限。事实上,文档提供程序甚至可以提供驻留在网络存储区或使用媒体传输协议 (MTP) 等协议的文件的访问权限。

但是,访问远程数据源中的大媒体文件面临一些挑战:

  • 媒体播放器需要以寻址方式访问来自文档提供程序的文件。当大媒体文件驻留在远程数据源上时,文档提供程序必须事先提取所有数据,并创建快照文件描述符。媒体播放器无法播放没有文件描述符的文件,因此在文档提供程序完成文件下载前,无法开始播放。
  • 照片应用等媒体集合管理器必须通过作用域文件夹遍历一系列访问 URI 才能访问存储在外部 SD 卡上的媒体。这种访问模式会让媒体上的批量操作(例如移动、复制和删除)变得非常缓慢。
  • 媒体集合管理器无法根据文档的 URI 确定其位置。这就让这些类型的应用难以允许用户选择媒体文件的保存位置。

Android 8.0 通过改进存储访问框架解决了各个挑战。

自定义文档提供程序

从 Android 8.0 开始,存储访问框架允许自定义文档提供程序为驻留在远程数据源中的文件创建可寻址的文件描述符。SAF 可打开文件,获取原生可寻址的文件描述符。然后 SAF 向文档提供程序提交离散字节请求。此功能使文档提供程序可以返回媒体播放器应用请求的准确字节范围,而不必事先缓存整个文件。

要使用此功能,您需要调用新的 StorageManager.openProxyFileDescriptor() 函数。openProxyFileDescriptor() 函数可接受 ProxyFileDescriptorCallback 对象作为回调。任何时候,当客户端应用对文档提供程序返回的文件描述符执行文件操作时,SAF 都会调用回调。

直接文档访问

从 Android 8.0 开始,您可以使用 getDocumentUri() 函数获得与给定 mediaUri 引用相同文档的 URI。不过,由于返回的 URI 由 DocumentsProvider 提供支持,媒体集合管理器可以直接访问文档,不用遍历作用域目录树。因此,媒体管理器能够以明显加快的速度对文档执行文件操作。

注意getDocumentUri() 函数仅可以定位媒体文件;无法授予应用访问这些文件的权限。要详细了解如何获取媒体文件的访问权限,请参阅参考文档。

文档路径

在 Android 8.0 中使用存储访问框架时,您可以根据文档的 ID,使用 findDocumentPath() 函数(存在于 DocumentsContract 和 DocumentsProvider 类中)从文件系统的根目录中确定路径。该函数将在 DocumentsContract.Path 对象中返回此路径。如果文件系统对相同文档有多个定义的路径,该函数将返回访问具有给定 ID 的文档时最常使用的路径。

此功能在下列情况下特别有用:

  • 您的应用使用可以显示特定文档位置的“另存为”对话框。
  • 您的应用在搜索结果视图中显示文件夹并且如果用户选择某个文件夹,应用必须加载此特定文件夹内的子文档。

:如果您的应用仅具有路径中某些文档的访问权限,那么 findDocumentPath() 的返回值将仅包含您的应用可以访问的文件夹和文档。

连接

WLAN 感知

Android 8.0 新增了对 WLAN 感知的支持,此技术基于周边感知联网 (NAN) 规范。在具有相应 WLAN 感知硬件的设备上,应用和附近设备可以通过 WLAN 进行搜索和通信,无需依赖互联网接入点。我们正在与硬件合作伙伴合作,以尽快将 WLAN 感知技术应用于设备。要了解有关如何将 WLAN 感知集成到您的应用中的信息,请参阅 WLAN 感知

蓝牙

Android 8.0 通过增加以下功能,增强了平台对蓝牙的支持:

  • 支持 AVRCP 1.4 标准,该标准支持音乐库浏览。
  • 支持蓝牙低功耗 (BLE) 5.0 标准。
  • 将 Sony LDAC 编解码器集成到蓝牙堆叠中。

配套设备配对

在尝试通过蓝牙、BLE 和 WLAN 与配套设备配对时,Android 8.0 提供的 API 允许您自定义配对请求对话框。如需了解详细信息,请参阅配套设备配对

如需了解有关在 Android 上使用蓝牙的详细信息,请参阅蓝牙指南。有关对蓝牙所作的特定于 Android 8.0 的变更,请参阅 Android 8.0 行为变更页面的蓝牙部分。

共享

智能共享

Android 8.0 了解用户的个性化分享首选项,在通过哪些应用分享各个类型的内容方面,也有着更好的把握。例如,如果用户为一张收据拍照,Android 8.0 可以建议费用跟踪应用;如果用户自拍,一款社交媒体应用可以更好地处理图像。Android 8.0 可以根据用户的个性化首选项自动学习所有这些模式。

智能分享适用于 image 之外的内容类型,例如 audiovideotext 和 URL 等。

要启用智能分享,请将具有最多三个字符串注释的 ArrayList 添加到分享内容的 intent。这些注释应说明内容中的主要部分或主题。下面的代码示例显示了如何向 intent 添加注释:

ArrayList<String> annotations = new ArrayList<>();

annotations.add("topic1");
annotations.add("topic2");
annotations.add("topic3");

intent.putStringArrayListExtra(
    Intent.EXTRA_CONTENT_ANNOTATIONS,
    annotations
);

 

如需了解有关智能分享注释的详细信息,请参阅 EXTRA_CONTENT_ANNOTATIONS

智能文本选择

在兼容设备上,Android 8.0 让应用可以帮助用户以更有意义的方式与文本交互。当用户长按某个实体中可识别格式的单词(例如某个地址或餐馆名称)时,系统会选中整个实体。用户会看到一个浮动工具栏,该工具栏包含可以处理所选文本实体的应用。例如,如果系统识别出某个地址,它可以将用户导向地图应用。

系统识别的实体包括地址、网址、电话号码和电子邮件地址。如需了解详细信息,请参阅 TextClassifier

无障碍功能

Android 8.0 支持开发者使用以下无障碍功能创建自己的无障碍服务。如需了解有关如何让您的应用更便于访问的更多信息,请参阅无障碍功能

无障碍功能按钮

您的无障碍服务现在可以请求在系统的导航区域显示无障碍功能按钮,该按钮让用户可从其设备上的任意位置快速激活您的服务功能。要执行此操作,请在某个 AccessibilityServiceInfo 对象的 android:accessibilityFlags 属性中添加 FLAG_REQUEST_ACCESSIBILITY_BUTTON 标志。稍后,您可以使用 registerAccessibilityButtonCallback() 注册回调。

:此功能仅适用于提供软件渲染导航区域的设备。请始终使用 isAccessibilityButtonAvailable(),并通过实现 onAvailabilityChanged() 根据无障碍功能按钮的可用性来响应变更。通过该方式,用户可以始终访问您的服务功能,即使该无障碍功能按钮不受支持或变得不可用。

独立的音量调整

Android 8.0 引入了 STREAM_ACCESSIBILITY 音量类别,允许您单独控制无障碍服务音频输出的音量,而不会影响设备上的其他声音。

要使用这个新的流类型来控制无障碍服务音量,请在无障碍服务中设置 FLAG_ENABLE_ACCESSIBILITY_VOLUME 选项。然后,您可以使用 adjustStreamVolume() 更改设备的无障碍服务音频音量。

指纹手势

您的无障碍服务也可以响应替代的输入机制,即沿设备的指纹传感器按特定方向滑动(上、下、左和右)。要接收有关这些交互的回调,请完成以下一系列步骤:

  1. 声明 USE_FINGERPRINT 权限和 CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES 功能。
  2. 在 android:accessibilityFlags 属性中设置 FLAG_REQUEST_FINGERPRINT_GESTURES 标志。
  3. 使用 registerFingerprintGestureCallback() 注册回调。

请记住,并非所有设备都包含指纹传感器。您可以使用 isHardwareDetected() 函数识别设备是否支持此传感器。即使对于包含指纹传感器的设备,您的服务也只有在指纹传感器不用于身份验证目的时才可使用它。要识别此传感器何时可用,请调用 isGestureDetectionAvailable() 函数并实现 onGestureDetectionAvailabilityChanged() 回调。

字词级突出显示

要确定 TextView 对象中可见字符的位置,您可以在 EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY 中将其作为第一个参数传递到 refreshWithExtraData() 中。随后会更新您为 refreshWithExtraData() 提供的作为第二个参数的 Bundle 对象,使之包含一个可打包的 Rect 对象数组。每个 Rect 对象代表某个特定字符的边界框。

如果您的服务使用 TextToSpeech 对象朗读屏幕上出现的内容,您可以获取有关文本到语音转换引擎何时开始朗读单个合成字词时的准确时间信息,前提是文本到语音转换引擎提供此信息。当引擎即将开始播放特定范围文本的音频时,Text-to-Speech API 会通知您的服务,将使用 onRangeStart() 函数开始朗读此范围的文本。

如果您创建自己的 TextToSpeechService 实现,您可以使用 rangeStart() 函数支持这一新功能。

标准化单端范围值

AccessibilityNodeInfo 的一些实例使用 AccessibilityNodeInfo.RangeInfo 的某个实例来表明界面元素可接受一定范围的值。使用 RangeInfo.obtain() 创建范围或使用 getMin() 和 getMax() 检索此范围的极值时,请注意,Android 8.0 规定了标准化单端范围:

提示文本

Android 8.0 包含可用于与文本可编辑对象的提示文本进行交互的多个函数:

  • isShowingHintText() 和 setShowingHintText() 函数分别显示和设置节点的当前文本内容是否表示节点的提示文本。如果节点不包含可编辑文本,则它不应包含提示文本。
  • 要访问提示文本本身,请使用 getHintText()。即使某个对象当前未显示提示文本,系统也能成功调用 getHintText()

连续的手势分派

您的服务现在可以使用 GestureDescription.StrokeDescription 构造函数中的最后一个参数 willContinue,指定属于同一设定手势的笔划的顺序。

安全性与隐私

权限

Android 8.0 引入了多个与电话有关的新权限:

这些权限均被划分为危险类别,属于 PHONE 权限组。

新的帐号访问和 Discovery API

Android 8.0 对应用访问用户帐号的方式引入多项改进。对于由身份验证器管理的帐号,身份验证器在决定对应用隐藏帐号还是显示帐号时可以使用自己的策略。Android 系统跟踪可以访问特定帐号的应用。

在以前的 Android 版本中,想要跟踪用户帐号列表的应用必须获取有关所有帐号的更新,包括具有不相关类型的帐号。Android 8.0 添加了 addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean, java.lang.String[]) 函数,其允许应用指定应接收帐号变更的帐号类型列表。

API 变更

AccountManager 提供六个新函数以帮助身份验证器管理哪些应用可以查看某个帐号:

Android 8.0 引入两个特殊的软件包名称值,以使用 setAccountVisibility(android.accounts.Account, java.lang.String, int) 函数指定未设置的应用的可见性级别。PACKAGE_NAME_KEY_LEGACY_VISIBLE 可见性值应用于具有 GET_ACCOUNTS 权限的应用,并且其目标 Android 版本低于 Android 8.0,或其签名与针对任意 Android 版本的身份验证器匹配。PACKAGE_NAME_KEY_LEGACY_NOT_VISIBLE 为之前未设置的应用提供默认的可见性值,对于此类应用,PACKAGE_NAME_KEY_LEGACY_VISIBLE 不适用。

如需了解有关新的帐号访问和发现 API 的详细信息,请参阅 AccountManager 和 OnAccountsUpdateListener 参考。

Google Safe Browsing API

WebView 类现在添加了一个 Safe Browsing API 来增强网络浏览的安全性。如需了解详细信息,请参阅 Google Safe Browsing API

测试

仪器测试

Android 8.0 为应用的仪器测试提供以下几项额外支持。

针对非默认应用进程运行

现在,您可以指定针对您的应用的默认进程以外的进程运行特定仪器测试。如果您的应用包含多个在不同进程中运行的操作组件,此配置非常有用。

要定义非默认进程仪器测试,请导航至您的清单文件,然后导航至所需的 <instrumentation> 元素。添加 android:targetProcess 属性,并将它的值设置为以下值之一:

  • 特定进程的名称。
  • 以逗号分隔的进程名称列表。
  • 通配符("*"),允许针对任何执行 android:targetPackage 属性中指定的软件包中的代码的已启动进程运行仪器测试。

在执行仪器测试时,您可以通过调用 getProcessName() 检查正在测试哪个进程。

在测试过程中报告结果

现在,通过调用 addResults(),您可以在执行仪器测试时(而不用等到测试后)报告结果。

用于测试的模拟 Intent

为了更轻松地为您应用的操作组件创建隔离、独立的界面测试,Android 8.0 引入了 onStartActivity() 函数。要处理您的测试类调用的特定 intent,您可以在 Instrumentation.ActivityMonitor 类的自定义子类中替换此函数。

当您的测试类调用 intent 时,该函数将返回一个存根 Instrumentation.ActivityResult 对象,而不是执行 intent 本身。通过在您的测试中使用这种模拟 intent 逻辑,您可以侧重于自己的操作组件如何准备和处理您传递到不同操作组件或完全不同的应用中的 intent。

运行时和工具

平台优化

Android 8.0 为平台引入了运行时优化和其他优化,这些优化将带来多项性能改进。这些优化包括并发压缩垃圾回收、更有效的内存利用和代码区域。

它们可以加快启动时间,并为 OS 和应用带来更好的性能。

更新的 Java 支持

Android 8.0 添加了对更多 OpenJDK Java API 的支持:

要详细了解这些新添加的软件包中的类和函数,请参阅 API 参考文档。

如果您想要在 Android Studio 中使用 Java 8 语言功能,您应下载最新的预览版本

更新的 ICU4J Android Framework API

Android 8.0 扩展了 ICU4J Android 框架 API—,它是 ICU4J API 的子集—,供应用开发者在 android.icu 软件包中使用。这些 API 使用设备上具有的本地化数据。因此,您无需在 APK 中编译 ICU4J 库,从而减少 APK 占用空间。

表 1. Android 中使用的 ICU、CLDR 和 Unicode 版本。

Android API 级别ICU 版本CLDR 版本Unicode 版本
Android 7.0(API 级别 24),Android 7.1(API 级别 25)56288.0
Android 8.058.230.0.39.0

如需详细了解针对受支持的 ICU4J API 的更新,请阅读版本说明

Android 企业版

已为运行 Android 8.0 的设备引入新的企业功能和 API。重要功能包括如下:

  • 完全托管的设备中的工作资料使企业可以在管理工作数据与个人数据的同时,将它们分离开来。
  • API 委派允许设备所有者和个人资料所有者将应用管理分配给其他应用。
  • 配置流程中的用户体验改进措施(包含新的自定义选项)缩短了设置时间。
  • 蓝牙、WLAN、备份和安全性方面的新增控制选项使企业可以更精细地管理设备。网络操作组件日志记录可帮助企业追查问题。

如需详细了解上述及其他新增 Android 企业版 API 和功能,请参阅企业中的 Android

Android 8.0 行为变更

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

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

针对所有 API 级别的应用

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

后台执行限制

Android 8.0 为提高电池续航时间而引入的变更之一是,当您的应用进入已缓存状态时,如果没有活动的组件,系统将解除应用具有的所有唤醒锁。

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

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

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

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

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

如需了解详细信息,请参阅后台执行限制

Android 后台位置限制

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

此类变更会影响以下 API:

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

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

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

如需了解此类变更的详细信息,请参阅后台位置限制

应用快捷键

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

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

如需了解有关应用快捷方式变更的更多信息,请参阅固定快捷方式和微件预览功能指南

语言区域和国际化

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

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

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

  • 调用 Currency.getDisplayName(null) 会引发 NullPointerException,以与文档规定的行为保持一致。
  • 时区名称的分析方法发生变化。之前,Android 设备使用在启动时取样的系统时钟值,缓存用于分析日期时间的时区名称。因此,如果在启动时或其他较为罕见的情况下系统时钟出错,可能对分析产生负面影响。

    现在,一般情况下,在分析时区名称时分析逻辑将使用 ICU 和当前系统时钟值。此项变更可提供更加准确的结果,如果您的应用使用 SimpleDateFormat 等类,此结果可能与之前的 Android 版本不同。

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

提醒窗口

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

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

如需了解详细信息,请参阅针对 Android 8.0 的应用的行为变更内的提醒窗口的常用窗口类型部分。

输入和导航

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

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

  • 现在,如果您没有为 View 对象(前景或背景图片)定义任何焦点状态颜色,框架会为 View 设置默认的焦点突出显示颜色。此焦点突出显示标志是基于操作组件主题背景的涟漪图片。

    如果您不希望 View 对象在接收焦点时使用此默认突出显示标志,请在包含 View 的布局 XML 文件中将 android:defaultFocusHighlightEnabled 属性设置为 false,或者将 false 传递至应用界面逻辑中的 setDefaultFocusHighlightEnabled()

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

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

如需详细了解如何在您的应用中改善对键盘导航的支持,请阅读支持键盘导航指南。

网页表单自动填充

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

WebSettings

WebViewDatabase

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

无障碍功能

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

如需了解有关如何让您的应用更便于访问的更多信息,请参阅无障碍功能

网络连接和 HTTP(S) 连接

Android 8.0 对网络连接和 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 8.0 废除了此权宜方法;系统对格式错误的 URI 会返回 null。

  • Android 8.0 在实现 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 8.0 之前的版本,即使 send() 调用成功,后续的 recv() 调用也会引发 SocketException。为确保一致性,现在这两个调用均会引发 SocketException。
  • 在回退到 TCP Echo 协议之前,InetAddress.isReachable() 会尝试执行 ICMP。
    • 对于某些屏蔽端口 7 (TCP Echo) 的主机(例如 google.com),如果它们接受 ICMP Echo 协议,现在也许能够访问它们。
    • 对于确实无法访问的主机,此项变更意味着调用需要两倍的时间才能返回结果。

蓝牙

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

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

无缝连接

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

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

安全性

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

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

有关提升应用安全性的其他准则,请参阅面向 Android 开发者的安全性

隐私性

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

  • 现在,平台改变了标识符的处理方式。

    要借助一个简单的标准系统实现应用获利,请使用广告 ID。广告 ID 是 Google Play 服务针对广告服务提供的唯一 ID,此 ID 可由用户重置。

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

记录未捕获的异常

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

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

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

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

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

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

集合的处理

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

Android 企业版

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

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

如需查看 Android 8.0 中的所有企业版变更和了解它们可能给您的应用带来的影响,请阅读企业中的 Android

针对 Android 8.0 的应用

这些行为变更专门应用于针对 O 平台或更高平台版本的应用。针对 Android 8.0 或更高平台版本进行编译,或将 targetSdkVersion 设为 Android 8.0 或更高版本的应用开发者必须修改其应用以正确支持这些行为(如果适用)。

提醒窗口

使用 SYSTEM_ALERT_WINDOW 权限的应用无法再使用以下窗口类型来在其他应用和系统窗口上方显示提醒窗口:

相反,应用必须使用名为 TYPE_APPLICATION_OVERLAY 的新窗口类型。

使用 TYPE_APPLICATION_OVERLAY 窗口类型显示应用的提醒窗口时,请记住新窗口类型的以下特性:

  • 应用的提醒窗口始终显示在状态栏和输入法等关键系统窗口的下面。
  • 系统可以移动使用 TYPE_APPLICATION_OVERLAY 窗口类型的窗口或调整其大小,以改善屏幕显示效果。
  • 通过打开通知栏,用户可以访问设置来阻止应用显示使用 TYPE_APPLICATION_OVERLAY 窗口类型显示的提醒窗口。

内容变更通知

Android 8.0 更改了 ContentResolver.notifyChange() 和 registerContentObserver(Uri, boolean, ContentObserver) 在针对 Android 8.0 的应用中的行为方式。

现在,这些 API 需要在所有 URI 中为颁发机构定义一个有效的 ContentProvider。使用相关权限定义一个有效的 ContentProvider 可帮助您的应用防范来自恶意应用的内容变更,并防止将可能的私密数据泄露给恶意应用。

视图焦点

可点击的 View 对象现在默认也可以成为焦点。如果您希望 View 对象可点击但不可成为焦点,请在包含 View 的布局 XML 文件中将 android:focusable 属性设置为 false,或者将 false 传递至应用界面逻辑中的 setFocusable()

安全性

如果您的应用的网络安全性配置选择退出对明文流量的支持,那么您的应用的 WebView 对象无法通过 HTTP 访问网站。每个 WebView 对象必须转而使用 HTTPS。

有关提升应用安全性的其他准则,请参阅面向 Android 开发者的安全性

帐号访问和可检测性

除非身份验证器拥有用户帐号或用户授予访问权限,否则,应用将无法再访问用户帐号。仅拥有 GET_ACCOUNTS 权限尚不足以访问用户帐号。要获得帐号访问权限,应用应使用 AccountManager.newChooseAccountIntent() 或特定于身份验证器的函数。获得帐号访问权限后,应用可以调用 AccountManager.getAccounts() 来访问帐号。

Android 8.0 已弃用 LOGIN_ACCOUNTS_CHANGED_ACTION。相反,应用在运行时应使用 addOnAccountsUpdatedListener() 获取帐号更新信息。

有关新增 API 和增加的帐号访问和可检测性函数的信息,请参阅此文档的“新增 API”部分中的帐号访问和可检测性

隐私性

以下变更影响 Android 8.0 的隐私性。

  • 系统属性 net.dns1net.dns2net.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 之前,如果应用在运行时请求权限并且被授予该权限,系统会错误地将属于同一权限组并且在清单中注册的其他权限也一起授予应用。

对于针对 Android 8.0 的应用,此行为已被纠正。系统只会授予应用明确请求的权限。然而,一旦用户为应用授予某个权限,则所有后续对该权限组中权限的请求都将被自动批准。

例如,假设某个应用在其清单中列出 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE。应用请求 READ_EXTERNAL_STORAGE,并且用户授予了该权限。如果该应用针对的是 API 级别 24 或更低级别,系统还会同时授予 WRITE_EXTERNAL_STORAGE,因为该权限也属于同一 STORAGE 权限组并且也在清单中注册过。如果该应用针对的是 Android 8.0,则系统此时仅会授予 READ_EXTERNAL_STORAGE;不过,如果该应用后来又请求 WRITE_EXTERNAL_STORAGE,则系统会立即授予该权限,而不会提示用户。

媒体

  • 框架会执行音频闪避。进行 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 时,应用不会失去焦点。新的 API 适用于需要暂停而不是闪避的应用。请注意,此行为无法在 Android 8.0 1 版本中实现。
  • 当用户打电话时,活动的媒体流将在通话期间静音。
  • 所有与音频相关的 API 都应使用 AudioAttributes 而不是音频流类型来说明音频播放用例。仅为音量控制继续使用音频流类型。流类型(例如,已弃用的 AudioTrack constructor)的其他用途仍然有效,但是系统会将其记录为错误。
  • 使用 AudioTrack 时,如果应用请求了足够大的音频缓冲区,则框架将尝试使用深度缓冲区输出(如果可用)。
  • 在 Android 8.0 中,媒体按钮事件的处理有所不同:
    1. 在界面操作组件中处理媒体按钮未发生变化:前台操作组件在处理媒体按钮时仍然优先。
    2. 如果前台操作组件不处理媒体按钮,系统会将媒体按钮路由到最近在本地播放音频的应用。在确定哪些应用接收媒体按钮事件时,不再考虑活动状态、标志和媒体会话的播放状态。即使在应用调用 setActive(false) 后,媒体会话仍然可以接收媒体按钮事件。
    3. 如果应用的媒体会话已经释放,系统会将媒体按钮事件发送到应用的 MediaButtonReceiver(如果有)。
    4. 对于任何其他情况,系统都会舍弃媒体按钮事件。与其开始播放错误的应用,不如不播放任何东西。

    下图汇总了新的媒体按钮路由逻辑。

    媒体按钮路由

原生库

在针对 Android 8.0 的应用中,如果原生库包含任何可写且可执行的加载代码段,则不会再加载原生库。倘若某些应用的原生库包含不正确的加载代码段,则此变更可能会导致这些应用停止工作。这是一种安全加强措施。

如需了解详细信息,请参阅可写且可执行的代码段

与早期的开发者预览版相同,Android 8.0 还有助于更轻松地发现所有与链接器有关的问题。链接器的变更绑定到应用的目标 API 级别。如果应用的目标 API 级别发生链接器变更,则该应用无法加载该库。如果您的目标 API 级别低于发生链接器变更的 API 级别,则 logcat 会显示一条警告消息。在预览版期间,与链接器有关的问题不仅会显示在 logcat 中,也会以 toast 的形式显示。对于特定的 API 级别,警告可能会变成错误,此变更有助于提前发现此类问题。

集合的处理

在 Android 8.0 中,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 8.0 之前的平台版本中,如果通过调用 List.sort() 进行排序,则当迭代处理 ArrayList 以及在迭代过程中调用 sort()时,会引发 ConcurrentModificationException。而 Collections.sort() 则不会引发异常。

    此项变更使平台行为更加一致:现在,两种方法都会引发 ConcurrentModificationException

类加载行为

Android 8.0 检查确保类加载器在加载新类时不会违反运行时假设条件。不论类引用自 Java(来自 forName())、Dalvik 字节码还是 JNI,都会执行这些检查。平台不会拦截 Java 对 loadClass() 函数的直接调用,也不会检查此类调用的结果。此行为不应影响运行良好的类加载器的正常运行。

平台将检查类加载器返回的类描述符是否与预期的描述符一致。如果返回的描述符与预期不符,平台会引发 NoClassDefFoundError 错误,并在异常日志中存储一条注明不一致之处的详细错误消息。

平台还检查请求的类描述符是否有效。此检查捕获间接加载诸如 GetFieldID() 等类的 JNI 调用,向这些类传递无效的描述符。例如,找不到包含 java/lang/String 签名的字段,是因为此签名无效;它应为 Ljava/lang/String;

这与 JNI 对 FindClass() 的调用不同,其中 java/lang/String 是一个有效的完全限定名称。

Android 8.0 不支持多个类加载器同时尝试使用相同的 DexFile 对象来定义类。尝试进行此操作,会导致 Android 运行时引发 InternalError 错误,同时显示消息“Attempt to register dex file <filename> with multiple class loaders”。

DexFile API 现已弃用,强烈建议您改为使用此平台的类加载器之一,包括 PathClassLoader 或 BaseDexClassLoader

注: 您可以创建多个引用文件系统中同一个 APK 或 JAR 文件容器的类加载器。这样做通常不会占用大量内存:如果存储而不压缩容器中的 DEX 文件,平台可以对此类文件执行 mmap 操作,而不直接提取它们。但是,如果平台必须从容器中提取 DEX 文件,以这种方式引用 DEX 文件可能占用大量内存。

在 Android 中,所有类加载器都被视为支持并行运行。当多个线程争用同一个类加载器加载相同的类时,第一个完成此操作的线程胜出,而操作结果将用于其他线程。无论类加载器是返回同一个类、返回不同的类还是引发异常,都将发生此行为。该平台静默忽略此类异常。

注意: 在低于 Android 8.0 的平台版本中,违反这些假设条件可能导致多次定义同一个类、由于类混淆造成堆损坏和其他不良影响。

 

向 Android 8.0 迁移应用

Android 8.0 引入了若干新的功能和 API,并加入了即便您未对应用做任何更改仍可能对其行为产生影响的一些变动。为帮助您做好准备,本页面将说明如何执行兼容性测试,以及如何更新应用以便利用 Android 8.0 的新功能:

  1. 确保平台兼容性

    验证您的应用能够在新版本平台上全功能运行。在此阶段,您不需要使用新的 API,也不需要更改应用的 targetSdkVersion,但可能需要进行一些细微的更改。

  2. 使用 Android 8.0 SDK 构建应用

    当您准备好利用平台的新功能时,将 targetSdkVersion 更新至“O”,验证应用是否仍可按预期方式运行,然后开始使用新的 API。

确保平台兼容性

这一步的目标是确保应用在 Android 8.0 上可照常运行。由于一些平台变化可能影响应用的行为方式,因此可能需要进行一些调整,但您不需要使用新的 API 或更改 targetSdkVersion

准备一台运行 Android 8.0 的设备

  • 如果您有一台兼容设备(Pixel、Pixel XL、Pixel C、Nexus 5X、Nexus 6P 或 Nexus Player),请从下载页面获得适合您的设备的 Android 8.0 系统映像,然后按照说明将映像刷入设备
  • 或下载适用于 Android Emulator 的 Android 8.0 系统映像。它列于 SDK 管理器的 Android 8.0 Preview 下,显示为 Google APIs Intel x86 Atom System Image

    :Android 8.0 系统映像只能通过 Android Studio 3.0 Canary 下载。如需了解详细信息,请参阅下面一节以获取 Android 8.0 SDK

执行兼容性测试

与 Android 8.0 的兼容性测试多半与您准备发布应用时执行的测试属于同一类型。这时有必要回顾一下核心应用质量准则测试最佳做法

不过,测试还有另一个层面:Android 8.0 向 Android 平台引入了一些变化,即便不对 targetSdkVersion 做任何变动,仍可能影响应用的行为或令其根本无法运行。因此,您必须回顾表 1 中的关键变化,并对任何为适应这些变化而实现的修复进行测试。

表 1. 对运行在 Android 8.0 设备上的所有应用都有影响的关键变化。

变化摘要其他参考资料
后台位置更新频率下降如果应用接收来自后台服务的位置更新,则其在 Android 8.0 上接收更新的频率要比旧版本 Android 低。具体地讲,后台服务接收位置更新的频率不能超过每小时几次。不过,当应用位于前台时,位置更新频率不变。后台位置限制
不再支持 net.hostname查询 net.hostname 系统属性返回的结果为空。
send(DatagramPacket) 引发新异常如果之前执行的 connect(InetAddress, int) 方法失败,send(DatagramPacket) 方法会引发 SocketException行为变更:网络连接和 HTTP(S) 连接
AbstractCollection 方法引发正常的 NullPointerException现在,AbstractCollection.removeAll(null) 和 AbstractCollection.retainAll(null) 始终引发 NullPointerException;之前,当集合为空时不会引发 NullPointerException。此项变更使行为符合文档要求。行为变更:集合的处理
Currency.getDisplayName(null)引发正常的 NullPointerException调用 Currency.getDisplayName(null) 会引发 NullPointerException行为变更:语言区域和国际化

如需查看更详尽的 Android 8.0 行为变更列表,另请参阅 Android 8.0 行为变更

构建具有 Android 8.0 功能的应用

如表 2 所述,除了提供新的 API 外,Android 8.0 还会在您更新 targetSdkVersion 时引发其他行为变更。本节说明如何将开发环境设置为以新平台为目标,以及如何着手构建和测试 Android 8.0 API 带来的变化和新功能。

:上述旨在确保平台兼容性的步骤是面向 Android 8.0 构建应用的先决条件,因此请您务必先完成这些步骤。

获取 Android 8.0 SDK

  1. 安装 Android Studio 3.0 Canary

    只有 Android Studio 3.0 包含对 Android 8.0 提供的所有新开发者功能的支持。因此您需要获得 Android Studio 3.0 Canary 版本,以便开始使用 Android 8.0 SDK。但您仍可保留已安装的 Android Studio 稳定版。

  2. 启动 Android Studio 3.0,然后点击 Tools > Android > SDK Manager 打开 SDK 管理器。
  3. 在 SDK Platforms 标签中,选中 Show Package Details。在 Android 8.0 Preview 下选中下列项:
    • Android SDK Platform O
    • Google APIs Intel x86 Atom System Image(只需在使用模拟器时选中)
  4. 切换到 SDK Tools 标签,选中所有已提供更新的项(点击每个显示破折号  的复选框)。这应该包括下列必需项:
    • Android SDK Build-Tools 26.0.0(rc2 或更高版本)
    • Android SDK Platform-Tools 26.0.0(rc2 或更高版本)
    • Android Emulator 26.0.0
    • Support Repository
  5. 点击 OK 安装所有选定的 SDK 软件包。

现在您就可以开始使用 Android 8.0 进行开发了。

更新构建配置

将 compileSdkVersionbuildToolsVersiontargetSdkVersion 和 Support Library 版本更新为下列版本:

android {
  compileSdkVersion 'android-O'
  buildToolsVersion '26.0.0-rc2'

  defaultConfig {
    targetSdkVersion 'O'
  }
  ...
}

dependencies {
  compile 'com.android.support:appcompat-v7:26.0.0-beta1'
}

// REQUIRED: Google's new Maven repo is required for the latest
// support library that is compatible with Android 8.0
repositories {
    maven {
        url 'https://maven.google.com'
    }
}

 

您不能在此配置下发布应用。“O”版本是一个临时 API 级别,只能用于 Android 8.0 期间的开发和测试。您必须等到最终 API 级别发布时再发布 Android 8.0 变更,届时再次更新配置。

从清单文件中移除广播接收器

由于 Android 8.0 引入了新的广播接收器限制,因此您应该移除所有为隐式广播 Intent 注册的广播接收器。将它们留在原位并不会在构建时或运行时令应用失效,但当应用运行在 Android 8.0 上时它们不起任何作用。

显式广播 Intent(只有您的应用可以响应的 Intent)在 Android 8.0 上仍以相同方式工作。

这个新增限制有一些例外情况。如需查看在以 Android 8.0 为目标平台的应用中仍然有效的隐式广播的列表,请参阅隐式广播例外

测试 Android 8.0 应用

完成以上准备工作后,您就可以构建应用,然后对其做进一步测试,以确保 Android 8.0 为目标平台时它能正常工作。这时有必要回顾一下核心应用质量准则测试最佳做法

如果您构建应用时设置了适用于 Android 8.0 的 targetSdkVersion,应该注意特定的平台变化。即便您不实现 Android 8.0 中的新功能,其中的一些变化仍可能严重影响应用的行为或令其根本无法运行。

表 2 列出了这些变化以及可获得更多信息的链接。

表 2. targetSdkVersion 设置为“O”时影响应用的关键变化。

变化摘要其他参考资料
隐私性Android 8.0 不支持使用 net.dns1、net.dns2、net.dns3 或 net.dns4 系统属性。行为变更:隐私性
实行了可写且可执行的代码段对于原生库,Android 8.0 实行的规则是:数据不应可执行,代码不应可写。行为变更:原生库
ELF 标头和节验证动态链接器对 ELF 标头和节头中的更多值进行检查,如果值无效则失败。行为变更:原生库
通知以 SDK 的 Android 8.0 版本为目标平台的应用必须实现一个或多个通知渠道,以便向用户发布通知。API 概览:通知
List.sort() 方法该方法的实现不得再调用 Collections.sort(),否则应用将因堆栈溢出而引发异常。行为变更:集合的处理
Collections.sort()方法在列表实现中,Collections.sort() 现在会引发 ConcurrentModificationException行为变更:集合的处理

如需查看更详尽的 Android 8.0 行为变更列表,请参阅 Android 8.0 行为变更

要想探究 Android 8.0 提供的新功能和新 API,请参阅 Android 8.0 功能和 API

 

Code Samples

Use the code samples below to learn about Android 8.0 (API level 26) capabilities and APIs. To download the samples in Android Studio, select the File > New > Import Samples menu option.

Note: These downloadable projects are designed for use with Gradle and Android Studio.

Notification Channels

NotificationChannels Sample - Android 8.0 (API level 26) adds support for notification channels, apps to organize their notifications into topical categories. Each category can have its own alert style, and users are able to selectively enable or disable categories to match their interests. This sample demonstrates how to create channels and appropriately annotate notifications.

Get it on GitHub: Java | Kotlin

Autofill Framework

AutofillFramework Sample - This sample demonstrates the use of the Autofill Framework introduced in Android 8.0 (API level 26). It includes implementations of client Activities that want to be autofilled, and a Service that can provide autofill data to those Activities.

Get it on GitHub: Java | Kotlin

Picture-in-Picture Mode

PictureInPicture Sample - This sample demonstrates basic usage of Picture-in-Picture mode for handheld devices. The sample plays a video. The video keeps on playing when the app is turned into Picture-in-Picture mode. On Picture-in-Picture screen, the app shows an action item to pause or resume the video.

Get it on GitHub: Java | Kotlin

Downloadable Fonts

DownloadableFonts - This sample demonstrates how to use the Downloadable Fonts feature introduced in Android 8.0 (API level 26). Downloadable Fonts allows apps to request a certain font from a provider, instead of bundling it or downloading it themselves. This means there is no need to independently bundle the font as an asset.

Get it on GitHub: Java | Kotlin

EmojiCompat - This sample demonstrates usage of the Emoji Compatibility Support Library. You can use this library to prevent your app from showing missing emoji characters in the form of tofu (□). You can use either bundled or downloadable emoji fonts. This sample shows both usages.

Get it on GitHub: Java | Kotlin

Background Execution Limits

Bluetooth Advertisements Sample - The Bluetooth Advertisements sample was updated to comply with Android 8.0 (API level 26)'s background execution limits. The sample previously created a background service which was used to broadcast Bluetooth LE Advertisements; this process is now started as a foreground service to ensure execution.

Get it on GitHub: Java

Background Location Restrictions

LocationUpdatesPendingIntent Sample - Shows how to request location updates using a PendingIntent. For apps targeting Android 7.x (API levels 24-25) but running on Android 8.0 (API level 26), developers can use eitherPendingIntent.getService() or PendingIntent.getBroadcast(). For apps targeting Android 8.0,PendingIntent.getService() does not work due to the limits placed on services started in the background. When targeting Android 8.0, developers should use PendingIntent.getBroadcast().

Get it on GitHub: Java

LocationUpdatesForegroundService Sample - Shows how to use a foreground service to get location updates when the app activities are not visible. For apps running on Android 8.0 (API level 26), background updates are limited to only a few times per hour. Using a foreground service is a way to receive more frequent updates.

Get it on GitHub: Java

AAudio

AAudio Echo Sample - AAudio is a new NDK API that gives Pro Audio apps access to low-latency audio on supported devices. This sample demonstrates how to create an input and output stream, configured for loopback.

Get it on GitHub: C++

 

后台执行限制

每次在后台运行时,应用都会消耗一部分有限的设备资源,例如 RAM。 这可能会影响用户体验,如果用户正在使用占用大量资源的应用(例如玩游戏或观看视频),影响会尤为明显。 为了提升用户体验,Android 8.0(API 级别 26)对应用在后台运行时可以执行的操作施加了限制。 本文档将介绍操作系统的一些变更,以及如何更新应用以使其能够在新限制下正常运行。

概览

多个 Android 应用和 Service 可以同时运行。 例如,用户可以在一个窗口中玩游戏,同时在另一个窗口中浏览网页,并使用第三个应用播放音乐。 同时运行的应用越多,对系统造成的负担越大。 如果还有应用或 Service 在后台运行,则会对系统造成更大负担,进而可能导致用户体验下降;例如,音乐应用可能会突然关闭。

为了降低发生这些问题的几率,Android 8.0 对应用在用户不与其直接交互时可以执行的操作施加了限制。 应用在两个方面受到限制:

  • 后台 Service 限制:处于空闲状态时,应用可以使用的后台 Service 存在限制。 这些限制不适用于前台 Service,因为前台 Service 更容易引起用户注意。

  • 广播限制:除了有限的例外情况,应用无法使用清单注册隐式广播。 它们仍然可以在运行时注册这些广播,并且可以使用清单注册专门针对它们的显式广播。

请注意:默认情况下,这些限制仅适用于适配 Android 8.0(API 级别 26)或更高版本的应用。 然而,即使应用适配的 API 级别低于 26,用户也可以从 Settings 屏幕为任意应用启用其中大多数限制。

在大多数情况下,应用都可以使用 JobScheduler 作业克服这些限制。 这种方法允许应用安排其在未活跃运行时执行工作,不过仍能够使系统可以在不影响用户体验的情况下安排这些作业。 Android 8.0 提供针对 JobScheduler 的多项改进,让您可以更轻松地使用计划作业取代 Service 和广播接收器;如需了解详细信息,请参阅 JobScheduler 改进

后台 Service 限制

在后台中运行的 Service 会消耗设备资源,这可能会降低用户体验。 为了缓解这一问题,系统对这些 Service 施加了一些限制。

系统可以区分前台后台应用。 (用于 Service 限制目的的后台定义与内存管理使用的定义不同;一个应用按照内存管理的定义可能处于后台,但按照能够启动 Service 的定义又处于前台。)如果满足以下任意条件,应用将被视为处于前台:

  • 具有可见 Activity(不管该 Activity 已启动还是已暂停)。
  • 具有前台 Service。
  • 另一个前台应用已关联到该应用(不管是通过绑定到其中一个 Service,还是通过使用其中一个内容提供程序)。 例如,如果另一个应用绑定到该应用的 Service,那么该应用处于前台:
    • IME
    • 壁纸 Service
    • 通知侦听器
    • 语音或文本 Service

如果以上条件均不满足,应用将被视为处于后台。

请注意:这些应用不会对绑定 Service 产生任何影响。 如果您的应用定义了绑定 Service,则不管应用是否处于前台,其他组件都可以绑定到该 Service。

处于前台时,应用可以自由创建和运行前台与后台 Service。 进入后台时,在一个持续数分钟的时间窗内,应用仍可以创建和使用 Service。 在该时间窗结束后,应用将被视为处于空闲状态。 此时,系统将停止应用的后台 Service,就像应用已经调用 Service 的 Service.stopSelf() 方法一样。

在这些情况下,后台应用将被置于一个临时白名单中并持续数分钟。 位于白名单中时,应用可以无限制地启动 Service,并且其后台 Service 也可以运行。 处理对用户可见的任务时,应用将被置于白名单中,例如:

请注意: IntentService 是一项 Service,因此其遵守针对后台 Service 的新限制。 因此,许多依赖 IntentService 的应用在适配 Android 8.0 或更高版本时无法正常工作。 出于这一原因,Android 支持库 26.0.0 引入了一个新的JobIntentService类,该类提供与 IntentService 相同的功能,但在 Android 8.0 或更高版本上运行时使用作业而非 Service。

在很多情况下,您的应用都可以使用 JobScheduler 作业替换后台 Service。 例如,CoolPhotoApp 需要检查用户是否已经收到好友共享的照片,即使该应用未在前台运行也需如此。 之前,应用使用一种会检查其云存储的后台 Service。 为了迁移到 Android 8.0(API 级别 26),开发者使用一个计划作业替换了这种后台 Service,该作业将按一定周期启动,查询服务器,然后退出。

在 Android 8.0 之前,创建前台 Service 的方式通常是先创建一个后台 Service,然后将该 Service 推到前台。 Android 8.0 有一项复杂功能:系统不允许后台应用创建后台 Service。 因此,Android 8.0 引入了一种全新的方法,即 startForegroundService(),以在前台启动新 Service。 在系统创建 Service 后,应用有五秒的时间来调用该 Service 的 startForeground() 方法以显示新 Service 的用户可见通知。 如果应用在此时间限制内调用startForeground(),则系统将停止此 Service 并声明此应用为 ANR

广播限制

如果应用注册为接收广播,则在每次发送广播时,应用的接收器都会消耗资源。 如果多个应用注册为接收基于系统事件的广播,则会引发问题:触发广播的系统事件会导致所有应用快速地连续消耗资源,从而降低用户体验。 为了缓解这一问题,Android 7.0(API 级别 24)对广播施加了一些限制,如后台优化中所述。 Android 8.0(API 级别 26)让这些限制更为严格。

  • 适配 Android 8.0 或更高版本的应用无法继续在其清单中为隐式广播注册广播接收器。 隐式广播是一种不专门针对该应用的广播。 例如,ACTION_PACKAGE_REPLACED 就是一种隐式广播,因为该广播将被发送给所有已注册侦听器,让后者知道设备上的某些软件包已被替换。 不过,ACTION_MY_PACKAGE_REPLACED 不是隐式广播,因为不管已为该广播注册侦听器的其他应用有多少,它都会只被发送给软件包已被替换的应用。
  • 应用可以继续在它们的清单中注册显式广播。
  • 应用可以在运行时使用 Context.registerReceiver() 为任意广播(不管是隐式还是显式)注册接收器。
  • 需要签名权限的广播不受此限制所限,因为这些广播只会发送到使用相同证书签名的应用,而不是发送到设备上的所有应用。

在许多情况下,之前注册隐式广播的应用使用 JobScheduler 作业可以获得类似的功能。 例如,一款社交照片应用可能需要不时地执行数据清理,并且倾向于在设备连接到充电器时执行此操作。 之前,应用已经在清单中为ACTION_POWER_CONNECTED 注册了一个接收器;当应用接收到该广播时,它会检查清理是否必要。 为了迁移到 Android 8.0 或更高版本,应用将该接收器从其清单中移除。 应用将清理作业安排在设备处于空闲状态和充电时运行。

请注意:很多隐式广播当前已不受此限制所限。 应用可以继续在其清单中为这些广播注册接收器,不管应用适配哪个 API 级别。 有关已豁免广播的列表,请参阅隐式广播例外。 {: .note}

迁移指南

默认情况下,这些变更仅影响适配 Android 8.0(API 级别 26)或更高版本的应用。 不过,即使应用适配的 API 级别低于 26 的,用户也可以从 Settings 屏幕中启用这些限制。您可能需要更新应用,使其符合新限制。

了解您的应用如何使用 Service。 如果您的应用依赖某些在它处于空闲时于后台运行的 Service,则您需要替换这些 Service。 可能的解决方法包括:

  • 如果处于后台时您的应用需要创建一个前台 Service,请使用 startForegroundService() 方法,而非 startService()
  • 如果 Service 容易引起用户注意,请将其设置为前台 Service。 例如,播放音频的 Service 始终应为前台 Service。 使用 startForegroundService() 方法创建 Service, 而非 startService()
  • 寻找一种使用计划作业实现 Service 功能的方式。 如果 Service 未在执行容易立即引起用户注意的操作,一般情况下,您都能够使用计划作业。
  • 发生网络事件时,请使用 FCM 选择性地唤醒您的应用,而不是在后台轮询。
  • 在应用正常处于前台之前,请推迟后台工作。

检查在您应用的清单中定义的广播接收器。 如果您的清单为显式广播声明了接收器,您必须予以替换。 可能的解决方法包括:

  • 通过调用 Context.registerReceiver() 而不是在清单中声明接收器的方式在运行时创建接收器。
  • 使用计划作业检查条件是否会触发隐式广播。

后台位置限制

为降低功耗,无论应用的目标 SDK 版本为何,Android 8.0 都会对后台应用检索用户当前位置的频率进行限制。

如果您的应用在后台运行时依赖实时提醒或运动检测,这一位置检索行为就显得特别重要,必须紧记。

重要说明:作为起点,我们只允许后台应用每小时接收几次位置更新。我们将在整个预览版阶段继续根据系统影响和开发者的反馈优化位置更新间隔。

系统会对前台应用和后台应用进行区分。应用满足以下任一条件即视为前台应用:

  • 它具有可见的 Activity,无论 Activity 处于启动还是暂停状态。
  • 它具有前台服务。
  • 另一个前台应用通过绑定到应用的其中一个服务或使用应用的其中一个内容提供程序与应用相连。

如果以上所有条件均不满足,应用即视为后台应用。

前台应用行为得到保留

如果应用在运行 Android 8.0 的设备上处于前台,其位置更新行为将与 Android 7.1.1(API 级别 25)及更低版本上相同。

警告:如果您的应用长时间进行近乎实时的位置更新检索,将大幅度缩短设备的电池寿命。

优化应用的位置行为

考虑在您的应用接收位置更新不频繁的情况下其后台运行用例是否根本无法成功。如果属于这种情况,您可以通过执行下列操作之一提高位置更新的检索频率:

  • 将您的应用转至前台。
  • 使用应用中的某个前台服务。激活此服务时,您的应用必须在通知区显示一个持续性的通知。
  • 使用 Geofencing API 的元素(例如 GeofencingApi 接口),这些元素针对最大限度减少耗电进行了专门优化。
  • 使用被动位置侦听器,它可以在后台应用加快位置请求频率时提高位置更新的接收频率。

:如果您的应用需要访问的位置历史记录包含时间频繁更新,请使用批处理版本的 Fused Location Provider API 元素,例如 FusedLocationProviderApi 接口。当您的应用运行于后台时,此 API 会以高于非批处理版本 API 的频率接收用户的位置。但切记,您的应用批量接收更新的频率仍仅为每小时几次。

受影响的 API

对后台应用位置检索行为的更改影响下列 API:

Fused Location Provider (FLP)

  • 如果您的应用运行在后台,位置系统服务只会根据 Android 8.0 行为变更中定义的间隔,按每小时几次的频率为其计算新位置。即使您的应用请求进行更频繁的位置更新,也仍是如此。
  • 如果您的应用运行在前台,与 Android 7.1.1(API 级别 25)相比,在位置采样率上不会有任何变化。

Geofencing

  • 后台应用可以高于接收 Fused Location Provider 更新的频率接收地理围栏转换事件。
  • 地理围栏事件的平均响应时间是大约每两分钟一次。

GNSS Measurements 和 GNSS Navigation Messages

Location Manager

  • 提供给后台应用的位置更新只会根据 Android 8.0 行为变更中定义的间隔,按每小时几次的频率提供。

     

    :如果运行您的应用的设备安装了 Google Play 服务,强烈建议您改用 Fused Location Provider (FLP)

WLAN 管理器

startScan() 方法对后台应用执行完整扫描的频率仅为每小时数次。如果不久之后后台应用再次调用此方法, WifiManager 类将提供上次扫描所缓存的结果。

Android 8.1 Features and APIs

Android 8.1 (API level 27) introduces a variety of new features and capabilities for users and developers. This document highlights what's new for developers.

Android Oreo (Go edition)

Android Go is our initiative to optimize the Android experience for billions of people coming online around the world. Starting with Android 8.1, we’re making Android a great platform for entry-level devices. Features in the Android Oreo (Go edition) configuration include:

  • Memory optimizations. Improved memory usage across the platform to ensure that apps can run efficiently on devices with 1GB or less RAM.
  • Flexible targeting options. New hardware feature constants to let you target the distribution of your apps to normal or low-RAM devices through Google Play.
  • Google Play.While all apps will be available on devices running Android Oreo (Go edition), Google Play will give visibility to apps specifically optimized by developers to provide a great experience for billions of people with the building for billions guidelines.

We’ve updated the building for billions guidelines with additional guidance on how to optimize your app for devices running Android Oreo (Go edition). For most developers, optimizing your existing APK or using Google Play’s Multiple APK feature to target a version of your APK to low-RAM devices is the best way to prepare for devices running Android Oreo (Go edition). Remember that making your app lighter and more efficient benefits your whole audience, regardless of device.

Neural Networks API

The Neural Networks API provides accelerated computation and inference for on-device machine learning frameworks like TensorFlow Lite—Google's cross-platform ML library for mobile— as well as Caffe2 and others. Visit the TensorFlow Lite open source repo for downloads and docs. TensorFlow Lite works with the Neural Networks API to run models likeMobileNetsInception v3, and Smart Reply efficiently on your mobile device.

Autofill framework updates

Android 8.1 (API level 27) provides several improvements to the Autofill Framework that you can incorporate into your apps.

The BaseAdapter class now includes the setAutofillOptions() method, which allows you to provide string representations of the values in an adapter. This is useful for spinner controls that dynamically generate the values in their adapters. For example, you can use the setAutofillOptions() method to provide a string representation of the list of years that the users can choose as part of a credit card expiration date. Autofill services can use the string representation to appropriately fill out the views that require the data.

Additionally, the AutofillManager class includes the notifyViewVisibilityChanged(View, int, boolean)method that you can call to notify the framework about changes in the visibility of a view in a virtual structure. There's also an overload of the method for non virtual structures. However, non virtual structures usually don't require you to explicitly notify the framework because the method is already called by the View class.

Android 8.1 also gives Autofill Services more ability to customize the save UI affordance by adding support for CustomDescription and Validator within SaveInfo.

Custom descriptions are useful to help the autofill service clarify what is being saved; for example, when the screen contains a credit card, it could display a logo of the credit card bank, the last four digits of the credit card number, and its expiration number. To learn more, see the CustomDescription class.

Validator objects are used to avoid displaying the autofill save UI when the Validator condition isn't satisfied. To learn more, see the Validator class along with its subclasses, LuhnChecksumValidator and RegexValidator.

Notifications

Android 8.1 includes the following changes to notifications:

  • Apps can now only make a notification alert sound once per second. Alert sounds that exceed this rate aren't queued and are lost. This change doesn't affect other aspects of notification behavior and notification messages still post as expected.
  • NotificationListenerService and ConditionProviderService are not supported on low-RAM Android-powered devices that return true when ActivityManager.isLowRamDevice() is called.

EditText update

Beginning with API level 27, the EditText.getText() method returns an Editable; previously it returned a CharSequence. This change is backward-compatible, as Editable implements CharSequence.

The Editable interface provides valuable additional functionality. For example, because Editable also implements the Spannable interface, you can apply markup to content within an instance of EditText.

Programmatic Safe Browsing actions

By using the WebView implementation of the Safe Browsing API, your app can detect when an instance of WebViewattempts to navigate to a URL that Google has classified as a known threat. By default, the WebView shows an interstitial that warns users of the known threat. This screen gives users the option to load the URL anyway or return to a previous page that's safe.

In Android 8.1, you can define programmatically how your app responds to a known threat:

  • You can control whether your app reports known threats to Safe Browsing.
  • You can have your app automatically perform a particular action—such as going back to safety—each time it encounters a URL that Safe Browsing classifies as a known threat.

Note: For optimal protection against known threats, wait until you've initialized Safe Browsing before you invoke a WebViewobject's loadUrl() method.

The following code snippets show how you can instruct your app's instances of WebView to always go back to safety after encountering a known threat:

AndroidManifest.xml

<manifest>
    <application>
        ...
        <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
                   android:value="true" />
    </application>
</manifest>

 

MyWebActivity.java

KOTLINJAVA

private WebView superSafeWebView;
private boolean safeBrowsingIsInitialized;

// ...

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    superSafeWebView = new WebView(this);
    superSafeWebView.setWebViewClient(new MyWebViewClient());
    safeBrowsingIsInitialized = false;

    superSafeWebView.startSafeBrowsing(this, new ValueCallback<Boolean>() {
        @Override
        public void onReceiveValue(Boolean success) {
            safeBrowsingIsInitialized = true;
            if (!success) {
                Log.e("MY_APP_TAG", "Unable to initialize Safe Browsing!");
            }
        }
    });
}

 

MyWebViewClient.java

KOTLINJAVA

public class MyWebViewClient extends WebViewClient {
    // Automatically go "back to safety" when attempting to load a website that
    // Safe Browsing has identified as a known threat. An instance of WebView
    // calls this method only after Safe Browsing is initialized, so there's no
    // conditional logic needed here.
    @Override
    public void onSafeBrowsingHit(WebView view, WebResourceRequest request,
            int threatType, SafeBrowsingResponse callback) {
        // The "true" argument indicates that your app reports incidents like
        // this one to Safe Browsing.
        callback.backToSafety(true);
        Toast.makeText(view.getContext(), "Unsafe web page blocked.",
                Toast.LENGTH_LONG).show();
    }
}

 

Video thumbnail extractor

The MediaMetadataRetriever class has a new method, getScaledFrameAtTime(), that finds a frame near a given time position and returns a bitmap with the same aspect ratio as the source frame, but scaled to fit into a rectangle of given width and height. This is useful for generating thumbnail images from video.

We recommend using this method rather than getFrameAtTime() which can waste memory because it returns a bitmap with the same resolution as the source video. For example, a frame from a 4K video would be a 16MB bitmap, far larger than you would need for a thumbnail image.

Shared memory API

Android 8.1 (API level 27) introduces a new SharedMemory API. This class allows you to create, map, and manage an anonymous SharedMemory instance. You set the memory protection on a SharedMemory object for reading and/or writing, and, since the SharedMemory object is Parcelable, you can easily pass it to another process through AIDL.

The SharedMemory API interoperates with the ASharedMemory facility in the NDK. ASharedMemory gives access to a file descriptor, which can then be mapped to read and write. It's a great way to share large amounts of data between apps or between multiple processes within a single app.

WallpaperColors API

Android 8.1 (API level 27) allows your live wallpaper to provide color information to the system UI. You do this by creating a WallpaperColors object from a bitmap, a drawable, or by using three manually-selected colors. You can also retrieve this color information.

To create a WallpaperColors object, do either of the following:

  • To create a WallpaperColors object by using three colors, create an instance of the WallpaperColors class by passing the primary, the secondary, and the tertiary color. The primary color must not be null.
  • To create a WallpaperColors object from a bitmap, call the fromBitmap() method by passing the bitmap source as parameter.
  • To create a WallpaperColors object from a drawable, call the fromDrawable() method by passing the drawable source as parameter.

To retrieve the primary, secondary, or tertiary color details from the wallpaper, call the following methods:

To notify the system about any significant color changes in your live wallpaper, call the notifyColorsChanged()method. This method triggers an onComputeColors() lifecycle event where you have an opportunity to provide a new WallpaperColors object.

To add a listener for color changes, you can call the addOnColorsChangedListener() method. You can also call the getWallpaperColors() method to retrieve the primary colors of a wallpaper.

Fingerprint updates

The FingerprintManager class has introduced the following error codes:

  • FINGERPRINT_ERROR_LOCKOUT_PERMANENT – The user has tried too many times to unlock their device using the fingerprint reader.
  • FINGERPRINT_ERROR_VENDOR – A vendor-specific fingerprint reader error occurred.

Cryptography updates

A number of cryptography changes have been made with Android 8.1:

  • New algorithms have been implemented in Conscrypt. The Conscrypt implementation is preferentially used over the existing Bouncy Castle implementation. New algorithms include:
    • AlgorithmParameters:GCM
    • KeyGenerator:AES
    • KeyGenerator:DESEDE
    • KeyGenerator:HMACMD5
    • KeyGenerator:HMACSHA1
    • KeyGenerator:HMACSHA224
    • KeyGenerator:HMACSHA256
    • KeyGenerator:HMACSHA384
    • KeyGenerator:HMACSHA512
    • SecretKeyFactory:DESEDE
    • Signature:NONEWITHECDSA
  • Cipher.getParameters().getParameterSpec(IvParameterSpec.class) no longer works for algorithms that use GCM. Instead, use getParameterSpec(GCMParameterSpec.class).
  • Many internal Conscrypt classes associated with TLS were refactored. Since developers sometimes access these reflectively, shims have been left in place to support previous usage, but some details have changed. For example, sockets previously were of type OpenSSLSocketImpl, but now they’re of type ConscryptFileDescriptorSocketor ConscryptEngineSocket, both of which extend OpenSSLSocketImpl.
  • SSLSession methods used to throw IllegalArgumentException when passed a null reference, they now throw NullPointerException.
  • The RSA KeyFactory no longer allows generation of keys from byte arrays that are larger than the encoded key. Calls to generatePrivate() and generatePublic() that provide a KeySpec where the key structure does not fill the entire buffer will result in an InvalidKeySpecException.
  • When a socket read is interrupted by the socket being closed, Conscrypt used to return -1 from the read. The read now throws SocketException.
  • The set of root CA certificates has been changed, mostly removing a large number of obsolete certificates, but also removing the root certificates for WoSign and StartCom. For more information on this decision, see the Google Security Blog post, Final removal of trust in WoSign and StartCom Certificates.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值