没有行动的梦想都是妄想。我们揣的不仅仅是自己的心情,还有生活分配的使命;我们怀的不仅仅是个人的喜好,还有人生分给的责任,好多的事,我们想做,没做,那是责任;好多的话,我们想说,没说,那是使命,路上的景很美,肩上的担很重,很想看风景,但更想看人生,于是,我们默默前行,漠然了那种种心情,淡然了那种种喜好。
Android 8.0 为用户和开发者引入多种新功能,提升了用户体验。
1 系统修改点
1.1 新的严格模式
Android 8.0 添加了三个新的 StrictMode 检测程序,帮助识别应用可能出现的错误:
detectUnbufferedIo()
将检测您的应用何时读取或写入未缓冲的数据,这可能极大影响性能。detectContentUriWithoutPermission()
将检测您的应用在其外部启动 Activity 时何时意外忘记向其他应用授予权限。detectUntaggedSockets()
将检测您的应用何时使用网络流量,而不使用setThreadStatsTag(int)
将流量标记用于调试目的
1.2 数据缓存
Android 8.0 优化了缓存数据的导航和行为。现在,每个应用均获得一定的磁盘空间配额,用于存储 getCacheQuotaBytes(UUID)
返回的缓存数据。
当系统需要释放磁盘空间时,将开始从超过配额最多的应用中删除缓存文件。因此,如果将您的缓存数据量始终保持低于配额的水平,则在必须清除系统中的某些文件时,您的缓存文件将能坚持到最后。系统在决定删除您的应用中的哪些缓存文件时,将首先考虑删除最旧的文件(由修改时间确定)。
您还可以针对每个目录启用两种新行为,以控制系统如何释放缓存数据:
StorageManager.setCacheBehaviorAtomic()
可用于指示某个目录及其所有内容应作为一个不可分割的整体进行删除。setCacheBehaviorTombstone(File, boolean)
可用于指示不应删除某个目录内的文件,而应将它们截断到 0 字节长度,使空文件保持完好。
最后,在需要为大文件分配磁盘空间时,可考虑使用新的 allocateBytes(FileDescriptor, long)
API,它将自动清除属于其他应用的缓存文件(根据需要),以满足您的请求。在确定设备是否有足够的磁盘空间保存您的新数据时,请调用 getAllocatableBytes(UUID)
而不要使用 getUsableSpace()
,因为前者会考虑系统要为您清除的任何缓存数据。
1.3内容提供程序分页
对已更新内容提供程序以支持加载大型数据集,每次加载一页。例如,一个具有大量图像的照片应用可查询要在页面中显示的数据的子集。内容提供程序返回的每个结果页面由一个 Cursor 对象表示。客户端和提供程序必须实现分页才能利用此功能。
如需了解有关内容提供程序变更的详细信息,请参阅 ContentProvider
和 ContentProviderClient
。
1.4内容刷新请求
Android8.0 中, ContentProvider
和 ContentResolver
类均包含 refresh()
函数,这样,客户端可以更轻松地知道所请求的信息是否为最新信息。
您可以扩展 ContentProvider
以添加自定义的内容刷新逻辑。请务必重写 refresh()
函数,以返回 true
,告知提供程序的客户端您已尝试自行刷新数据。
您的客户端应用可通过调用另一个函数(又称 refresh()
),显式请求已刷新的内容。在调用此函数时,传入待刷新数据的 URI。
1.5 JobScheduler 改进
Android 8.0 引入了对 JobScheduler
的多项改进。由于您通常可以使用计划作业替代现在受限的后台服务或隐式广播接收器,这些改进可以让您的应用更轻松地符合新的后台执行限制。
JobScheduler
的更新包括:
- 您现在可以将工作队列与计划作业关联。要将一个工作项添加到作业的队列中,请调用
JobScheduler.enqueue()
。当作业运行时,它可以将待定工作从队列中剥离并进行处理。这种功能可以处理之前需要启动后台服务(尤其是实现IntentService
的服务)的许多用例。 - 您现在可以通过调用
JobInfo.Builder.setClipData()
的方式将ClipData
与作业关联。利用此选项,您可以将 URI 权限授予与作业关联,类似于这些权限传递到Context.startService()
的方式。您也可以将 URI 权限授予用于工作队列上的 intent。 - 计划作业现在支持多个新的约束条件:
- 如果设备的可用存储空间非常低,作业将不会运行。
- 如果电池电量等于或低于临界阈值,作业将不会运行;临界阈值是指设备显示 Low battery warning 系统对话框的电量。
- 作业需要一个按流量计费的网络连接,比如大多数移动数据网络数据套餐。
JobInfo.isRequireStorageNotLow()
JobInfo.isRequireBatteryNotLow()
NETWORK_TYPE_METERED
1.6自定义数据存储
Android 8.0 允许您为首选项提供自定义数据存储,如果您的应用将首选项存储在云或本地数据库中,或者如果首选项特定于某个设备,此功能会非常有用。如需了解有关实现数据存储的详细信息,请参阅自定义数据存储。
findViewById() 签名变更
现在,findViewById()
函数的全部实例均返回 <T extends View> T
,而不是 View
。此变更会带来以下影响:
- 例如,如果
someMethod(View)
和someMethod(TextView)
均接受调用findViewById()
的结果,这可能导致现有代码的返回类型不确定。 - 在使用 Java 8 源语言时,这需要在返回类型不受限制时(例如,
assertNotNull(findViewById(...)).someViewMethod())
)显式转换为View
。 - 重写非最终的
findViewById()
函数(例如,Activity.findViewById()
)将需要更新其返回类型
2 媒体增强功能
2.1VolumeShaper
增加了一个新的 VolumeShaper 类。用它来执行简短的自动音量转换,例如淡入、淡出和交叉淡入淡出。
2.2音频焦点增强功能
音频应用通过请求和舍弃音频焦点的方式在设备上共享音频输出。应用通过启动或停止播放或者闪避音量的方式处理处于聚焦状态的变更。有一个新的 AudioFocusRequest
类。对于此类,应用在处理音频焦点变化时会使用新功能:自动闪避和延迟聚焦。
2.3媒体指标
新的 getMetrics()
函数将返回一个包含配置和性能信息的 PersistableBundle
对象,用一个包含属性和值的地图表示。为以下媒体类定义 getMetrics()
函数:
MediaPlayer.getMetrics()
MediaRecorder.getMetrics()
MediaCodec.getMetrics()
MediaExtractor.getMetrics()
为每个实例单独收集指标,并持续到实例的生命周期结束为止。如果没有可用的指标,则此函数将返回 null。返回的实际指标取决于类。
2.4MediaPlayer
Android 8.0 为 MediaPlayer 类添加了多种新函数。这些函数可以从多个方面增强您的应用处理媒体播放的能力:
- 在搜索帧时进行精细控制。
- 播放受数字版权管理保护的材料的功能。
MediaPlayer 现在支持采样级加密。
2.5音频录制器
- 音频录制器现在支持对流式传输有用的 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
中。
2.6音频播放控制
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()
,以便您的服务了解它何时可以开始产生声音。
2.7 增强的媒体文件访问功能
存储访问框架 (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()
的返回值将仅包含您的应用可以访问的文件夹和文档。
3 无线
3.1 WLAN 感知
Android 8.0 新增了对 WLAN 感知的支持,此技术基于周边感知联网 (NAN) 规范。在具有相应 WLAN 感知硬件的设备上,应用和附近设备可以通过 WLAN 进行搜索和通信,无需依赖互联网接入点。我们正在与硬件合作伙伴合作,以尽快将 WLAN 感知技术应用于设备。要了解有关如何将 WLAN 感知集成到您的应用中的信息,请参阅 WLAN 感知。
3.2 蓝牙
Android 8.0 通过增加以下功能,增强了平台对蓝牙的支持:
- 支持 AVRCP 1.4 标准,该标准支持音乐库浏览。
- 支持蓝牙低功耗 (BLE) 5.0 标准。
- 将 Sony LDAC 编解码器集成到蓝牙堆叠中。
3.3配套设备配对
在尝试通过蓝牙、BLE 和 WLAN 与配套设备配对时,Android 8.0 提供的 API 允许您自定义配对请求对话框。如需了解详细信息,请参阅配套设备配对。
如需了解有关在 Android 上使用蓝牙的详细信息,请参阅蓝牙指南。有关对蓝牙所作的特定于 Android 8.0 的变更,请参阅 Android 8.0 行为变更页面的蓝牙部分.
4 共享
4.1智能共享
Android 8.0 了解用户的个性化分享首选项,在通过哪些应用分享各个类型的内容方面,也有着更好的把握。例如,如果用户为一张收据拍照,Android 8.0 可以建议费用跟踪应用;如果用户自拍,一款社交媒体应用可以更好地处理图像。Android 8.0 可以根据用户的个性化首选项自动学习所有这些模式。
智能分享适用于 image
之外的内容类型,例如 audio
、video
、text
和 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 );
4.2智能文本选择
在兼容设备上,Android 8.0 让应用可以帮助用户以更有意义的方式与文本交互。当用户长按某个实体中可识别格式的单词(例如某个地址或餐馆名称)时,系统会选中整个实体。用户会看到一个浮动工具栏,该工具栏包含可以处理所选文本实体的应用。例如,如果系统识别出某个地址,它可以将用户导向地图应用。
系统识别的实体包括地址、网址、电话号码和电子邮件地址。如需了解详细信息,请参阅 TextClassifier
。
5 无障碍功能
Android 8.0 支持开发者使用以下无障碍功能创建自己的无障碍服务。如需了解有关如何让您的应用更便于访问的更多信息,请参阅无障碍功能。
5.1 无障碍功能按钮
您的无障碍服务现在可以请求在系统的导航区域显示无障碍功能按钮,该按钮让用户可从其设备上的任意位置快速激活您的服务功能。要执行此操作,请在某个 AccessibilityServiceInfo
对象的 android:accessibilityFlags
属性中添加 FLAG_REQUEST_ACCESSIBILITY_BUTTON
标志。稍后,您可以使用 registerAccessibilityButtonCallback()
注册回调。
注:此功能仅适用于提供软件渲染导航区域的设备。请始终使用 isAccessibilityButtonAvailable()
,并通过实现 onAvailabilityChanged()
根据无障碍功能按钮的可用性来响应变更。通过该方式,用户可以始终访问您的服务功能,即使该无障碍功能按钮不受支持或变得不可用。
5.2 独立的音量调整
Android 8.0 引入了 STREAM_ACCESSIBILITY
音量类别,允许您单独控制无障碍服务音频输出的音量,而不会影响设备上的其他声音。
要使用这个新的流类型来控制无障碍服务音量,请在无障碍服务中设置 FLAG_ENABLE_ACCESSIBILITY_VOLUME
选项。然后,您可以使用 adjustStreamVolume()
更改设备的无障碍服务音频音量。
5.3指纹手势
无障碍服务也可以响应替代的输入机制,即沿设备的指纹传感器按特定方向滑动(上、下、左和右)。要接收有关这些交互的回调,请完成以下一系列步骤:
- 声明
USE_FINGERPRINT
权限和CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES
功能。 - 在
android:accessibilityFlags
属性中设置FLAG_REQUEST_FINGERPRINT_GESTURES
标志。 - 使用
registerFingerprintGestureCallback()
注册回调。
请记住,并非所有设备都包含指纹传感器。您可以使用 isHardwareDetected()
函数识别设备是否支持此传感器。即使对于包含指纹传感器的设备,您的服务也只有在指纹传感器不用于身份验证目的时才可使用它。要识别此传感器何时可用,请调用 isGestureDetectionAvailable()
函数并实现 onGestureDetectionAvailabilityChanged()
回调。
5.4字词级突出显示
要确定 TextView
对象中可见字符的位置,您可以在 EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
中将其作为第一个参数传递到 refreshWithExtraData()
中。随后会更新您为 refreshWithExtraData()
提供的作为第二个参数的 Bundle
对象,使之包含一个可打包的 Rect
对象数组。每个 Rect
对象代表某个特定字符的边界框。
如果您的服务使用 TextToSpeech
对象朗读屏幕上出现的内容,您可以获取有关文本到语音转换引擎何时开始朗读单个合成字词时的准确时间信息,前提是文本到语音转换引擎提供此信息。当引擎即将开始播放特定范围文本的音频时,Text-to-Speech API 会通知您的服务,将使用 onRangeStart()
函数开始朗读此范围的文本。
如果您创建自己的 TextToSpeechService
实现,您可以使用 rangeStart()
函数支持这一新功能。
5.5标准化单端范围值
AccessibilityNodeInfo
的一些实例使用 AccessibilityNodeInfo.RangeInfo
的某个实例来表明界面元素可接受一定范围的值。使用 RangeInfo.obtain()
创建范围或使用 getMin()
和 getMax()
检索此范围的极值时,请注意,Android 8.0 规定了标准化单端范围:
- 对于没有最小值的范围,
Float.NEGATIVE_INFINITY
表示最小值。 - 对于没有最大值的范围,
Float.POSITIVE_INFINITY
表示最大值。
5.6提示文本
Android 8.0 包含可用于与文本可编辑对象的提示文本进行交互的多个函数:
isShowingHintText()
和setShowingHintText()
函数分别显示和设置节点的当前文本内容是否表示节点的提示文本。如果节点不包含可编辑文本,则它不应包含提示文本。- 要访问提示文本本身,请使用
getHintText()
。即使某个对象当前未显示提示文本,系统也能成功调用getHintText()
。
5.7连续的手势分派
您的服务现在可以使用 GestureDescription.StrokeDescription
构造函数中的最后一个参数 willContinue
,指定属于同一设定手势的笔划的顺序。
6 安全性与隐私
6.1权限
Android 8.0 引入了多个与电话有关的新权限:
ANSWER_PHONE_CALLS
允许您的应用通过编程方式接听呼入电话。要在您的应用中处理呼入电话,您可以使用acceptRingingCall()
函数。READ_PHONE_NUMBERS
权限允许您的应用读取设备中存储的电话号码。
6.2 新的帐号访问和 Discovery API
Android 8.0 对应用访问用户帐号的方式引入多项改进。对于由身份验证器管理的帐号,身份验证器在决定对应用隐藏帐号还是显示帐号时可以使用自己的策略。Android 系统跟踪可以访问特定帐号的应用。
在以前的 Android 版本中,想要跟踪用户帐号列表的应用必须获取有关所有帐号的更新,包括具有不相关类型的帐号。Android 8.0 添加了 addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean, java.lang.String[])
函数,其允许应用指定应接收帐号变更的帐号类型列表。
6.3 API 变更
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 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
参考。
6.4 Google Safe Browsing API
WebView
类现在添加了一个 Safe Browsing API 来增强网络浏览的安全性。如需了解详细信息,请参阅
Google Safe Browsing API
。