Android 8.0 Oreo介绍

Android Oreo

Android 8.0 功能和 API

(1)、用户体验

通知

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

1、通知渠道:Android 8.0引入了通知渠道,其允许您为要显示的每种通知类型创建用户可自定义的渠道。用户界面将通知渠道称之为通知类别。

2、通知标志:Android 8.0引入了对在应用启动器图标上显示通知标志的支持。通知标志可反映某个应用是否存在与其关联、并且用户尚未予以清除也未对其采取行动的通知。通知标志也称为通知点。

3、休眠:用户可以通知置于休眠状态,以便稍后重新显示它。重新显示时通知的重要程度与首次显示时相同,用用可以移除或更新已休眠的通知,但更新休眠的通知并不会使其重新显示。

4、通知超时,现在,使用setTimeoutAfter()创建通知时您可以设置超时。您可以使用此函数指定一个持续时间,超过该持续时间后,通知应取消。如果需要,您可以再指定的超时持续时间之前取消通知。

5、通知设置:当您使用Notification.INTENT_CATEFORY_NOTIFICATION_PREFERENCES Intent从通知创建指向应用通知设置的链接时,您可以调用setSettingsText()来设置要显示的文本。此系统可以提供以下Extra数据和Intent,用于过滤应用必须向用户显示的设置:EXTRA_CHANNEL_IDNOTIFICATION_TAGNOTIFICATION_ID.

6、通知清除:系统现在可区分通知是由用户清楚,还是由应用移除。要查看清除通知的方式,您应实现NotificationListenerService类的新的onNotificationRemoved()函数。

7、背景颜色:您现在可以设置和启用通知的背景颜色。只能在用户必须一眼就能看到的持续任务的通知中使用此功能。例如,您可以为与驾车路线或正在进行的通话有关的通知设置背景颜色。您还可以使用Notification.Builder.setColor()设置所需的背景颜色。这样做将允许您使用Notification.Builder.setColorized()启用通知的背景颜色设置。

8、消息样式:现在,使用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变更:Android8.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 中的字体,允许您使用字体作为资源。这意味着,不再需要以资产的形式捆绑字体。字体在 文件中编译,并且作为一种资源,可自动用于系统。然后,您可以利用一种新的资源类型 font 来访问这些字体。

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

 

 

 

自动调整 TextView 的大小

Android 8.0 允许您根据 TextView 的大小自动设置文本展开或收缩的大小。这意味着,在不同屏幕上优化文本大小或者优化包含动态内容的文本大小比以往简单多了

自适应图标

Android 8.0 引入自适应启动器图标。自适应图标支持视觉效果,可在不同设备型号上显示为各种不同的形状。

颜色管理

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

WebView API

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

● Version API

● Google SafeBrowsing API

● Termination Handle API

● Renderer Importance API

固定快捷方式和小部件

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

最大屏幕纵横比

Android 7.1API 级别 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 的进程,并以新的尺寸重新创建它。

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

setLaunchDisplayId()

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

getLaunchDisplayId()

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

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

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

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

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

layout_marginVertical,同时定义 layout_marginTop  layout_marginBottom

layout_marginHorizontal,同时定义 layout_marginLeft  layout_marginRight

paddingVertical,同时定义 paddingTop  paddingBottom

paddingHorizontal,同时定义 paddingLeft  paddingRight

注:如果您自定义应用逻辑以支持不同语言和文化(包括文本方向),请记住,这些属性不会影响 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)、系统

新的 StrictMode 检测程序

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

detectUnbufferedIo() 将检测您的应用何时读取或写入未缓冲的数据,这可能极大影响性能。

detectContentUriWithoutPermission() 将检测您的应用在其外部启动 Activity 时何时意外忘记向其他应用授予权限。

detectUntaggedSockets() 将检测您的应用何时使用网络流量,而不使用 setThreadStatsTag(int) 将流量标记用于调试目的。

缓存数据

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

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

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

StorageManager.setCacheBehaviorAtomic() 可用于指示某个目录及其所有内容应作为一个不可分割的整体进行删除。

setCacheBehaviorTombstone(File, boolean) 可用于指示不应删除某个目录内的文件,而应将它们截断到 0 字节长度,使空文件保持完好。

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

内容提供程序分页

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

内容刷新请求

现在,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())将需要更新其返回类型。

(3)、媒体增强功能

VolumeShaper

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

音频焦点增强功能

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

媒体指标

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

MediaPlayer.getMetrics()

MediaRecorder.getMetrics()

MediaCodec.getMetrics()

MediaExtractor.getMetrics()

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

