Android 4.3 API

API 级别:18

Android 4.3 (JELLY_BEAN_MR2) 是对 Jelly Bean 版本的更新,为用户和应用开发者提供了新功能。本文档介绍了其中最值得关注的新 API。

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

更新目标 API 级别

为了更好地在搭载 Android 4.3 的设备上优化您的应用,您应将 targetSdkVersion 设置为 "18",将其安装在 Android 4.3 系统映像上并进行测试,然后发布包含此变更的更新。

通过向代码添加条件,以便在执行您的 minSdkVersion 不支持的 API 之前,检查系统 API 级别,您可以使用 Android 4.3 中的 API 同时支持较低版本。如需详细了解如何保持向后兼容性,请参阅支持不同的平台版本

Android 支持库中还提供了各种 API,可让您在旧版平台上实现新功能。

如需详细了解 API 级别的工作原理,请参阅什么是 API 级别?

重要的行为变更

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

如果您的应用使用隐式 intent...

您的应用在受限配置文件环境中可能出现异常。

受限个人资料环境中的用户可能无法使用所有标准 Android 应用。例如,受限个人资料可能会停用网络浏览器和相机应用。因此,您的应用不应假设哪些应用可用,因为如果您在未验证应用是否可以处理 Intent 的情况下调用 startActivity(),应用可能会在受限配置文件中崩溃。

使用隐式 intent 时,您应始终通过调用 resolveActivity()queryIntentActivities() 来验证应用是否可用于处理该 intent。例如:

Kotlin

val intent = Intent(Intent.ACTION_SEND)
...
if (intent.resolveActivity(packageManager) != null) {
    startActivity(intent)
} else {
    Toast.makeText(context, R.string.app_not_available, Toast.LENGTH_LONG).show()
}

Java

Intent intent = new Intent(Intent.ACTION_SEND);
...
if (intent.resolveActivity(getPackageManager()) != null) {
    startActivity(intent);
} else {
    Toast.makeText(context, R.string.app_not_available, Toast.LENGTH_LONG).show();
}

如果您的应用依赖于帐号...

您的应用在受限配置文件环境中可能出现异常。

默认情况下,受限个人资料环境中的用户无权访问用户帐号。如果您的应用依赖于 Account,那么在受限资料中使用时,应用可能会崩溃或出现异常行为。

由于应用依赖于敏感的帐号信息,因此如果您想完全阻止受限个人资料使用您的应用,请在清单的 <application> 元素中指定 android:requiredAccountType 属性。

如果您希望允许受限个人资料在他们无法创建自己的帐号的情况下继续使用您的应用,则可以停用需要帐号的应用功能,或允许受限个人资料访问主要用户创建的帐号。如需了解详情,请参阅下面有关支持受限个人资料中帐号的部分。

如果您的应用使用 VideoView...

您的视频在 Android 4.3 上可能会缩小。

在以前的 Android 版本中,VideoView widget 错误地将 layout_heightlayout_width"wrap_content" 值计算为与 "match_parent" 相同。因此,虽然针对高度或宽度使用 "wrap_content" 之前可能已提供了您所需的视频布局,但这样做可能会导致 Android 4.3 及更高版本上的视频尺寸更小。若要解决此问题,请将 "wrap_content" 替换为 "match_parent",并验证您的视频在 Android 4.3 以及较低版本上是否按预期显示。

受限个人资料

在 Android 平板电脑上,用户现在可以根据主要用户创建受限个人资料。当用户创建受限个人资料时,他们可以启用限制,例如哪些应用可供该个人资料使用。Android 4.3 中有一组新的 API 还允许您为自己开发的应用构建精细限制设置。例如,通过使用新 API,您可以允许用户在受限配置文件环境中运行时控制应用中提供的内容类型。

供用户控制您构建的限制的界面由系统的“设置”应用管理。如需向用户显示应用的限制设置,您必须通过创建接收 ACTION_GET_RESTRICTION_ENTRIES intent 的 BroadcastReceiver 来声明应用提供的限制。系统会调用此 intent 以查询所有应用的可用限制,然后构建界面以允许主要用户管理每个受限个人资料的限制。

BroadcastReceiveronReceive() 方法中,您必须为应用提供的每个限制创建一个 RestrictionEntry。每个 RestrictionEntry 都会定义限制标题、说明和以下数据类型之一:

  • TYPE_BOOLEAN 表示限制条件为 true 或 false。
  • TYPE_CHOICE:适用于具有多个互斥选择的限制(单选按钮选项)。
  • TYPE_MULTI_SELECT适用于具有多个互斥的选项(复选框选项)的限制。

然后,将所有 RestrictionEntry 对象放入 ArrayList,并将其作为 EXTRA_RESTRICTIONS_LIST extra 的值放入广播接收器的结果。

