API 级别:19

Android 4.4 ([KITKAT](https://developer.android.google.cn/reference/android/os/Build.VERSION_CODES.html#KITKAT)) 是新的 Android 平台版本,为用户和应用开发者提供了新功能。本文旨在介绍其中最值得关注的新 API。

作为应用开发者,您应尽快从 SDK 管理器下载 Android 4.4 系统映像和 SDK 平台。如果您没有运行 Android 4.4 的设备来测试您的应用,请使用 Android 4.4 系统映像在 Android 模拟器上测试您的应用。然后,基于 Android 4.4 平台开发应用以开始使用最新的 API。

更新目标 API 级别

要进一步优化您的应用在运行 Android 4.4 的设备上的性能,应将您的 targetSdkVersion 设置为 "19",在 Android 4.4 系统映像上安装您的应用并进行测试,然后发布此变更的更新。

您可以通过在代码中添加条件,在执行您的 minSdkVersion 不支持的 API 之前检查系统 API 级别,实现在使用 Android 4.4 中的 API 的同时仍为旧版本提供支持。要详细了解如何保持向后兼容性,请阅读支持不同平台版本

如需了解有关 API 级别工作方式的详细信息,请阅读什么是 API 级别?

重要的行为变更

如果您之前发布过 Android 应用,请注意您的应用可能受到 Android 4.4 变化的影响。

如果您的应用从外部存储空间读取…

您的应用在 Android 4.4 上运行时无法读取外部存储空间上的共享文件,除非您的应用具有 [READ_EXTERNAL_STORAGE](https://developer.android.google.cn/reference/android/Manifest.permission.html#READ_EXTERNAL_STORAGE) 权限。也就是说,没有此权限,您无法再访问 [getExternalStoragePublicDirectory()](https://developer.android.google.cn/reference/android/os/Environment.html#getExternalStoragePublicDirectory(java.lang.String)) 返回的目录中的文件。但是,如果您仅需要访问 [getExternalFilesDir()](https://developer.android.google.cn/reference/android/content/Context.html#getExternalFilesDir(java.lang.String)) 提供的您的应用特有目录,那么,您不需要 [READ_EXTERNAL_STORAGE](https://developer.android.google.cn/reference/android/Manifest.permission.html#READ_EXTERNAL_STORAGE) 权限。

如果您的应用使用 WebView…

在 Android 4.4 上运行时,您的应用的行为会有所不同,将应用的 targetSdkVersion 更新为“19”或更高版本时尤其如此。

[WebView](https://developer.android.google.cn/reference/android/webkit/WebView.html) 类的底层代码和相关 API 已升级为基于现代的 Chromium 源代码快照。这会带来各种性能提升,同时为新的 HTML5 功能和远程调试 [WebView](https://developer.android.google.cn/reference/android/webkit/WebView.html) 内容提供支持。此次升级的范围意味着如果您的应用使用 [WebView](https://developer.android.google.cn/reference/android/webkit/WebView.html),则在某些情况下其行为可能会受影响。尽管对已知的行为变更进行了记录,但仅在您将应用的 targetSdkVersion 更新为“19”或更高版本时这些变更才会对应用产生很大的影响—新的 [WebView](https://developer.android.google.cn/reference/android/webkit/WebView.html) 在“兼容模式”中运行以便在面向 API 级别 18 和更低级别的应用中提供部分旧功能—您的应用有可能依赖来自以前的 [WebView](https://developer.android.google.cn/reference/android/webkit/WebView.html) 版本的未知行为。

因此,如果您的现有应用使用 [WebView](https://developer.android.google.cn/reference/android/webkit/WebView.html),则务必尽快在 Android 4.4 上进行测试,并查阅迁移到 Android 4.4 中的 WebView,以了解将 targetSdkVersion 更新为“19”或更高版本时对应用可能产生怎样的影响。

如果您的应用使用 AlarmManager…

将您的应用的 targetSdkVersion 设置为“19”或更高版本时,您使用 [set()](https://developer.android.google.cn/reference/android/app/AlarmManager.html#set(int,%20long,%20android.app.PendingIntent))[setRepeating()](https://developer.android.google.cn/reference/android/app/AlarmManager.html#setRepeating(int,%20long,%20long,%20android.app.PendingIntent)) 创建的闹铃将变得不准确。

为提高电源效率,Android 现在批处理在合理的相似时间发生的所有应用的闹铃,以便系统仅唤醒设备一次,而不是多次唤醒设备来处理每个闹铃。

如果您的闹铃没有与精确的时钟时间关联,但您的闹铃仍必须在特定时间范围(例如,在下午 2 点至 4 点之间)触发,那么您可以使用新的 [setWindow()](https://developer.android.google.cn/reference/android/app/AlarmManager.html#setWindow(int,%20long,%20long,%20android.app.PendingIntent)) 方法,其接受闹铃的“最早”时间以及最早时间之后的一个时间“窗口”,在这个窗口内,系统应触发闹铃。

如果您的闹铃必须固定到一个精确的时钟时间(例如,日历事件提醒),那么您可以使用新的 [setExact()](https://developer.android.google.cn/reference/android/app/AlarmManager.html#setExact(int,%20long,%20android.app.PendingIntent)) 方法。
这个精确的批处理行为仅适用于更新后的应用。如果您已将 targetSdkVersion 设置为“18”或更低版本,那么在 Android 4.4 上运行时,您的闹铃的行为方式和在以前版本上一样。

如果您的应用使用 ContentResolver 同步数据…

将应用的 targetSdkVersion 设置为“19”或更高版本时,使用 [addPeriodicSync()](https://developer.android.google.cn/reference/android/content/ContentResolver.html#addPeriodicSync(android.accounts.Account,%20java.lang.String,%20android.os.Bundle,%20long)) 创建同步将在默认的 Flex 间隔内(在您指定期间的 4% 左右)执行您的同步操作。例如,如果您的轮询频率是 24 小时,则您的同步操作每天可能会在大约一小时的时间窗口内发生,而不是在确切地同一时间发生。

要指定您自己的 Flex 间隔进行同步操作,您应开始使用新的 [requestSync()](https://developer.android.google.cn/reference/android/content/ContentResolver.html#requestSync(android.accounts.Account,%20java.lang.String,%20android.os.Bundle)) 方法。如需了解更多详情,请参阅下面的同步适配器部分。

这个 Flex 间隔行为仅适用于更新后的应用。如果您已将 targetSdkVersion 设置为“18”或更低版本,那么在 Android 4.4 上运行时,您的现有同步请求的行为方式和在以前版本上一样。

打印框架

现在,Android 包含一个完整框架,允许用户使用通过 WLAN、蓝牙或其他服务连接的打印机打印任何文档。系统在需要打印文档的应用和向打印机传输打印作业的服务之间处理此事务。[android.print](https://developer.android.google.cn/reference/android/print/package-summary.html) 框架提供指定打印文档并将其传输到系统进行打印所需要的所有 API。对于给定的打印作业,您实际需要哪个 API 取决于您的内容。

打印通用内容

如果您要从 UI 将内容打印为文档,首先,您需要创建一个 [PrintDocumentAdapter](https://developer.android.google.cn/reference/android/print/PrintDocumentAdapter.html) 的子类。在此类中,您必须实现几个回调方法,包括 [onLayout()](https://developer.android.google.cn/reference/android/print/PrintDocumentAdapter.html#onLayout(android.print.PrintAttributes,%20android.print.PrintAttributes,%20android.os.CancellationSignal,%20android.print.PrintDocumentAdapter.LayoutResultCallback,%20android.os.Bundle))[onWrite()](https://developer.android.google.cn/reference/android/print/PrintDocumentAdapter.html#onWrite(android.print.PageRange[],%20android.os.ParcelFileDescriptor,%20android.os.CancellationSignal,%20android.print.PrintDocumentAdapter.WriteResultCallback)),前者是为了基于提供的打印属性建立布局,后者是为了将可打印内容序列化为一个 [ParcelFileDescriptor](https://developer.android.google.cn/reference/android/os/ParcelFileDescriptor.html)

为了将您的内容写入 [ParcelFileDescriptor](https://developer.android.google.cn/reference/android/os/ParcelFileDescriptor.html),您必须向其传递一个 PDF。新的 [PdfDocument](https://developer.android.google.cn/reference/android/graphics/pdf/PdfDocument.html) API 简化了此操作,它提供来自 [getCanvas()](https://developer.android.google.cn/reference/android/graphics/pdf/PdfDocument.Page.html#getCanvas())[Canvas](https://developer.android.google.cn/reference/android/graphics/Canvas.html),您可以在上面绘制可打印内容。然后,使用 [writeTo()](https://developer.android.google.cn/reference/android/graphics/pdf/PdfDocument.html#writeTo(java.io.OutputStream)) 方法将 [PdfDocument](https://developer.android.google.cn/reference/android/graphics/pdf/PdfDocument.html) 写入 [ParcelFileDescriptor](https://developer.android.google.cn/reference/android/os/ParcelFileDescriptor.html)

在针对 [PrintDocumentAdapter](https://developer.android.google.cn/reference/android/print/PrintDocumentAdapter.html) 定义您的实现后,您可以根据用户请求使用 [PrintManager](https://developer.android.google.cn/reference/android/print/PrintManager.html) 方法 [print()](https://developer.android.google.cn/reference/android/print/PrintManager.html#print(java.lang.String,%20android.print.PrintDocumentAdapter,%20android.print.PrintAttributes)) 执行打印作业,该方法将 [PrintDocumentAdapter](https://developer.android.google.cn/reference/android/print/PrintDocumentAdapter.html) 作为它的一个参数。

打印图像

如果您只想打印照片或其他位图,则支持库中的帮助程序 API 可以为您完成所有工作。只需创建一个新的 [PrintHelper](https://developer.android.google.cn/reference/android/support/v4/print/PrintHelper.html)实例,使用 [setScaleMode()](https://developer.android.google.cn/reference/android/support/v4/print/PrintHelper.html#setScaleMode(int)) 设置缩放模式,然后将您的 [Bitmap](https://developer.android.google.cn/reference/android/graphics/Bitmap.html) 传递到 [printBitmap()](https://developer.android.google.cn/reference/android/support/v4/print/PrintHelper.html#printBitmap(java.lang.String,%20android.graphics.Bitmap))。就这么简单。支持库将处理与系统进行的所有剩余交互,以将位图传输到打印机。

构建打印服务

作为打印机原始设备制造商,您可以通过 Android 设备使用 [android.printservice](https://developer.android.google.cn/reference/android/printservice/package-summary.html) 框架为您的打印机提供互操作性,您可以以 APK 形式构建和分配打印服务,用户可以将其安装在他们的设备上。打印服务应用主要作为无外设服务运行,为 [PrintService](https://developer.android.google.cn/reference/android/printservice/PrintService.html) 类创建子类,后者从系统接收打印作业,并使用适当的协议将作业传输到它的打印机。
如需有关如何打印您的应用内容的详细信息,请阅读打印内容

短信提供程序

[Telephony](https://developer.android.google.cn/reference/android/provider/Telephony.html) 内容提供程序(“短信提供程序”)允许应用读写设备上的短信和彩信。它包含已进行接收、起草、发送、挂起等操作的短信和彩信的表格。

从 Android 4.4 开始,系统设置允许用户选择一个“默认短信应用”。选择后,只有默认短信应用可以在短信提供程序中进行写入操作,并且当用户接收短信或彩信时,只有默认短信应用可以接收 [SMS_DELIVER_ACTION](https://developer.android.google.cn/reference/android/provider/Telephony.Sms.Intents.html#SMS_DELIVER_ACTION) 广播或 [WAP_PUSH_DELIVER_ACTION](https://developer.android.google.cn/reference/android/provider/Telephony.Sms.Intents.html#WAP_PUSH_DELIVER_ACTION) 广播。默认短信应用有责任在收到或发送新消息时将消息的详细信息写入短信提供程序。

其他未被选为默认短信应用的应用只能读取短信提供程序,但通过侦听 [SMS_RECEIVED_ACTION](https://developer.android.google.cn/reference/android/provider/Telephony.Sms.Intents.html#SMS_RECEIVED_ACTION) 广播,这些应用也许会在设备接收短信时收到通知,因为该广播是不可中止的,所以可能会发送给多个应用。此广播主要用于那些没有被选为默认短信应用、但需要读取特殊传入消息(例如进行手机号码验证)的应用。

如需了解详细信息,请阅读博文让您的短信应用为迎接 KitKat 做好准备

无线和连接

主机卡模拟

Android 应用现在可以模拟使用 APDU 进行数据交换的 ISO14443-4 (ISO-DEP) NFC 卡(根据在 ISO7816-4 中进行的指定)。这让运行 Android 4.4 并启用 NFC 的设备可以同时模拟多个 NFC 卡,并允许 NFC 支付终端或其他 NFC 读取器基于应用标识符 (AID) 通过适当的 NFC 卡发起交易。

如果您要在应用中模拟使用这些协议的 NFC 卡,则可基于 [HostApduService](https://developer.android.google.cn/reference/android/nfc/cardemulation/HostApduService.html) 类创建一个服务组件。然而,如果您的应用改用一个安全的元素进行卡模拟,那么您必须基于 [OffHostApduService](https://developer.android.google.cn/reference/android/nfc/cardemulation/OffHostApduService.html) 类创建一个服务,该服务不会直接参与交易,但必须通过它才能注册应由安全元素处理的 AID。

如需了解详细信息,请阅读 NFC 卡模拟指南。

NFC 读取器模式

新的 NFC 读取器模式允许 Activity 将所有 NFC Activity 限制为在前台时仅读取 Activity 感兴趣的标记类型。您可以使用 [enableReaderMode()](https://developer.android.google.cn/reference/android/nfc/NfcAdapter.html#enableReaderMode(android.app.Activity,%20android.nfc.NfcAdapter.ReaderCallback,%20int,%20android.os.Bundle)) 为您的 Activity 启用读取器模式,提供一个 [NfcAdapter.ReaderCallback](https://developer.android.google.cn/reference/android/nfc/NfcAdapter.ReaderCallback.html) 的实现,用于在检测到新的标记时接收回调。

这个新功能与主机卡模拟结合使用,将允许 Android 同时在移动支付接口的两端运行:一个设备作为支付终端运行(运行读取器模式 Activity 的设备),另一个设备作为支付客户端运行(模拟 NFC 卡的设备)。

红外线发射器

现在,在附带红外线 (IR) 发射器的设备上运行时,您可以使用 [ConsumerIrManager](https://developer.android.google.cn/reference/android/hardware/ConsumerIrManager.html) API 发射红外线信号。要获取 [ConsumerIrManager](https://developer.android.google.cn/reference/android/hardware/ConsumerIrManager.html) 的实例,请使用 [CONSUMER_IR_SERVICE](https://developer.android.google.cn/reference/android/content/Context.html#CONSUMER_IR_SERVICE) 调用 [getSystemService()](https://developer.android.google.cn/reference/android/content/Context.html#getSystemService(java.lang.Class%3CT%3E)) 作为参数。然后,您可以使用 [getCarrierFrequencies()](https://developer.android.google.cn/reference/android/hardware/ConsumerIrManager.html#getCarrierFrequencies()) 查询设备支持的红外线频率,并通过使用 [transmit()](https://developer.android.google.cn/reference/android/hardware/ConsumerIrManager.html#transmit(int,%20int[])) 传递所需的频率和信号模式来发射信号。

首先,您必须通过调用 [hasIrEmitter()](https://developer.android.google.cn/reference/android/hardware/ConsumerIrManager.html#hasIrEmitter()) 检查设备是否附带红外线发射器,但是,如果您的应用仅与附带红外线发射器的设备兼容,则应在您的 "android.hardware.consumerir" ([FEATURE_CONSUMER_IR](https://developer.android.google.cn/reference/android/content/pm/PackageManager.html#FEATURE_CONSUMER_IR)) 的清单中包含一个 <uses-feature> 元素。

多媒体

自适应播放

现在,可通过 [MediaCodec](https://developer.android.google.cn/reference/android/media/MediaCodec.html) API 支持自适应视频播放,从而实现在 [Surface](https://developer.android.google.cn/reference/android/view/Surface.html) 上播放时分辨率可无缝变更 - 您可以馈送新分辨率的解码器输入帧,输出缓冲区的分辨率将发生变化,但不会出现大间隙。

您可以通过向 [MediaFormat](https://developer.android.google.cn/reference/android/media/MediaFormat.html) 添加两个密钥用于从编解码器指定您的应用所需的最大分辨率([KEY_MAX_WIDTH](https://developer.android.google.cn/reference/android/media/MediaFormat.html#KEY_MAX_WIDTH)[KEY_MAX_HEIGHT](https://developer.android.google.cn/reference/android/media/MediaFormat.html#KEY_MAX_HEIGHT)),来启用自适应播放。将上述密钥添加到您的 [MediaFormat](https://developer.android.google.cn/reference/android/media/MediaFormat.html) 后,使用 [configure()](https://developer.android.google.cn/reference/android/media/MediaCodec.html#configure(android.media.MediaFormat,%20android.view.Surface,%20android.media.MediaCrypto,%20int))[MediaFormat](https://developer.android.google.cn/reference/android/media/MediaFormat.html) 传递到您的 [MediaCodec](https://developer.android.google.cn/reference/android/media/MediaCodec.html) 实例。

编解码器将以无缝方式在等于或小于这些值的分辨率之间进行转换。编解码器可能还支持大于指定最大值的分辨率(只要在受支持配置文件的限制内),但可能无法以无缝方式转换到较大分辨率。

要在解码 H.264 视频时更改分辨率,请继续使用 MediaCodec.queueInputBuffer() 为帧排队,但请确保在一个缓冲区中通过即时解码刷新 (IDR) 帧同时提供新的序列参数集 (SPS) 和图像参数集 (PPS) 值。

不过,在尝试配置编解码器进行自适应播放之前,您必须先使用 [FEATURE_AdaptivePlayback](https://developer.android.google.cn/reference/android/media/MediaCodecInfo.CodecCapabilities.html#FEATURE_AdaptivePlayback) 调用 [isFeatureSupported(String)](https://developer.android.google.cn/reference/android/media/MediaCodecInfo.CodecCapabilities.html#isFeatureSupported(java.lang.String)) 来验证设备是否支持自适应播放。

:是否支持自适应播放因供应商而异。某些编解码器可能需要更多内存来实现较大的分辨率 hint。因此,您应基于您正在解码的源材料设置分辨率最大值。

音频点播时间戳

为促进音频视频同步,新的 [AudioTimestamp](https://developer.android.google.cn/reference/android/media/AudioTimestamp.html) 类在一个由 [AudioTrack](https://developer.android.google.cn/reference/android/media/AudioTrack.html) 处理的音频流中提供有关特定“帧”的时间线详细信息。要获取最新可用的时间戳,请实例化 [AudioTimestamp](https://developer.android.google.cn/reference/android/media/AudioTimestamp.html) 对象并将其传递到 [getTimestamp()](https://developer.android.google.cn/reference/android/media/AudioTrack.html#getTimestamp(android.media.AudioTimestamp))。如果请求时间戳成功,将使用帧单元中的某个位置以及显示或承诺显示该帧的预估时间填充 [AudioTrack](https://developer.android.google.cn/reference/android/media/AudioTrack.html) 实例。

您可以在 [AudioTimestamp](https://developer.android.google.cn/reference/android/media/AudioTimestamp.html)(其属于单调递增)中使用 nanoTime 的值查找与 framePosition 相比关联最密切的视频帧,这样可以拖放、复制或内插视频帧以便与音频匹配。或者,您可以确定 nanoTime 的值和未来视频帧的预计时间之间的增量时间(考虑样本率),以预测哪个音频帧有望与视频帧同时发生。

Surface 图像读取器

新的 [ImageReader](https://developer.android.google.cn/reference/android/media/ImageReader.html) API 让您可以直接访问图像缓冲区,因为它们渲染为 [Surface](https://developer.android.google.cn/reference/android/view/Surface.html)。您可以通过静态方法 [newInstance()](https://developer.android.google.cn/reference/android/media/ImageReader.html#newInstance(int,%20int,%20int,%20int)) 获取 [ImageReader](https://developer.android.google.cn/reference/android/media/ImageReader.html)。然后,调用 [getSurface()](https://developer.android.google.cn/reference/android/media/ImageReader.html#getSurface()) 新建一个 [Surface](https://developer.android.google.cn/reference/android/view/Surface.html),并使用 [MediaPlayer](https://developer.android.google.cn/reference/android/media/MediaPlayer.html)[MediaCodec](https://developer.android.google.cn/reference/android/media/MediaCodec.html) 等制作器传输您的图像数据。要在可通过 Surface 获取新图像时收到通知,请实现 [ImageReader.OnImageAvailableListener](https://developer.android.google.cn/reference/android/media/ImageReader.OnImageAvailableListener.html) 接口,并使用 [setOnImageAvailableListener()](https://developer.android.google.cn/reference/android/media/ImageReader.html#setOnImageAvailableListener(android.media.ImageReader.OnImageAvailableListener,%20android.os.Handler)) 注册它。

现在,由于您将内容绘制到 [Surface](https://developer.android.google.cn/reference/android/view/Surface.html),因此,当每个新图像帧可用时,您的 [ImageReader.OnImageAvailableListener](https://developer.android.google.cn/reference/android/media/ImageReader.OnImageAvailableListener.html) 将收到对 [onImageAvailable()](https://developer.android.google.cn/reference/android/media/ImageReader.OnImageAvailableListener.html#onImageAvailable(android.media.ImageReader)) 的调用,为您提供相应的 [ImageReader](https://developer.android.google.cn/reference/android/media/ImageReader.html)。通过调用 [acquireLatestImage()](https://developer.android.google.cn/reference/android/media/ImageReader.html#acquireLatestImage())[acquireNextImage()](https://developer.android.google.cn/reference/android/media/ImageReader.html#acquireNextImage()),您可以使用 [ImageReader](https://developer.android.google.cn/reference/android/media/ImageReader.html) 获取帧的图像数据作为 [Image](https://developer.android.google.cn/reference/android/media/Image.html) 对象。

通过 [Image](https://developer.android.google.cn/reference/android/media/Image.html) 对象,您可以直接访问 [ByteBuffer](https://developer.android.google.cn/reference/java/nio/ByteBuffer.html) 中的图像的时间戳、格式、尺寸和像素数据。不过,要让 [Image](https://developer.android.google.cn/reference/android/media/Image.html) 类解释您的图像,则必须按照 [ImageFormat](https://developer.android.google.cn/reference/android/graphics/ImageFormat.html)[PixelFormat](https://developer.android.google.cn/reference/android/graphics/PixelFormat.html) 中的常量定义的某个类型对其进行格式化。

峰值和有效值 (RMS) 测量

现在,通过创建 [Visualizer.MeasurementPeakRms](https://developer.android.google.cn/reference/android/media/audiofx/Visualizer.MeasurementPeakRms.html) 的新实例,并将其传递到 [getMeasurementPeakRms()](https://developer.android.google.cn/reference/android/media/audiofx/Visualizer.html#getMeasurementPeakRms(android.media.audiofx.Visualizer.MeasurementPeakRms)),您可以从 [Visualizer](https://developer.android.google.cn/reference/android/media/audiofx/Visualizer.html) 查询当前音频流的峰值和有效值 (RMS)。当您调用此方法时,给定 [Visualizer.MeasurementPeakRms](https://developer.android.google.cn/reference/android/media/audiofx/Visualizer.MeasurementPeakRms.html) 的峰值和有效值 (RMS) 将设为最新的测量值。

音量增强器

[LoudnessEnhancer](https://developer.android.google.cn/reference/android/media/audiofx/LoudnessEnhancer.html)[AudioEffect](https://developer.android.google.cn/reference/android/media/audiofx/AudioEffect.html) 的一个新子类,允许您增加 [MediaPlayer](https://developer.android.google.cn/reference/android/media/MediaPlayer.html)[AudioTrack](https://developer.android.google.cn/reference/android/media/AudioTrack.html) 的音量。此方法特别适合与上面提到的新的 [getMeasurementPeakRms()](https://developer.android.google.cn/reference/android/media/audiofx/Visualizer.html#getMeasurementPeakRms(android.media.audiofx.Visualizer.MeasurementPeakRms)) 方法结合使用,以便在播放其他媒体的同时增加语音音轨的音量。

遥控器

Android 4.0(API 级别 14)引入了 [RemoteControlClient](https://developer.android.google.cn/reference/android/media/RemoteControlClient.html) API,其允许媒体应用从远程客户端使用媒体控制器事件,例如锁定屏幕上的媒体控件。现在,新的 [RemoteController](https://developer.android.google.cn/reference/android/media/RemoteController.html) API 允许您构建自己的遥控器,使您能够开发创新的应用和外围设备,来控制与 [RemoteControlClient](https://developer.android.google.cn/reference/android/media/RemoteControlClient.html) 集成的任何媒体应用的播放。

要生成遥控器,您可随意实现您的用户界面,但要向用户的媒体应用传输媒体按钮事件,就必须创建一个服务来扩展 [NotificationListenerService](https://developer.android.google.cn/reference/android/service/notification/NotificationListenerService.html) 类和实现 [RemoteController.OnClientUpdateListener](https://developer.android.google.cn/reference/android/media/RemoteController.OnClientUpdateListener.html) 接口。

[NotificationListenerService](https://developer.android.google.cn/reference/android/service/notification/NotificationListenerService.html) 作为基础非常重要,因为它可提供适当的隐私限制,其要求用户在系统安全性设置中启用您的应用作为通知侦听器。

[NotificationListenerService](https://developer.android.google.cn/reference/android/service/notification/NotificationListenerService.html) 类包含一对您必须实现的抽象方法,但如果您仅关心用于处理媒体播放的媒体控制器事件,那么您可以为其设置空实现,并将重点转向 [RemoteController.OnClientUpdateListener](https://developer.android.google.cn/reference/android/media/RemoteController.OnClientUpdateListener.html) 方法。

从遥控器进行评分

Android 4.4 基于遥控客户端(使用 [RemoteControlClient](https://developer.android.google.cn/reference/android/media/RemoteControlClient.html) 接收媒体控件事件的应用)的现有功能创建,添加了允许用户从遥控器对当前曲目进行评分的功能。

新的 [Rating](https://developer.android.google.cn/reference/android/media/Rating.html) 类封装与用户评分有关的信息。评分由其评分样式([RATING_HEART](https://developer.android.google.cn/reference/android/media/Rating.html#RATING_HEART)[RATING_THUMB_UP_DOWN](https://developer.android.google.cn/reference/android/media/Rating.html#RATING_THUMB_UP_DOWN)[RATING_3_STARS](https://developer.android.google.cn/reference/android/media/Rating.html#RATING_3_STARS)[RATING_4_STARS](https://developer.android.google.cn/reference/android/media/Rating.html#RATING_4_STARS)[RATING_5_STARS](https://developer.android.google.cn/reference/android/media/Rating.html#RATING_5_STARS)[RATING_PERCENTAGE](https://developer.android.google.cn/reference/android/media/Rating.html#RATING_PERCENTAGE))和适用于该样式的评分值定义。

要允许用户从遥控器对您的曲目进行评分:

  • 通过在 [setTransportControlFlags()](https://developer.android.google.cn/reference/android/media/RemoteControlClient.html#setTransportControlFlags(int)) 中添加 [FLAG_KEY_MEDIA_RATING](https://developer.android.google.cn/reference/android/media/RemoteControlClient.html#FLAG_KEY_MEDIA_RATING) 标记指示系统您想要向用户公开评分 UI(如果适用)。
  • 调用 [editMetadata()](https://developer.android.google.cn/reference/android/media/RemoteControlClient.html#editMetadata(boolean)) 以检索 [RemoteControlClient.MetadataEditor](https://developer.android.google.cn/reference/android/media/RemoteControlClient.MetadataEditor.html),并使用 [addEditableKey()](https://developer.android.google.cn/reference/android/media/MediaMetadataEditor.html#addEditableKey(int)) 向其传递 [RATING_KEY_BY_USER](https://developer.android.google.cn/reference/android/media/MediaMetadataEditor.html#RATING_KEY_BY_USER)
  • 然后,指定评分样式,方法是调用 [putObject()](https://developer.android.google.cn/reference/android/media/MediaMetadataEditor.html#putObject(int,%20java.lang.Object)) 并向其传递 [RATING_KEY_BY_USER](https://developer.android.google.cn/reference/android/media/MediaMetadataEditor.html#RATING_KEY_BY_USER) 作为密钥,同时向其传递上述某个评分样式作为值。

要在用户从遥控器更改评分时接收回调,则实现新的 [RemoteControlClient.OnMetadataUpdateListener](https://developer.android.google.cn/reference/android/media/RemoteControlClient.OnMetadataUpdateListener.html) 接口,并向 [setMetadataUpdateListener()](https://developer.android.google.cn/reference/android/media/RemoteControlClient.html#setMetadataUpdateListener(android.media.RemoteControlClient.OnMetadataUpdateListener)) 传递一个实例。当用户更改评分时,您的 [RemoteControlClient.OnMetadataUpdateListener](https://developer.android.google.cn/reference/android/media/RemoteControlClient.OnMetadataUpdateListener.html) 将收到一个对 [onMetadataUpdate()](https://developer.android.google.cn/reference/android/media/RemoteControlClient.OnMetadataUpdateListener.html#onMetadataUpdate(int,%20java.lang.Object)) 的调用,传递 [RATING_KEY_BY_USER](https://developer.android.google.cn/reference/android/media/MediaMetadataEditor.html#RATING_KEY_BY_USER) 作为密钥,同时传递一个 [Rating](https://developer.android.google.cn/reference/android/media/Rating.html) 对象作为值。

隐藏式字幕

现在,播放 HTTP Live Stream (HLS) 视频时,[VideoView](https://developer.android.google.cn/reference/android/widget/VideoView.html) 支持 WebVTT 字幕跟踪,根据用户在系统设置中定义的隐藏式字幕首选项显示字幕跟踪。

您也可以使用 [addSubtitleSource()](https://developer.android.google.cn/reference/android/widget/VideoView.html#addSubtitleSource(java.io.InputStream,%20android.media.MediaFormat)) 方法为 [VideoView](https://developer.android.google.cn/reference/android/widget/VideoView.html) 提供 WebVTT 字幕跟踪。此方法接受一个 [InputStream](https://developer.android.google.cn/reference/java/io/InputStream.html),其携带字幕数据和指定该字幕数据格式的 [MediaFormat](https://developer.android.google.cn/reference/android/media/MediaFormat.html) 对象,您可以使用 [createSubtitleFormat()](https://developer.android.google.cn/reference/android/media/MediaFormat.html#createSubtitleFormat(java.lang.String,%20java.lang.String)) 指定该对象。这些字幕也会按照用户的首选项显示在视频上。

如果您不使用 [VideoView](https://developer.android.google.cn/reference/android/widget/VideoView.html) 显示您的视频内容,则应尽可能使您的字幕叠加层与用户的隐藏式字幕首选项匹配。新的 [CaptioningManager](https://developer.android.google.cn/reference/android/view/accessibility/CaptioningManager.html) API 允许您查询用户的隐藏式字幕首选项,包括 [CaptioningManager.CaptionStyle](https://developer.android.google.cn/reference/android/view/accessibility/CaptioningManager.CaptionStyle.html) 定义的样式,如字体和颜色。如果用户在视频已开始后调整部分首选项,您应侦听首选项的变化,方法是注册一个 [CaptioningManager.CaptioningChangeListener](https://developer.android.google.cn/reference/android/view/accessibility/CaptioningManager.CaptioningChangeListener.html) 实例以在任意首选项发生变化时接收回调,然后根据需要更新您的字幕。

动画和图形

场景和转场

新的 [android.transition](https://developer.android.google.cn/reference/android/transition/package-summary.html) 框架提供有诸多 API,可为在不同用户界面状态之间创建动画提供便利。一个关键的功能是让您能够通过为每个状态创建单独的布局,定义不同的 UI 状态,称为“场景”。当您需要制作从一个场景到另一个场景的动画时,可执行“转场”,其计算更改从当前场景到下一场景的布局所需的动画。

要在两个场景间进行转场,您通常需要执行以下操作:

  1. 指定包含您要更改的 UI 组件的 [ViewGroup](https://developer.android.google.cn/reference/android/view/ViewGroup.html)
  2. 指定表示变更的最终结果的布局(下一个场景)。
  3. 指定应添加布局变更动画的转场类型。
  4. 执行转场。

您可以使用 [Scene](https://developer.android.google.cn/reference/android/transition/Scene.html) 对象完成第 1 步和第 2 步。[Scene](https://developer.android.google.cn/reference/android/transition/Scene.html) 包含描述执行转场所必需的布局属性的元数据,其中包括场景的父视图和场景布局。您可以使用类构造函数或静态方法 [getSceneForLayout()](https://developer.android.google.cn/reference/android/transition/Scene.html#getSceneForLayout(android.view.ViewGroup,%20int,%20android.content.Context)) 创建 [Scene](https://developer.android.google.cn/reference/android/transition/Scene.html)

然后,您必须使用 [TransitionManager](https://developer.android.google.cn/reference/android/transition/TransitionManager.html) 完成第 3 步和第 4 步。其中一个方法是将您的 [Scene](https://developer.android.google.cn/reference/android/transition/Scene.html) 传递到静态方法 [go()](https://developer.android.google.cn/reference/android/transition/TransitionManager.html#go(android.transition.Scene))。这样可在当前布局中找到场景的父视图,并在子视图上执行转场,以实现 [Scene](https://developer.android.google.cn/reference/android/transition/Scene.html) 定义的布局。

或者,您根本不需要创建 [Scene](https://developer.android.google.cn/reference/android/transition/Scene.html) 对象,而是改为调用 [beginDelayedTransition()](https://developer.android.google.cn/reference/android/transition/TransitionManager.html#beginDelayedTransition(android.view.ViewGroup)),指定一个包含您要更改的视图的 [ViewGroup](https://developer.android.google.cn/reference/android/view/ViewGroup.html)。然后,添加、移除或重新配置目标视图。在系统根据需要进行更改后,转场开始以动画方式呈现所有受影响的视图。

要进行其他控制,您可以使用项目 res/transition/ 目录中的 XML 文件定义应在预定义的场景间发生的转场集。在 <transitionManager> 元素内部,指定一个或多个 <transition> 标记,每个标记均指定一个场景(引用布局文件)以及进入和/或退出该场景时将应用的转场。然后,使用 [inflateTransitionManager()](https://developer.android.google.cn/reference/android/transition/TransitionInflater.html#inflateTransitionManager(int,%20android.view.ViewGroup)) 扩展此转场集。使用返回的 [TransitionManager](https://developer.android.google.cn/reference/android/transition/TransitionManager.html) 通过 [transitionTo()](https://developer.android.google.cn/reference/android/transition/TransitionManager.html#transitionTo(android.transition.Scene)) 执行每个转场,传递一个由 <transition> 标记表示的 [Scene](https://developer.android.google.cn/reference/android/transition/Scene.html)。您还可以使用 [TransitionManager](https://developer.android.google.cn/reference/android/transition/TransitionManager.html) API 以编程方式定义转场集。

指定转场时,您可以使用 [Transition](https://developer.android.google.cn/reference/android/transition/Transition.html) 的子类(如 [Fade](https://developer.android.google.cn/reference/android/transition/Fade.html)[ChangeBounds](https://developer.android.google.cn/reference/android/transition/ChangeBounds.html))定义的多个预定义类型。如果您不指定转场类型,系统会默认使用 [AutoTransition](https://developer.android.google.cn/reference/android/transition/AutoTransition.html),其根据需要自动消失、移动和重新调整视图。此外,您也可以通过扩展其中的任何一个类来创建自定义转场,以执行您需要的动画。自定义转场可以跟踪您想要跟踪的任何属性变更,并基于这些变更创建您需要的任何动画。例如,您可以提供 [Transition](https://developer.android.google.cn/reference/android/transition/Transition.html) 的子类,用于侦听对视图的“旋转”属性进行的变更,然后以动画方式呈现所有变更。

如需了解详细信息,请参阅 [TransitionManager](https://developer.android.google.cn/reference/android/transition/TransitionManager.html) 文档。

动画暂停

[Animator](https://developer.android.google.cn/reference/android/animation/Animator.html) API 现在允许您使用方法 [pause()](https://developer.android.google.cn/reference/android/animation/Animator.html#pause())[resume()](https://developer.android.google.cn/reference/android/animation/Animator.html#resume()) 暂停和继续进行中的动画。

要跟踪动画的状态,您可以实现 [Animator.AnimatorPauseListener](https://developer.android.google.cn/reference/android/animation/Animator.AnimatorPauseListener.html) 接口,其在暂停和继续动画时提供回调:[pause()](https://developer.android.google.cn/reference/android/animation/Animator.html#pause())[resume()](https://developer.android.google.cn/reference/android/animation/Animator.html#resume())。然后使用 [addPauseListener()](https://developer.android.google.cn/reference/android/animation/Animator.html#addPauseListener(android.animation.Animator.AnimatorPauseListener))[Animator](https://developer.android.google.cn/reference/android/animation/Animator.html) 对象添加侦听器。

或者,您可以创建 [AnimatorListenerAdapter](https://developer.android.google.cn/reference/android/animation/AnimatorListenerAdapter.html) 抽象类的子类,其现在包含针对 [Animator.AnimatorPauseListener](https://developer.android.google.cn/reference/android/animation/Animator.AnimatorPauseListener.html)定义的暂停和继续回调的空实现。

可重复使用的位图

现在,您可以重复使用 [BitmapFactory](https://developer.android.google.cn/reference/android/graphics/BitmapFactory.html) 中的任意可变位图来解码任何其他位图,即使新位图具有不同的尺寸也无妨,只要生成的已解码位图的字节计数(可通过 [getByteCount()](https://developer.android.google.cn/reference/android/graphics/Bitmap.html#getByteCount()) 获取)小于或等于分配的已重用位图的字节计数(可通过 [getAllocationByteCount()](https://developer.android.google.cn/reference/android/graphics/Bitmap.html#getAllocationByteCount()) 获取)即可。如需了解详细信息,请参阅 [inBitmap](https://developer.android.google.cn/reference/android/graphics/BitmapFactory.Options.html#inBitmap)

适用于 [Bitmap](https://developer.android.google.cn/reference/android/graphics/Bitmap.html) 的新 API 允许针对 [BitmapFactory](https://developer.android.google.cn/reference/android/graphics/BitmapFactory.html) 外部的重复使用进行相似的重新配置(适用于手动位图生成或自定义解码逻辑)。现在,您可以使用方法 [setHeight()](https://developer.android.google.cn/reference/android/graphics/Bitmap.html#setHeight(int))[setWidth()](https://developer.android.google.cn/reference/android/graphics/Bitmap.html#setWidth(int)) 设置位图的尺寸,并使用 [setConfig()](https://developer.android.google.cn/reference/android/graphics/Bitmap.html#setConfig(android.graphics.Bitmap.Config)) 指定一个新的 [Bitmap.Config](https://developer.android.google.cn/reference/android/graphics/Bitmap.Config.html),而不会影响底层位图分配。此外,利用 [reconfigure()](https://developer.android.google.cn/reference/android/graphics/Bitmap.html#reconfigure(int,%20int,%20android.graphics.Bitmap.Config)) 方法可方便地将这些变更合并到一个调用。

不过,您不应重新配置视图系统当前使用的位图,因为底层像素缓冲区重新映射的方式不可预测。

用户内容

存储访问框架

在以前的 Android 版本上,如果您想让应用从其他应用中检索特定的文件类型,它必须调用具有[ACTION_GET_CONTENT](https://developer.android.google.cn/reference/android/content/Intent.html#ACTION_GET_CONTENT) 操作的 intent。此操作仍适合请求您要导入到应用中的文件。不过,Android 4.4 引入了 [ACTION_OPEN_DOCUMENT](https://developer.android.google.cn/reference/android/content/Intent.html#ACTION_OPEN_DOCUMENT) 操作,其允许用户选择特定类型的文件并授予应用对该文件的长期读取权限(可能也授予写入权限),无需向您的应用导入此文件。

如果您要开发为文件提供存储服务(如云保存服务)的应用,可以加入这个统一的 UI,通过实现一个内容提供程序作为新 [DocumentsProvider](https://developer.android.google.cn/reference/android/provider/DocumentsProvider.html) 类的子类来选取文件。您的 [DocumentsProvider](https://developer.android.google.cn/reference/android/provider/DocumentsProvider.html) 的子类必须包含一接受 [PROVIDER_INTERFACE](https://developer.android.google.cn/reference/android/provider/DocumentsContract.html#PROVIDER_INTERFACE)操作 ("android.content.action.DOCUMENTS_PROVIDER") 的 intent 过滤器。然后,您必须在 [DocumentsProvider](https://developer.android.google.cn/reference/android/provider/DocumentsProvider.html) 中实现四个抽象方法:
[queryRoots()](https://developer.android.google.cn/reference/android/provider/DocumentsProvider.html#queryRoots(java.lang.String[]))此方法必须使用 [DocumentsContract.Root](https://developer.android.google.cn/reference/android/provider/DocumentsContract.Root.html) 中定义的列返回一个描述文档存储空间的所有根目录的 [Cursor](https://developer.android.google.cn/reference/android/database/Cursor.html)[queryChildDocuments()](https://developer.android.google.cn/reference/android/provider/DocumentsProvider.html#queryChildDocuments(java.lang.String,%20java.lang.String[],%20android.os.Bundle))此方法必须使用 [DocumentsContract.Document](https://developer.android.google.cn/reference/android/provider/DocumentsContract.Document.html) 中定义的列返回一个描述指定目录中的所有文件的 [Cursor](https://developer.android.google.cn/reference/android/database/Cursor.html)[queryDocument()](https://developer.android.google.cn/reference/android/provider/DocumentsProvider.html#queryDocument(java.lang.String,%20java.lang.String[]))此方法必须使用 [DocumentsContract.Document](https://developer.android.google.cn/reference/android/provider/DocumentsContract.Document.html) 中定义的列返回一个描述指定文件的 [Cursor](https://developer.android.google.cn/reference/android/database/Cursor.html)[openDocument()](https://developer.android.google.cn/reference/android/provider/DocumentsProvider.html#openDocument(java.lang.String,%20java.lang.String,%20android.os.CancellationSignal))此方法必须返回一个表示指定文件的 [ParcelFileDescriptor](https://developer.android.google.cn/reference/android/os/ParcelFileDescriptor.html)。用户选择了文件,并且客户端应用通过调用 [openFileDescriptor()](https://developer.android.google.cn/reference/android/content/ContentResolver.html#openFileDescriptor(android.net.Uri,%20java.lang.String)) 请求对该文件的访问权限后,系统便会调用此方法。

如需了解详细信息,请参阅存储访问框架指南。

外部存储空间访问

现在,您可以在辅助外部存储介质上读取和写入应用特定文件,如在设备提供模拟的存储空间和 SD 卡时。新增的方法 [getExternalFilesDirs()](https://developer.android.google.cn/reference/android/content/Context.html#getExternalFilesDirs(java.lang.String)) 与现有的 [getExternalFilesDir()](https://developer.android.google.cn/reference/android/content/Context.html#getExternalFilesDir(java.lang.String)) 方法作用相同,不过它会返回一个 [File](https://developer.android.google.cn/reference/java/io/File.html) 对象数组。在读取或写入此方法返回的任意路径之前,将 [File](https://developer.android.google.cn/reference/java/io/File.html) 对象传递到新的 [getStorageState()](https://developer.android.google.cn/reference/android/os/Environment.html#getStorageState(java.io.File)) 方法以验证存储空间当前是否可用。

现在,用于访问应用专有缓存目录和 OBB 目录的其他方法也具有对应的版本,这些版本可提供辅助存储设备的访问权限:分别为 [getExternalCacheDirs()](https://developer.android.google.cn/reference/android/content/Context.html#getExternalCacheDirs())[getObbDirs()](https://developer.android.google.cn/reference/android/content/Context.html#getObbDirs())

将返回的 [File](https://developer.android.google.cn/reference/java/io/File.html) 数组中的第一个条目视为设备的主要外部存储空间,其与 [getExternalFilesDir()](https://developer.android.google.cn/reference/android/content/Context.html#getExternalFilesDir(java.lang.String)) 等现有方法返回的 [File](https://developer.android.google.cn/reference/java/io/File.html) 相同。

:从 Android 4.4 开始,当您需要使用上述方法仅访问您的应用特定外部存储区域时,此平台不再要求您的应用获取 [WRITE_EXTERNAL_STORAGE](https://developer.android.google.cn/reference/android/Manifest.permission.html#WRITE_EXTERNAL_STORAGE)[READ_EXTERNAL_STORAGE](https://developer.android.google.cn/reference/android/Manifest.permission.html#READ_EXTERNAL_STORAGE)。不过,如果您要访问 [getExternalStoragePublicDirectory()](https://developer.android.google.cn/reference/android/os/Environment.html#getExternalStoragePublicDirectory(java.lang.String)) 提供的外部存储空间的可共享区域,则需要这些权限。

同步适配器

[ContentResolver](https://developer.android.google.cn/reference/android/content/ContentResolver.html) 中新的 [requestSync()](https://developer.android.google.cn/reference/android/content/ContentResolver.html#requestSync(android.accounts.Account,%20java.lang.String,%20android.os.Bundle)) 方法将请求封装在新的 [SyncRequest](https://developer.android.google.cn/reference/android/content/SyncRequest.html) 对象中(您可以使用 [SyncRequest.Builder](https://developer.android.google.cn/reference/android/content/SyncRequest.Builder.html) 创建该对象),从而简化了为 [ContentProvider](https://developer.android.google.cn/reference/android/content/ContentProvider.html) 定义同步请求的一些步骤。[SyncRequest](https://developer.android.google.cn/reference/android/content/SyncRequest.html) 中的属性提供与现有 [ContentProvider](https://developer.android.google.cn/reference/android/content/ContentProvider.html) 同步调用相同的功能,但增加了一项功能,即:可以通过启用 [setDisallowMetered()](https://developer.android.google.cn/reference/android/content/SyncRequest.Builder.html#setDisallowMetered(boolean)) 指定当网络按流量计费时应放弃同步。

用户输入

新传感器类型

新的 [TYPE_GEOMAGNETIC_ROTATION_VECTOR](https://developer.android.google.cn/reference/android/hardware/Sensor.html#TYPE_GEOMAGNETIC_ROTATION_VECTOR) 传感器基于磁力计提供旋转矢量数据,当陀螺仪不可用或与批处理传感器事件结合用于在手机处于休眠状态下记录设备的屏幕方向时,它可以很好地代替 [TYPE_ROTATION_VECTOR](https://developer.android.google.cn/reference/android/hardware/Sensor.html#TYPE_ROTATION_VECTOR) 传感器。此传感器耗电比 [TYPE_ROTATION_VECTOR](https://developer.android.google.cn/reference/android/hardware/Sensor.html#TYPE_ROTATION_VECTOR) 更低,但可能容易出现噪音事件数据,因此当用户在户外时其效果最佳。

Android 现在也在硬件中支持内置计步传感器:
[TYPE_STEP_DETECTOR](https://developer.android.google.cn/reference/android/hardware/Sensor.html#TYPE_STEP_DETECTOR)用户每迈出一步,此传感器就会触发一个事件。对于每个用户步伐,此传感器提供一个值为 1.0 的事件和一个指示此步伐发生时间的时间戳。[TYPE_STEP_COUNTER](https://developer.android.google.cn/reference/android/hardware/Sensor.html#TYPE_STEP_COUNTER)此传感器也会针对检测到的每个步伐触发一个事件,但提供的步数是自应用首次注册该传感器以来累计的总步数。

请注意,这两个计步传感器提供的结果并非总是相同。与来自 [TYPE_STEP_DETECTOR](https://developer.android.google.cn/reference/android/hardware/Sensor.html#TYPE_STEP_DETECTOR) 的事件相比,[TYPE_STEP_COUNTER](https://developer.android.google.cn/reference/android/hardware/Sensor.html#TYPE_STEP_COUNTER)事件的发生延迟时间更长,但这是因为 [TYPE_STEP_COUNTER](https://developer.android.google.cn/reference/android/hardware/Sensor.html#TYPE_STEP_COUNTER) 算法会进行较多的处理以消除误报。因此,[TYPE_STEP_COUNTER](https://developer.android.google.cn/reference/android/hardware/Sensor.html#TYPE_STEP_COUNTER) 在传输事件时可能较为缓慢,但其结果应更为准确。
这两个计步传感器都依赖硬件(Nexus 5 是首款支持它们的设备),因此您应使用 [FEATURE_SENSOR_STEP_DETECTOR](https://developer.android.google.cn/reference/android/content/pm/PackageManager.html#FEATURE_SENSOR_STEP_DETECTOR)[FEATURE_SENSOR_STEP_COUNTER](https://developer.android.google.cn/reference/android/content/pm/PackageManager.html#FEATURE_SENSOR_STEP_COUNTER) 常量检查 [hasSystemFeature()](https://developer.android.google.cn/reference/android/content/pm/PackageManager.html#hasSystemFeature(java.lang.String)) 的可用性。

批处理传感器事件

为更好地管理设备电源,[SensorManager](https://developer.android.google.cn/reference/android/hardware/SensorManager.html) API 现在允许您指定您想要系统向应用传输批量传感器事件的频率。这不会减少给定时间段内您的应用可用的实际传感器事件数量,但是会降低系统使用传感器更新调用 [SensorEventListener](https://developer.android.google.cn/reference/android/hardware/SensorEventListener.html) 的频率。也就是说,系统会将一段时间内发生的所有事件保存起来,然后一次性将它们传输到您的应用,而不是在每个事件发生时将其传输到您的应用。

为提供批处理,[SensorManager](https://developer.android.google.cn/reference/android/hardware/SensorManager.html) 类添加了两个新的 [registerListener()](https://developer.android.google.cn/reference/android/hardware/SensorManager.html#registerListener(android.hardware.SensorEventListener,%20android.hardware.Sensor,%20int,%20int)) 方法版本,其允许您指定“最大报告延迟时间”。这个新参数指定您的 [SensorEventListener](https://developer.android.google.cn/reference/android/hardware/SensorEventListener.html) 对于传输新传感器事件可容忍的最大延迟。例如,如果您指定一分钟的批量延迟时间,则系统将通过连续调用 [onSensorChanged()](https://developer.android.google.cn/reference/android/hardware/SensorEventListener.html#onSensorChanged(android.hardware.SensorEvent)) 方法(针对批处理的每个事件调用一次)以不超过一分钟的间隔传输最新的批处理事件集。传感器事件的延迟时间绝不会超过最大报告延迟时间值,但如果其他应用针对相同的传感器请求了较短的延迟时间,则事件可能会较快到达。

不过,请注意,仅当 CPU 处于唤醒状态时,此传感器才会基于报告延迟时间向应用传输批处理事件。尽管在 CPU 处于休眠状态时支持批处理的硬件传感器将继续收集传感器事件,但它不会唤醒 CPU 向应用传输批处理事件。当传感器最终耗尽了其用于存储事件的内存时,它将开始丢弃最早的事件以保存最新的事件。您可以在传感器填满其内存之前唤醒设备来避免丢失事件,然后调用 [flush()](https://developer.android.google.cn/reference/android/hardware/SensorManager.html#flush(android.hardware.SensorEventListener)) 来采集最新的批处理事件。要预估内存的填满时间和刷新时间,请调用 [getFifoMaxEventCount()](https://developer.android.google.cn/reference/android/hardware/Sensor.html#getFifoMaxEventCount()) 以获取它可以保存的最大数量的传感器事件,并用该数字除以您的应用需要各个事件的比率。使用该计算结果通过 [AlarmManager](https://developer.android.google.cn/reference/android/app/AlarmManager.html) 设置唤醒闹铃,其调用 [Service](https://developer.android.google.cn/reference/android/app/Service.html)(实现 [SensorEventListener](https://developer.android.google.cn/reference/android/hardware/SensorEventListener.html))来刷新传感器。

:并非所有设备都支持批处理传感器事件,因为它需要硬件传感器的支持。不过,从 Android 4.4 开始,您应始终使用新的 [registerListener()](https://developer.android.google.cn/reference/android/hardware/SensorManager.html#registerListener(android.hardware.SensorEventListener,%20android.hardware.Sensor,%20int,%20int)) 方法,因为如果设备不支持批处理,系统会相应地忽略批处理延迟时间参数,并实时传输传感器事件。

控制器身份

现在,Android 使用一个唯一的整数识别每个已连接的控制器,您可以使用 [getControllerNumber()](https://developer.android.google.cn/reference/android/view/InputDevice.html#getControllerNumber()) 查询该整数,这样您可以更轻松地将每个控制器与游戏中的不同玩家进行关联。每个控制器的编号可能会因用户断开、连接或重新配置控制器而发生变化,因此您应注册一个 [InputManager.InputDeviceListener](https://developer.android.google.cn/reference/android/hardware/input/InputManager.InputDeviceListener.html) 实例来跟踪与每个输入设备对应的控制器编号。然后,在发生变化时为每个 [InputDevice](https://developer.android.google.cn/reference/android/view/InputDevice.html) 调用 [getControllerNumber()](https://developer.android.google.cn/reference/android/view/InputDevice.html#getControllerNumber())
现在,已连接的设备还提供产品和供应商 ID,这些 ID 可通过 [getProductId()](https://developer.android.google.cn/reference/android/view/InputDevice.html#getProductId())[getVendorId()](https://developer.android.google.cn/reference/android/view/InputDevice.html#getVendorId()) 获取。如果您需要基于设备上可用的密钥集修改您的密钥映射,您可以查询该设备以检查特定密钥是否可以通过 [hasKeys(int...)](https://developer.android.google.cn/reference/android/view/InputDevice.html#hasKeys(int...)) 获取。

用户界面

沉浸式全屏模式

要为您的应用提供填充整个屏幕的布局,适用于 [setSystemUiVisibility()](https://developer.android.google.cn/reference/android/view/View.html#setSystemUiVisibility(int)) 的新标记 [SYSTEM_UI_FLAG_IMMERSIVE](https://developer.android.google.cn/reference/android/view/View.html#SYSTEM_UI_FLAG_IMMERSIVE)(与 [SYSTEM_UI_FLAG_HIDE_NAVIGATION](https://developer.android.google.cn/reference/android/view/View.html#SYSTEM_UI_FLAG_HIDE_NAVIGATION) 结合使用时)将启用新的沉浸式全屏模式。在启用沉浸式全屏模式后,您的 Activity 将继续接收所有触摸事件。用户可以沿着系统状态栏正常出现的区域向内滑动来显示系统状态栏。这将清除 [SYSTEM_UI_FLAG_HIDE_NAVIGATION](https://developer.android.google.cn/reference/android/view/View.html#SYSTEM_UI_FLAG_HIDE_NAVIGATION) 标记(如果应用了 [SYSTEM_UI_FLAG_FULLSCREEN](https://developer.android.google.cn/reference/android/view/View.html#SYSTEM_UI_FLAG_FULLSCREEN) 标记,也会清除该标记),因此系统状态栏保持可见状态。但是,如果您想要系统状态栏在片刻后再次隐藏,可以改用 [SYSTEM_UI_FLAG_IMMERSIVE_STICKY](https://developer.android.google.cn/reference/android/view/View.html#SYSTEM_UI_FLAG_IMMERSIVE_STICKY) 标记。

透明系统状态栏

现在,您可以使用新主题背景 [Theme.Holo.NoActionBar.TranslucentDecor](https://developer.android.google.cn/reference/android/R.style.html#Theme_Holo_NoActionBar_TranslucentDecor)[Theme.Holo.Light.NoActionBar.TranslucentDecor](https://developer.android.google.cn/reference/android/R.style.html#Theme_Holo_Light_NoActionBar_TranslucentDecor) 将系统状态栏设置为部分透明。通过启用透明系统状态栏,您的布局将填充系统状态栏后面的区域,因此,您也必须为不应被系统状态栏覆盖的布局部分启用 [fitsSystemWindows](https://developer.android.google.cn/reference/android/R.attr.html#fitsSystemWindows)

如果您要创建自定义主题背景,则将其中某个主题背景设置为父主题背景,或在您的主题背景中添加 [windowTranslucentNavigation](https://developer.android.google.cn/reference/android/R.attr.html#windowTranslucentNavigation)[windowTranslucentStatus](https://developer.android.google.cn/reference/android/R.attr.html#windowTranslucentStatus) 样式属性。

增强的通知侦听器

Android 4.3 添加了 [NotificationListenerService](https://developer.android.google.cn/reference/android/service/notification/NotificationListenerService.html) API,从而允许应用在系统发布新通知后接收有关新通知的信息。在 Android 4.4 中,通知侦听器可以检索通知的附加元数据,并填写有关通知操作的详细信息:
新增的 [Notification.extras](https://developer.android.google.cn/reference/android/app/Notification.html#extras) 字段包含一个 [Bundle](https://developer.android.google.cn/reference/android/os/Bundle.html),以向您的通知生成器提供 [EXTRA_TITLE](https://developer.android.google.cn/reference/android/app/Notification.html#EXTRA_TITLE)[EXTRA_PICTURE](https://developer.android.google.cn/reference/android/app/Notification.html#EXTRA_PICTURE) 等附加元数据。新增的 [Notification.Action](https://developer.android.google.cn/reference/android/app/Notification.Action.html) 类定义附加到通知的操作的特性,您可以从新的 [actions](https://developer.android.google.cn/reference/android/app/Notification.html#actions) 字段中检索这些特性。

可绘制的 RTL 布局镜像

在以前的 Android 版本中,如果您的应用包含应针对从右向左布局扭转其水平方向的图像,那么您必须在 drawables-ldrtl/ 资源目录中添加镜像。现在,通过在可绘制资源上启用 [autoMirrored](https://developer.android.google.cn/reference/android/R.attr.html#autoMirrored) 属性,或通过调用 [setAutoMirrored()](https://developer.android.google.cn/reference/android/graphics/drawable/Drawable.html#setAutoMirrored(boolean)),系统可以自动为您进行镜像。启用后,当布局方向为从右到左时,[Drawable](https://developer.android.google.cn/reference/android/graphics/drawable/Drawable.html) 将自动镜像。

无障碍功能

现在,借助 [View](https://developer.android.google.cn/reference/android/view/View.html) 类,可以通过向您的 XML 布局添加新的 [accessibilityLiveRegion](https://developer.android.google.cn/reference/android/R.attr.html#accessibilityLiveRegion) 属性或调用 [setAccessibilityLiveRegion()](https://developer.android.google.cn/reference/android/view/View.html#setAccessibilityLiveRegion(int)),为使用新增文本内容动态更新的 UI 部分声明“实时区域”。例如,应将带有显示“密码错误”通知的文本字段的登录屏幕标记为一个实时区域,以便当它发生变化时,屏幕阅读器可以复述此消息。

现在,提供无障碍服务的应用还可以利用新 API 增强其能力,这些 API 通过 [AccessibilityNodeInfo.CollectionInfo](https://developer.android.google.cn/reference/android/view/accessibility/AccessibilityNodeInfo.CollectionInfo.html)[AccessibilityNodeInfo.CollectionItemInfo](https://developer.android.google.cn/reference/android/view/accessibility/AccessibilityNodeInfo.CollectionItemInfo.html) 提供与列表或网格视图等视图集有关的信息。

应用权限

您的应用必须使用 <uses-permission> 标记请求下面的新权限才能使用某些新 API:
[INSTALL_SHORTCUT](https://developer.android.google.cn/reference/android/Manifest.permission.html#INSTALL_SHORTCUT)允许应用在启动器中安装快捷键[UNINSTALL_SHORTCUT](https://developer.android.google.cn/reference/android/Manifest.permission.html#UNINSTALL_SHORTCUT)允许应用在启动器中卸载快捷键[TRANSMIT_IR](https://developer.android.google.cn/reference/android/Manifest.permission.html#TRANSMIT_IR)允许应用使用设备的红外线发射器(如果可用)
:从 Android 4.4 开始,当您想要使用 [getExternalFilesDir()](https://developer.android.google.cn/reference/android/content/Context.html#getExternalFilesDir(java.lang.String)) 等方法访问外部存储空间上应用特有区域时,此平台不再要求您的应用获取 [WRITE_EXTERNAL_STORAGE](https://developer.android.google.cn/reference/android/Manifest.permission.html#WRITE_EXTERNAL_STORAGE)[READ_EXTERNAL_STORAGE](https://developer.android.google.cn/reference/android/Manifest.permission.html#READ_EXTERNAL_STORAGE)。但是,如果您要访问 [getExternalStoragePublicDirectory()](https://developer.android.google.cn/reference/android/os/Environment.html#getExternalStoragePublicDirectory(java.lang.String)) 提供的外部存储空间上可共享的区域,则仍需要此权限。

设备功能

下面是您可以使用 <uses-feature> 标记声明的新设备功能,以声明您的应用要求,并在 Google Play 上启用过滤功能或在运行时进行检查:
[FEATURE_CONSUMER_IR](https://developer.android.google.cn/reference/android/content/pm/PackageManager.html#FEATURE_CONSUMER_IR)此设备可与消费者红外线设备进行通信。[FEATURE_DEVICE_ADMIN](https://developer.android.google.cn/reference/android/content/pm/PackageManager.html#FEATURE_DEVICE_ADMIN)此设备通过设备管理员支持设备规范执行。[FEATURE_NFC_HOST_CARD_EMULATION](https://developer.android.google.cn/reference/android/content/pm/PackageManager.html#FEATURE_NFC_HOST_CARD_EMULATION)此设备支持基于主机的 NFC 卡模拟。[FEATURE_SENSOR_STEP_COUNTER](https://developer.android.google.cn/reference/android/content/pm/PackageManager.html#FEATURE_SENSOR_STEP_COUNTER)此设备附带硬件计步器。[FEATURE_SENSOR_STEP_DETECTOR](https://developer.android.google.cn/reference/android/content/pm/PackageManager.html#FEATURE_SENSOR_STEP_DETECTOR)此设备附带硬件步测器。

如需详细了解 Android 4.4 中的所有 API 变更,请参阅 API 差异报告