MediaPlayer

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

搜索帧时进行精细控制。

播放受数字版权管理保护的材料的功能。

MediaPlayer 现在支持采样级加密

音频录制器

音频录制器现在支持对流式传输有用的 MPEG2_TS 格式:

mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS);

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(),以便您的服务了解它何时可以开始产生声音。

 

(4)、安全性与隐私

权限

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

ANSWER_PHONE_CALLS 允许您的应用通过编程方式接听呼入电话。要在您的应用中处理呼入电话,您可以使用 acceptRingingCall() 函数。

READ_PHONE_NUMBERS 权限允许您的应用读取设备中存储的电话号码。

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

(5)、运行时和工具

平台优化

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

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

更新的 Java 支持

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

● OpenJDK 8 中的 java.time

● OpenJDK 7 中的 java.nio.file  java.lang.invoke

.....

 

Android 8.0 行为变更

1)、针对所有 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 中的功能,但您必须在应用中手动将它们转换成应用快捷方式

提醒窗口

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

TYPE_PHONE

TYPE_PRIORITY_PHONE

TYPE_SYSTEM_ALERT

TYPE_SYSTEM_OVERLAY

TYPE_SYSTEM_ERROR

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

网页表单自动填充

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

WebSettings

getSaveFormData() 函数现在返回 false。之前,此函数返回 true。

● 调用 setSaveFormData() 不再有任何效果。

WebViewDatabase

● 调用 clearFormData() 不再有任何效果。

hasFormData() 函数现在返回 false。之前,当表单包含数据时,此函数返回 true。

网络连接和 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-agentproxy-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 包含以下与安全性有关的变更:

此平台不再支持 SSLv3

在与未正确实现 TLS 协议版本协商的服务器建立 HTTPS 连接时,HttpsURLConnection 不再尝试回退到之前的 TLS 协议版本并重试的权宜方法。

● Android 8.0 将使用安全计算 (SECCOMP) 过滤器来过滤所有应用。允许的系统调用列表仅限于通过 bionic 公开的系统调用。此外,还提供了其他几个后向兼容的系统调用,但我们不建议使用这些系统调用。

现在,您的应用的 WebView 对象将在多进程模式下运行。网页内容在独立的进程中处理,此进程与包含应用的进程相隔离,以提高安全性。

您无法再假定 APK 驻留在名称以-1-2结尾的目录中。应用应使用sourceDir获取此目录,而不能直接使用目录格式。

......

隐私性:

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

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

对于在 OTA 之前安装到某个版本 Android 8.0API 级别 26)的应用,除非在 OTA 后卸载并重新安装,否则 ANDROID_ID 的值将保持不变。要在 OTA 后在卸载期间保留值,开发者可以使用密钥/值备份关联旧值和新值。

对于安装在运行 Android 8.0 的设备上的应用,ANDROID_ID 的值现在将根据应用签署密钥和用户确定作用域。应用签署密钥、用户和设备的每个组合都具有唯一的 ANDROID_ID 值。因此,在相同设备上运行但具有不同签署密钥的应用将不会再看到相同的 Android ID(即使对于同一用户来说,也是如此)。

只要签署密钥相同(并且应用未在 OTA 之前安装到某个版本的 O),ANDROID_ID 的值在软件包卸载或重新安装时就不会发生变化。

即使系统更新导致软件包签署密钥发生变化,ANDROID_ID 的值也不会变化。

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

查询 net.hostname 系统属性返回的结果为空。

 

记录未捕获的异常:

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

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

集合的处理:

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

Android 企业版:

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

新增多种行为,帮助应用支持完全托管设备中的工作资料。

变更系统更新处理、应用验证和身份验证方式,以提高设备和系统的完整性。

改进用户在配置、通知、最近使用的应用屏幕和 Always on VPN 方面的体验。

......

2)、针对Android 8.0 的应用

提醒窗口:

使用 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 8.0 更改了 ContentResolver.notifyChange()  registerContentObserver(Uri, boolean, ContentObserver) 在针对 Android 8.0 的应用中的行为方式。

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

安全性:

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

权限:

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,则系统会立即授予该权限,而不会提示用户。

原生库:

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

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

集合的处理:

Android 8.0 中,Collections.sort() 是在 List.sort() 的基础上实现的。在 Android 7.xAPI 级别 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 的平台版本中,违反这些假设条件可能导致多次定义同一个类、由于类混淆造成堆损坏和其他不良影响。

posted @ 2017-10-20 15:52  JhonMr  阅读(2538)  评论(0编辑  收藏  举报