系统会在“设置”应用中为应用的限制创建界面,并使用您为每个 RestrictionEntry 对象提供的唯一键来保存每项限制。当用户打开您的应用时,您可以通过调用 getApplicationRestrictions() 查询任何当前限制。这将返回一个 Bundle,其中包含您使用 RestrictionEntry 对象定义的每个限制的键值对。

如果您想提供布尔值、单选题和多选值无法处理的更具体的限制,则可以创建一个 activity,在该 activity 中,用户可以指定限制,并允许用户通过限制设置打开该 activity。在您的广播接收器中,将 EXTRA_RESTRICTIONS_INTENT extra 包含在结果 Bundle 中。此 extra 必须指定 Intent,以指示要启动的 Activity 类(使用 putParcelable() 方法通过 intent 传递 EXTRA_RESTRICTIONS_INTENT)。当主要用户进入您的 activity 以设置自定义限制时,您的 activity 必须使用 EXTRA_RESTRICTIONS_LISTEXTRA_RESTRICTIONS_BUNDLE 键返回包含 extra 中的限制值的结果,具体取决于您是分别指定 RestrictionEntry 对象还是键值对。

支持受限个人资料中的帐号

添加到主要用户的任何帐号都可供受限个人资料使用,但默认情况下无法通过 AccountManager API 访问这些帐号。如果您尝试在受限个人资料中使用 AccountManager 添加帐号,则会收到失败结果。由于存在这些限制,您有以下三种选择:

  • 允许通过受限个人资料访问所有者的帐号。

    要通过受限个人资料访问帐号,您必须将 android:restrictedAccountType 属性添加到 <application> 标记中:

    <application ...
        android:restrictedAccountType="com.example.account.type" >
    

    注意:启用此属性后,您的应用将可从受限个人资料访问主要用户的帐号。因此,仅当应用显示的信息不会泄露被视为敏感信息的个人身份信息 (PII) 时,您才应允许此行为。系统设置将通知主要用户,您的应用向其帐号授予受限个人资料,因此应向用户明确说明帐号访问权限对于应用的功能非常重要。如果可能,您还应为主要用户提供足够的限制控制措施,以定义应用中允许多少帐号访问权限。

  • 在无法修改帐号时停用某些功能。

    如果您想要使用帐号,但实际上并不需要帐号来实现应用的主要功能,则可以检查帐号可用性,并在功能不可用时停用功能。您应该首先检查是否有现成的帐号。如果不能,则通过调用 getUserRestrictions() 查询是否可以创建新帐号,并检查结果中的 DISALLOW_MODIFY_ACCOUNTS extra。如果值为 true,则应停用需要访问帐号的应用功能。例如:

    Kotlin

    val um = context.getSystemService(Context.USER_SERVICE) as UserManager
    val restrictions: Bundle = um.userRestrictions
    if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) {
        // cannot add accounts, disable some functionality
    }
    

    Java

    UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
    Bundle restrictions = um.getUserRestrictions();
    if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) {
        // cannot add accounts, disable some functionality
    }
    

    注意:在这种情况下,您应在清单文件中声明任何新属性。

  • 在无法访问私人帐号时停用您的应用。

    如果您的应用因应用依赖于帐号中的敏感个人信息(并且受限个人资料目前无法添加新帐号)而变得不能供受限个人资料使用,请将 android:requiredAccountType 属性添加到 <application> 标记中:

    <application ...
        android:requiredAccountType="com.example.account.type" >
    

    例如,Gmail 应用使用此属性为受限个人资料停用自身的功能,因为受限个人资料不应使用所有者的个人电子邮件地址。

  • 无线和连接

    蓝牙低功耗(智能就绪)

    Android 现已通过 android.bluetooth 中的新 API 支持蓝牙低功耗 (LE)。利用这些新 API,您可以构建与蓝牙低功耗外围设备(如心率监测器和计步器)通信的 Android 应用。

    由于蓝牙 LE 是一项硬件功能,并非所有 Android 设备都提供。因此,您必须在清单文件中为 "android.hardware.bluetooth_le" 声明一个 <uses-feature> 元素:

    <uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
    

    如果您已熟悉 Android 的传统蓝牙 API,请注意使用蓝牙 LE API 会有一些不同。最重要的是,现在应使用 BluetoothManager 类来执行一些高级操作,如获取 BluetoothAdapter、获取已连接设备的列表以及检查设备状态。例如,您现在应通过以下方式获取 BluetoothAdapter

    Kotlin

    val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
    bluetoothAdapter = bluetoothManager.adapter
    

    Java

    final BluetoothManager bluetoothManager =
            (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
    bluetoothAdapter = bluetoothManager.getAdapter();
    

    如需发现蓝牙 LE 外围设备,请对 BluetoothAdapter 调用 startLeScan(),并向其传递 BluetoothAdapter.LeScanCallback 接口的实现。当蓝牙适配器检测到蓝牙 LE 外围设备时,您的 BluetoothAdapter.LeScanCallback 实现会收到对 onLeScan() 方法的调用。此方法为您提供了一个 BluetoothDevice 对象(表示检测到的设备)、设备的 RSSI 值,以及一个包含设备通告记录的字节数组。

    如果您只想扫描特定类型的外围设备,则可以调用 startLeScan() 并添加一个 UUID 对象数组,用于指定应用支持的 GATT 服务。

    注意:您只能扫描蓝牙 LE 设备,或者使用旧版 API 扫描传统蓝牙设备。您无法同时扫描 LE 和传统蓝牙设备。

    若要随后连接到蓝牙 LE 外围设备,请对相应的 BluetoothDevice 对象调用 connectGatt(),并向其传递 BluetoothGattCallback 的实现。您的 BluetoothGattCallback 实现会收到关于设备连接状态和其他事件的回调。在 onConnectionStateChange() 回调期间,如果该方法传递 STATE_CONNECTED 作为新状态,您就可以开始与设备通信。

    如要访问设备上的蓝牙功能,您的应用还需要请求特定的蓝牙用户权限。如需更多信息,请参阅蓝牙低功耗 API 指南。

    WLAN 仅扫描模式

    尝试识别用户所在位置时,Android 可能会使用 Wi-Fi 通过扫描附近的接入点来帮助确定位置。不过,用户通常会使 Wi-Fi 保持关闭状态以节省电量,从而导致位置数据不太准确。Android 现在包含一种仅扫描模式,可让设备 Wi-Fi 扫描接入点,以便在不连接到接入点的情况下获取位置信息,从而大大减少电池用量。

    如果您想获取用户的位置信息,但 Wi-Fi 当前处于关闭状态,您可以调用 startActivity() 并使用 ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE 操作,请求用户启用 Wi-Fi 仅扫描模式。

    Wi-Fi 配置

    新的 WifiEnterpriseConfig API 允许面向企业的服务自动为受管设备配置 Wi-Fi。

    快速响应来电

    从 Android 4.0 开始,“快速回复”功能允许用户使用即时短信回复来电,而无需接听来电或解锁设备。到目前为止,这些快速消息始终由默认的即时通讯应用处理。现在,任何应用都可以通过为 ACTION_RESPOND_VIA_MESSAGE 创建带有 intent 过滤器的 Service 来声明其处理这些消息的功能。

    当用户通过快速响应来响应来电时,“电话”应用会发送 ACTION_RESPOND_VIA_MESSAGE intent,其中包含描述接听者(来电者)的 URI,以及包含用户想要发送的消息的 EXTRA_TEXT extra。当您的服务收到 intent 时,它应该传递消息并立即自行停止(您的应用不应显示 activity)。

    为了接收此 intent,您必须声明 SEND_RESPOND_VIA_MESSAGE 权限。

    多媒体

    MediaExtractor 和 MediaCodec 增强功能

    Android 现在可让您使用 MediaCodecMediaExtractor 中的现有 API,更轻松地按照 ISO/IEC 23009-1 标准编写自己的基于 HTTP 的动态自适应流式传输 (DASH) 播放器。这些 API 底层的框架已更新,以支持解析碎片 MP4 文件,但您的应用仍负责解析 MPD 元数据并将各个流传递给 MediaExtractor

    如果要将 DASH 用于加密内容,请注意 getSampleCryptoInfo() 方法会返回描述每个加密媒体样本结构的 MediaCodec.CryptoInfo 元数据。此外,getPsshInfo() 方法已添加到 MediaExtractor,以便您可以访问 DASH 媒体的 PSSH 元数据。此方法会返回 UUID 对象到字节的映射,其中 UUID 指定加密方案,字节是该方案特有的数据。

    媒体数字版权管理

    新的 MediaDrm 类提供了一种模块化解决方案,通过将 DRM 问题与媒体播放分离,来对媒体内容进行数字版权管理 (DRM)。例如,通过这种 API 分离,您无需使用 Widevine 媒体格式即可播放 Widevine 加密内容。此 DRM 解决方案还支持 DASH 通用加密,因此您可以对流式传输内容使用各种 DRM 方案。

    您可以使用 MediaDrm 获取不透明的密钥请求消息,并处理来自服务器的密钥响应消息,以进行许可获取和预配。您的应用负责处理与服务器的网络通信;MediaDrm 类仅提供生成和处理消息的功能。

    MediaDrm API 旨在与 Android 4.1(API 级别 16)中引入的 MediaCodec API 结合使用,其中包括用于对内容进行编码和解码的 MediaCodec、用于处理加密内容的 MediaCrypto,以及用于对内容进行提取和多路复用的 MediaExtractor

    您必须先构建 MediaExtractorMediaCodec 对象。然后,您便可以访问用于标识 DRM 方案的 UUID(通常通过内容中的元数据进行访问),并使用它通过其构造函数构造 MediaDrm 对象的实例。

    通过 Surface 进行视频编码

    Android 4.1(API 级别 16)添加了 MediaCodec 类,用于对媒体内容进行低级编码和解码。对视频进行编码时,Android 4.1 要求您为媒体提供 ByteBuffer 数组,但 Android 4.3 现在允许您使用 Surface 作为编码器的输入。例如,这可让您对来自现有视频文件的输入或使用由 OpenGL ES 生成的帧进行编码。

    如需使用 Surface 作为编码器的输入,请先为 MediaCodec 调用 configure()。然后,调用 createInputSurface() 以接收 Surface,然后在其上流式传输媒体。

    例如,您可以将给定 Surface 用作 OpenGL 上下文的窗口,只需将其传递给 eglCreateWindowSurface() 即可。然后,在渲染 Surface 时,调用 eglSwapBuffers() 以将帧传递给 MediaCodec

    如需开始编码,请对 MediaCodec 调用 start()。完成后,调用 signalEndOfInputStream() 以终止编码,然后对 Surface 调用 release()

    媒体多路复用

    新的 MediaMuxer 类支持在一个音频流与一个视频流之间进行多路复用。这些 API 与 Android 4.2 中添加的 MediaExtractor 类相对应,后者用于对媒体进行多路复用(解复用)操作。

    支持的输出格式在 MediaMuxer.OutputFormat 中定义。目前,MP4 是唯一受支持的输出格式,MediaMuxer 目前一次仅支持一个音频流和/或一个视频流。

    MediaMuxer 主要与 MediaCodec 配合使用,因此您可以通过 MediaCodec 执行视频处理,然后通过 MediaMuxer 将输出保存到 MP4 文件中。您还可以将 MediaMuxerMediaExtractor 结合使用,在无需编码或解码的情况下执行媒体修改。

    RemoteControlClient 的播放进度和调试

    在 Android 4.0(API 级别 14)中,添加了 RemoteControlClient,用于从遥控器客户端启用媒体播放控件,例如锁定屏幕上提供的控件。Android 4.3 现在为此类控制器提供了显示播放位置的功能,以及用于拖拽播放的控件。如果您已使用 RemoteControlClient API 为媒体应用启用了遥控器,则可以通过实现两个新接口来允许播放播放操作。

    首先,您必须通过将 FLAG_KEY_MEDIA_POSITION_UPDATE 标志传递给 setTransportControlsFlags() 来启用该标志。

    然后,实现以下两个新接口:

    RemoteControlClient.OnGetPlaybackPositionListener
    这包含回调 onGetPlaybackPosition(),当遥控器需要更新其界面中的进度时,该回调会请求媒体的当前位置。
    RemoteControlClient.OnPlaybackPositionUpdateListener
    其中包含回调 onPlaybackPositionUpdate(),该回调在用户使用遥控器界面调试播放内容时告知应用媒体的新时间代码。

    将播放更新为新位置后,请调用 setPlaybackState() 以指示新的播放状态、位置和速度。

    定义这些接口后,您可以分别通过调用 setOnGetPlaybackPositionListener()setPlaybackPositionUpdateListener()RemoteControlClient 设置这些接口。

    显卡

    对 OpenGL ES 3.0 的支持

    Android 4.3 添加了 Java 接口和对 OpenGL ES 3.0 的原生支持。OpenGL ES 3.0 中提供的主要新功能包括:

    • 加速高级视觉效果
    • 将高质量 ETC2/EAC 纹理压缩作为标准特征
    • 支持整数和 32 位浮点的新版 GLSL ES 着色语言
    • 高级纹理渲染
    • 对纹理大小和渲染缓冲区格式进行更广泛的标准化

    Android 上 OpenGL ES 3.0 的 Java 接口随 GLES30 提供。使用 OpenGL ES 3.0 时,请务必在清单文件中使用 <uses-feature> 标记和 android:glEsVersion 属性对其进行声明。例如:

    <manifest>
        <uses-feature android:glEsVersion="0x00030000" />
        ...
    </manifest>
    

    还要记得通过调用 setEGLContextClientVersion() 并传递 3 作为版本来指定 OpenGL ES 上下文。

    如需详细了解如何使用 OpenGL ES,包括如何在运行时检查设备支持的 OpenGL ES 版本,请参阅 OpenGL ES API 指南。

    可绘制对象的 mipmapping

    使用 mipmap 作为位图或可绘制对象的来源是一种提供优质图像和各种图像缩放比例的简单方法,如果您希望在动画播放过程中缩放图像,这种方法特别有用。

    Android 4.2(API 级别 17)在 Bitmap 类中添加了对 mipmap 的支持 - 如果您提供了 mipmap 来源并启用了 setHasMipMap(),Android 会交换 Bitmap 中的 mip 图片。现在,在 Android 4.3 中,您还可以通过提供 mipmap 资源并在位图资源文件中设置 android:mipMap 属性,或通过调用 hasMipMap() 来为 BitmapDrawable 对象启用 mipmap。

    界面

    查看叠加层

    新的 ViewOverlay 类会在 View 之上提供一个透明层,您可以在其中添加视觉内容,这不会影响布局层次结构。您可以通过调用 getOverlay() 获取任何 ViewViewOverlay。叠加层的大小和位置始终与其宿主视图(创建它的视图)相同,这让您可以添加显示在主机视图前面的内容,但无法扩展该主机视图的边界。

    如果您要创建动画(例如将视图滑动到其容器之外或在屏幕上移动项目而不影响视图层次结构),使用 ViewOverlay 特别有用。不过,由于叠加层的可用区域受限于其宿主视图所在的区域,因此如果您想为某个视图在布局中超出其位置的动画添加动画效果,则必须使用具有所需布局边界的父视图中的叠加层。

    为 widget 视图(如 Button)创建叠加层时,您可以通过调用 add(Drawable)Drawable 对象添加到叠加层中。如果您为布局视图调用 getOverlay()(例如 RelativeLayout),则返回的对象将是 ViewGroupOverlayViewGroupOverlay 类是 ViewOverlay 的子类,它还允许您通过调用 add(View) 来添加 View 对象。

    注意:添加到叠加层的所有可绘制对象和视图都只是视觉内容。它们无法接收焦点或输入事件。

    例如,以下代码通过将视图置于父视图的叠加层中,然后对该视图执行平移动画,为向右滑动的视图添加动画效果:

    Kotlin

    val view: View? = findViewById(R.id.view_to_remove)
    val container: ViewGroup? = view?.parent as ViewGroup
    
    container?.apply {
        overlay.add(view)
        ObjectAnimator.ofFloat(view, "translationX", right.toFloat())
                .start()
    }
    

    Java

    View view = findViewById(R.id.view_to_remove);
    ViewGroup container = (ViewGroup) view.getParent();
    container.getOverlay().add(view);
    ObjectAnimator anim = ObjectAnimator.ofFloat(view, "translationX", container.getRight());
    anim.start();
    

    光学边界布局

    对于包含九宫格背景图片的视图,您现在可以指定它们应根据背景图片的“光学”边界(而不是视图的“裁剪”边界)与相邻的视图对齐。

    例如,图 1 和图 2 分别显示了相同的布局,但图 1 中的版本使用的是裁剪边界(默认行为),而图 2 使用的是光学边界。由于用于按钮和相框模式的 9-patch 图片包括边缘周围的内边距,因此在使用裁剪边界时,它们似乎不会相互对齐,也不会对齐文本。

    注意:图 1 和图 2 中的屏幕截图启用了“显示布局边界”开发者设置。对于每个视图,红线表示光学边界,蓝线表示裁剪边界,粉色表示外边距。

    图 1. 使用裁剪边界的布局(默认)。

    图 2. 使用光学边界的布局。

    如需根据视图的光学边界对齐视图,请在其中一个父布局中将 android:layoutMode 属性设置为 "opticalBounds"。例如:

    <LinearLayout android:layoutMode="opticalBounds" ... >
    

    图 3. Holo 按钮 9-patch 的缩放视图,具有光学边界。

    为此,应用于视图背景的九宫格图像必须使用沿九宫格文件底部和右侧的红线指定光学边界(如图 3 所示)。红线表示应从裁剪边界中减去的区域,保留图像的光学边界。

    在布局中为 ViewGroup 启用光学边界时,所有后代视图都会继承光学边界布局模式,除非您通过将 android:layoutMode 设置为 "clipBounds" 来为组替换该模式。所有布局元素还都会遵循其子视图的光学边界,并根据其内视图的光学边界调整自己的边界。不过,布局元素(ViewGroup 的子类)目前不支持应用于自身背景的九宫格图像的光学边界。

    如果您通过为 ViewViewGroup 或其任何子类创建子类来创建自定义视图,您的视图将继承这些光学绑定行为。

    注意:Holo 主题支持的所有 widget 都使用光学边界进行了更新,包括 ButtonSpinnerEditText 等。因此,如果您的应用应用了 Holo 主题(Theme.HoloTheme.Holo.Light 等),将 android:layoutMode 属性设置为 "opticalBounds",您可以立即受益。

    如需使用 Draw 9-patch 工具为您自己的 9-patch 图像指定光学边界,请在点击边框像素时按住 Control。

    Rect 值的动画

    您现在可以使用新的 RectEvaluator 在两个 Rect 值之间添加动画效果。这个新类是 TypeEvaluator 的实现,您可以将其传递给 ValueAnimator.setEvaluator()

    窗口附加和焦点监听器

    以前,如果要监听视图何时附加/分离到窗口,或何时其焦点发生更改,您需要替换 View 类,以分别实现 onAttachedToWindow()onDetachedFromWindow()onWindowFocusChanged()

    现在,如需接收附加和分离事件,您可以改为实现 ViewTreeObserver.OnWindowAttachListener,并使用 addOnWindowAttachListener() 在视图上设置它。如需接收焦点事件,您可以实现 ViewTreeObserver.OnWindowFocusChangeListener,并使用 addOnWindowFocusChangeListener() 在视图上设置它。

    TV 过扫描支持

    为确保您的应用填满每台电视的整个屏幕,您现在可以为应用布局启用过扫描。过扫描模式由 FLAG_LAYOUT_IN_OVERSCAN 标志决定,您可以使用 Theme_DeviceDefault_NoActionBar_Overscan 等平台主题启用该标记,也可以通过在自定义主题中启用 windowOverscan 样式来启用该标记。

    屏幕方向

    <activity> 标记的 screenOrientation 属性现在支持其他值,以遵循用户对自动屏幕旋转的偏好:

    "userLandscape"
    行为与 "sensorLandscape" 相同,但如果用户停用自动屏幕旋转,则它会锁定为正常横向方向且不会翻转。
    "userPortrait"
    行为与 "sensorPortrait" 相同,但如果用户停用自动屏幕旋转,则它会锁定为正常的纵向方向且不会翻转。
    "fullUser"
    行为与 "fullSensor" 相同,并且允许所有四个方向旋转,除非用户停用自动屏幕旋转,否则它会锁定在用户的首选方向上。

    此外,您现在还可以声明 "locked",以将应用的屏幕方向锁定为当前的屏幕方向。

    旋转动画

    WindowManager 中新增了 rotationAnimation 字段,可让您从系统切换屏幕方向时要使用的三个动画中选择一个。三个动画分别是:

    注意:这些动画只有在您将 activity 设置为使用“全屏”模式时才可用,您可以使用 Theme.Holo.NoActionBar.Fullscreen 等主题启用该模式。

    例如,您可以按以下步骤启用“淡入淡出”动画:

    Kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        val params: WindowManager.LayoutParams = window.attributes
        params.rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE
        window.attributes = params
        ...
    }
    

    Java

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        WindowManager.LayoutParams params = getWindow().getAttributes();
        params.rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
        getWindow().setAttributes(params);
        ...
    }
    

    用户输入

    新传感器类型

    借助新的 TYPE_GAME_ROTATION_VECTOR 传感器,您可以检测设备的旋转情况,而无需担心磁场干扰。与 TYPE_ROTATION_VECTOR 传感器不同,TYPE_GAME_ROTATION_VECTOR 并非基于磁北方向。

    新的 TYPE_GYROSCOPE_UNCALIBRATEDTYPE_MAGNETIC_FIELD_UNCALIBRATED 传感器提供原始传感器数据,而不考虑偏差估算。也就是说,现有的 TYPE_GYROSCOPETYPE_MAGNETIC_FIELD 传感器提供的传感器数据分别考虑到了设备中陀螺偏移和硬铁的估算偏差。然而,这些传感器的新“未校准”版本会提供原始传感器数据并单独提供估算的偏差值。借助这些传感器,您可以使用外部数据增强估算偏差,从而为传感器数据提供自己的自定义校准。

    通知监听器

    Android 4.3 添加了一个新的服务类 NotificationListenerService,让您的应用可以在系统发布新通知时接收有关这些通知的信息。

    如果您的应用目前使用无障碍服务 API 访问系统通知,您应更新应用以改用这些 API。

    联系人提供程序

    查询“contactables”

    新的联系人提供程序查询 Contactables.CONTENT_URI 提供了一种有效的方法来获取一个 Cursor,其中包含属于指定查询匹配的所有联系人的所有电子邮件地址和电话号码。

    查询联系人增量

    联系人提供程序中添加了新的 API,可让您高效地查询最近对联系人数据所做的更改。以前,当联系人数据中的某些内容发生更改时,您的应用会收到通知,但您并不知道具体发生了哪些内容,并且需要检索所有联系人,然后遍历这些联系人才能发现更改。

    如需跟踪插入和更新更改,您现在可以在选择中包含 CONTACT_LAST_UPDATED_TIMESTAMP 参数,以仅查询自您上次查询提供程序以来发生更改的联系人。

    为了跟踪删除的联系人,新表 ContactsContract.DeletedContacts 提供了关于删除的联系人的日志(但每个删除的联系人会在此表中保留一段有限的时间)。与 CONTACT_LAST_UPDATED_TIMESTAMP 类似,您可以使用新的选择参数 CONTACT_DELETED_TIMESTAMP 来检查自上次查询提供程序以来删除了哪些联系人。该表还包含常量 DAYS_KEPT_MILLISECONDS,该常量包含日志保留的天数(以毫秒为单位)。

    此外,当用户通过系统设置菜单清除联系人存储空间时,联系人提供程序会广播 CONTACTS_DATABASE_CREATED 操作,从而有效地重新创建联系人提供程序数据库。它旨在告知应用,它们需要删除已存储的所有联系信息,并使用新查询重新加载。

    如需获取使用这些 API 检查联系人更改的示例代码,请参阅 SDK 示例下载内容中提供的 ApiDemos 示例。

    本地化

    改进了对双向文本的支持

    以前的 Android 版本支持从右到左 (RTL) 的语言和布局,但有时无法正确处理混合方向文本。因此,Android 4.3 添加了 BidiFormatter API,可帮助您正确设置包含反向内容的文本格式,而不会对文本的任何部分进行乱码。

    例如,如果您想创建一个包含字符串变量的语句(如“Did you say 15 Bay Street, Laurel, CA?”),您通常会将本地化的字符串资源和变量传递给 String.format()

    Kotlin

    val suggestion = String.format(resources.getString(R.string.did_you_mean), address)
    

    Java

    Resources res = getResources();
    String suggestion = String.format(res.getString(R.string.did_you_mean), address);
    

    不过,如果语言区域是希伯来语,那么格式化的字符串将如下所示:

    האם התכוונת ל 15 Bay Street, Laurel, CA?

    这个数字不对,因为“15”应该放在“Bay Street”。解决方案是使用 BidiFormatter 及其 unicodeWrap() 方法。例如,上面的代码变为:

    Kotlin

    val bidiFormatter = BidiFormatter.getInstance()
    val suggestion = String.format(
            resources.getString(R.string.did_you_mean),
            bidiFormatter.unicodeWrap(address)
    )
    

    Java

    Resources res = getResources();
    BidiFormatter bidiFormatter = BidiFormatter.getInstance();
    String suggestion = String.format(res.getString(R.string.did_you_mean),
            bidiFormatter.unicodeWrap(address));
    

    默认情况下,unicodeWrap() 使用第一强方向估计启发法,如果文本方向的第一个信号不能代表整体内容的适当方向,则可能会出错。如有必要,您可以通过将某个 TextDirectionHeuristic 常量从 TextDirectionHeuristics 传递给 unicodeWrap() 来指定其他启发法。

    注意:这些新的 API 也可以通过 Android 支持库(包含 BidiFormatter 类和相关 API)用于旧版 Android。

    无障碍服务

    处理按键事件

    AccessibilityService 现在可以使用 onKeyEvent() 回调方法接收按键输入事件的回调。这样,无障碍服务就可以处理基于按键的输入设备(例如键盘)的输入,并将这些事件转换为特殊操作,而这些操作以前可能只能通过触控输入或设备的方向键来实现。

    选择文本并复制/粘贴

    AccessibilityNodeInfo 现在提供一些 API,以允许 AccessibilityService 选择、剪切、复制和粘贴节点中的文本。

    如需指定要剪切或复制的文本的选择,无障碍服务可以使用新操作 ACTION_SET_SELECTION,并使用 ACTION_ARGUMENT_SELECTION_START_INTACTION_ARGUMENT_SELECTION_END_INT 向其传递选择的开始和结束位置。或者,您也可以通过使用现有操作 ACTION_NEXT_AT_MOVEMENT_GRANULARITY(以前仅用于移动光标位置)操控光标位置,然后添加参数 ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN 来选择文本。

    然后,您可以使用 ACTION_CUTACTION_COPY 进行剪切或复制,然后再使用 ACTION_PASTE 进行粘贴。

    注意:这些新的 API 也可以通过 Android 支持库(具有 AccessibilityNodeInfoCompat 类)用于旧版 Android。

    声明无障碍功能

    从 Android 4.3 开始,无障碍服务必须在其元数据文件中声明无障碍功能,才能使用某些无障碍功能。如果元数据文件中未请求该功能,则该功能将为空操作。如需声明服务的无障碍功能,您必须使用与 AccessibilityServiceInfo 类中的各种“功能”常量对应的 XML 属性。

    例如,如果服务未请求 flagRequestFilterKeyEvents 功能,则该服务不会收到按键事件。

    测试和调试

    自动化界面测试

    新的 UiAutomation 类提供了可让您模拟用户操作以实现测试自动化的 API。通过使用平台的 AccessibilityService API,UiAutomation API 可让您检查屏幕内容并注入任意键盘和触摸事件。

    如需获取 UiAutomation 的实例,请调用 Instrumentation.getUiAutomation()。为此,在从 adb shell 运行 InstrumentationTestCase 时,必须在 instrument 命令中提供 -w 选项。

    借助 UiAutomation 实例,您可以执行任意事件以测试应用,具体方法是调用 executeAndWaitForEvent(),向其传递要执行的 Runnable、操作的超时期限以及 UiAutomation.AccessibilityEventFilter 接口的实现。在您的 UiAutomation.AccessibilityEventFilter 实现中,您将收到一个调用,您可以通过该调用过滤您感兴趣的事件,并确定给定测试用例是否成功。

    如需观察测试期间的所有事件,请创建 UiAutomation.OnAccessibilityEventListener 的实现并将其传递给 setOnAccessibilityEventListener()。然后,每次事件发生时,监听器接口都会收到对 onAccessibilityEvent() 的调用,并收到描述该事件的 AccessibilityEvent 对象。

    UiAutomation API 还在非常低的级别公开了其他各种操作,以鼓励开发界面测试工具(如 uiautomator)。例如,UiAutomation 还可以:

    • 注入输入事件
    • 更改屏幕方向
    • 截取屏幕截图

    最重要的是,对于界面测试工具,UiAutomation API 可跨应用边界工作,这一点与 Instrumentation 中的不同。

    适用于应用的 Systrace 事件

    Android 4.3 添加了 Trace 类,其中包含 beginSection()endSection() 这两个静态方法,以便您定义要包含在 systrace 报告中的代码块。通过在应用中创建可跟踪代码段,systrace 日志可让您更详细地分析应用中发生速度减慢的问题。

    如需了解如何使用 Systrace 工具,请参阅使用 Systrace 分析展示广告和性能

    安全性

    适用于应用私钥的 Android 密钥库

    Android 现在在 KeyStore 工具中提供了一个名为“Android 密钥库”的自定义 Java 安全提供程序,该提供程序可让您生成并保存只有您的应用可以查看和使用的私钥。要加载 Android 密钥库,请将 "AndroidKeyStore" 传递给 KeyStore.getInstance()

    如需在 Android 密钥库中管理应用的私有凭据,请使用 KeyPairGeneratorKeyPairGeneratorSpec 生成一个新密钥。首先,通过调用 getInstance() 获取 KeyPairGenerator 的实例。然后,调用 initialize(),并向其传递 KeyPairGeneratorSpec 的实例,您可以使用 KeyPairGeneratorSpec.Builder 获取该实例。最后,通过调用 generateKeyPair() 获取 KeyPair

    硬件凭据存储

    Android 现在还支持由硬件支持存储 KeyChain 凭据,让密钥不可提取,从而提高安全性。也就是说,一旦密钥位于由硬件支持的密钥库(安全元件、TPM 或 TrustZone)中,就可以用于加密操作,但无法导出私钥材料。连操作系统内核也无法访问此密钥材料。虽然并非所有 Android 设备都支持硬件上的存储空间,但您可以通过调用 KeyChain.IsBoundKeyAlgorithm() 在运行时检查是否提供由硬件支持的存储空间。

    清单声明

    可声明的必备功能

    <uses-feature> 元素中现在支持下列值,因此您可以确保仅将应用安装在提供应用所需功能的设备上。

    FEATURE_APP_WIDGETS
    声明您的应用提供应用 widget,并且应仅安装在带有可供用户嵌入应用 widget 的主屏幕或类似位置的设备上。示例:
    <uses-feature android:name="android.software.app_widgets" android:required="true" />
    
    FEATURE_HOME_SCREEN
    声明您的应用充当主屏幕替换应用,并且应仅安装在支持第三方主屏幕应用的设备上。 示例:
    <uses-feature android:name="android.software.home_screen" android:required="true" />
    
    FEATURE_INPUT_METHODS
    声明您的应用提供自定义输入法(使用 InputMethodService 构建的键盘),并且应仅安装在支持第三方输入法的设备上。 示例:
    <uses-feature android:name="android.software.input_methods" android:required="true" />
    
    FEATURE_BLUETOOTH_LE
    声明您的应用使用蓝牙低功耗 API,并且应仅安装在能够通过蓝牙低功耗与其他设备通信的设备上。 示例:
    <uses-feature android:name="android.software.bluetooth_le" android:required="true" />
    

    用户权限

    <uses-permission> 现在支持以下值,以声明您的应用访问特定 API 所需的权限。

    BIND_NOTIFICATION_LISTENER_SERVICE
    使用新的 NotificationListenerService API 所必需的。
    SEND_RESPOND_VIA_MESSAGE
    接收 ACTION_RESPOND_VIA_MESSAGE intent 的必需项。

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