Android 4.3 API

API 级别:18

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

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

更新目标 API 级别

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

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

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

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

重要的行为变更

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

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

您的应用在受限个人资料环境中可能会出现异常。

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

使用隐式 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 及更高版本的设备上,视频可能会小得多。要解决此问题,请将 与"match_parent""wrap_content",并验证你的视频在 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。在广播接收器中,在结果 Bundle 中添加 EXTRA_RESTRICTIONS_INTENT extra。此 extra 必须指定一个 Intent,指明要启动的 Activity 类(使用 putParcelable() 方法通过 intent 传递 EXTRA_RESTRICTIONS_INTENT)。当主要用户进入您的 activity 以设置自定义限制时,您的 然后,activity 必须使用 EXTRA_RESTRICTIONS_LISTEXTRA_RESTRICTIONS_BUNDLE 键,具体取决于您是否指定 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,请注意使用 Bluetooth 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 外围设备,请在相应设备上调用 connectGatt() BluetoothDevice 对象,向其传递一个实现 BluetoothGattCallback。您的 BluetoothGattCallback 实现会收到有关连接的回调 设备状态和其他事件。现在是onConnectionStateChange() 回调,当该方法将 STATE_CONNECTED 作为新状态传递时,您可以开始与设备通信。

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

    WLAN 仅扫描模式

    尝试确定用户位置时,Android 可能会使用 Wi-Fi 扫描附近的接入点,以帮助确定位置。不过,用户通常会关闭 Wi-Fi, 节约电量,导致位置数据不太准确。Android 现在包含一个 仅扫描模式,允许设备 Wi-Fi 扫描接入点以帮助获取位置信息 从而大大减少电池用量。

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

    Wi-Fi 配置

    借助新的 WifiEnterpriseConfig API,面向企业的服务可以为受管设备自动配置 Wi-Fi。

    快速回复来电

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

    当用户使用快速回复来响应来电时,“电话”应用会发送 包含 URI 的 ACTION_RESPOND_VIA_MESSAGE intent 用于描述接收方(调用方)和 EXTRA_TEXT extra 替换为用户想要发送的消息。当您的服务收到 intent 时,应传递消息并立即停止自身(您的应用不应显示 activity)。

    如需接收此 intent,您必须声明 SEND_RESPOND_VIA_MESSAGE 权限。

    多媒体

    MediaExtractor 和 MediaCodec 增强功能

    Android 现在可让您更轻松地编写自己的动态自适应 符合 ISO/IEC 23009-1 标准的 HTTP (DASH) 播放器流式传输 使用 MediaCodecMediaExtractor 中的现有 API。这些 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 对象。然后,您就可以访问 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 是 MediaExtractor 的对应项 Android 4.2 中添加了用于对媒体进行去复用(多路复用)媒体的类。

    支持的输出格式在 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() 指定 OpenGL ES 上下文,将 3 作为版本传递。

    有关使用 OpenGL ES 的详细信息,包括如何检查设备是否支持 在运行时使用 OpenGL ES 版本,请参阅 OpenGL ES API 指南。

    针对可绘制对象的 Mipmapping

    使用 mipmap 作为位图或可绘制对象的源是一种提供 以及各种不同的比例尺度,这对于 在动画播放过程中缩放的图片。

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

    界面

    查看叠加层

    新的 ViewOverlay 类在顶部提供一个透明层 一个 View,您可以在其中添加视觉内容,并且不会影响 布局层次结构您可以通过调用 getOverlay() 获取任何 ViewViewOverlay。叠加层始终与其宿主视图(用于创建该叠加层的视图)具有相同的大小和位置,这样您就可以添加显示在宿主视图前面的内容,但这些内容不能超出宿主视图的边界。

    在创建映像时,使用 ViewOverlay 特别有用 提供动画效果,例如将视图滑出容器或在屏幕上移动项目 而不会影响视图层次结构不过,由于叠加层的可用区域是 (如果您要为向外移动的视图添加动画效果) 则必须使用父视图中的叠加层,该叠加层具有所需 布局边界。

    为 widget 视图(如 Button)创建叠加层时,您需要 可通过调用 Drawable 对象向叠加层添加 add(Drawable)。如果您针对布局视图(例如 RelativeLayout)调用 getOverlay(),则返回的对象为 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 使用的是光学边界。由于 用于按钮和相框的九宫格图像边缘有内边距, 在使用裁剪边界时,这些标签与文本或文本之间不会显示。

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

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

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

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

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

    图 3. 带有光学边界的 Holo 按钮 9-patch 的放大视图。

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

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

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

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

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

    Rect 值的动画

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

    窗口附加和焦点监听器

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

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

    电视过扫描支持

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

    屏幕方向

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

    "userLandscape"
    "sensorLandscape" 的行为相同,但如果用户停用自动旋转,则会锁定在正常横屏模式,并且不会翻转。
    "userPortrait"
    行为与 "sensorPortrait" 相同,除非用户随后停用了自动屏幕旋转功能 它就会锁定在正常的纵向方向,而不会翻转。
    "fullUser"
    行为与 "fullSensor" 相同,并且允许向所有四个方向旋转,但以下四个方向除外: 如果用户停用自动屏幕旋转,屏幕将锁定在用户首选方向上。

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

    旋转动画

    rotationAnimation 字段 WindowManager 可让您从自己设置的三种动画中选择其一 。这三种动画分别是:

    注意:只有在您将 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 检查联系人更改的示例代码,请在 ApiDemos 中查找 SDK 示例下载中提供了示例。

    本地化

    改进了对双向文本的支持

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

    例如,如果您想要创建一个具有字符串变量的句子,如“Did you mean 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() 会使用 第一强方向性估计启发式算法,如果第一个是 指示文本方向的信号并未表示整体内容的合适方向。 如果需要,您可以通过将 TextDirectionHeuristics 中的某个 TextDirectionHeuristic 常量传递给 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 开始,无障碍服务必须声明无障碍功能 ,才能使用某些无障碍功能。如果未在元数据文件中请求该 capability,则该功能将不执行任何操作。如需声明服务的无障碍功能,您必须使用与 AccessibilityServiceInfo 类中的各种“capability”常量对应的 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 中提供自定义 Java 安全提供程序 名为“Android 密钥库”的工具 可能只有您的应用可以查看和使用。要加载 Android 密钥库,请将 将"AndroidKeyStore"更改为KeyStore.getInstance()

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

    硬件凭据存储

    Android 现在还支持由硬件支持的存储空间,供您的 KeyChain 使用 凭据,通过使密钥无法提取来提高安全性。也就是说,密钥进入硬件支持的密钥库(安全元件、TPM 或 TrustZone)后,可以用于加密操作,但私钥材料无法导出。甚至操作系统内核 无法访问此密钥材料。虽然并非所有 Android 设备都支持 则可通过调用 KeyChain.IsBoundKeyAlgorithm()

    清单声明

    可声明的必备功能

    现在支持在 <uses-feature> 元素中使用以下值,以便您确保只在提供应用所需功能的设备上安装您的应用。

    FEATURE_APP_WIDGETS
    声明您的应用提供应用微件,并且应仅安装在符合以下条件的设备上 提供可供用户嵌入应用微件的主屏幕或类似位置。 示例:
    <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 差异报告