使用收藏夹保持井井有条 根据您的偏好保存和分类内容。

有关已添加、已修改和已移除 API 的详细列表,请阅读 API 差异报告。有关已添加 API 的详细信息,请访问 Android API 参考 — 对于 Android 15,请查找 API 级别 35 中添加的 API。要了解平台变更可能影响您应用的领域,请务必查看 Android 15 行为变更,包括面向 Android 15 的应用面向所有应用

摄像头和媒体

Android 15 包含多种功能,可改善摄像头和媒体体验,并让您可以使用工具和硬件来支持创作者在 Android 上实现他们的愿景。

有关 Android 媒体和摄像头最新功能和开发者解决方案的更多信息,请参阅 Google I/O 大会上的“构建现代 Android 媒体和摄像头体验”演讲。

低光增强

Android 15 引入了 低光增强,这是一种可用于 Camera 2夜间模式摄像头扩展 的自动曝光模式。低光增强可在低光条件下调整预览流的曝光。这与夜间模式摄像头扩展创建静止图像的方式不同,因为夜间模式会组合一系列照片以创建单个增强图像。虽然夜间模式非常适合创建静止图像,但它无法创建连续的帧流,而低光增强可以。因此,低光增强支持以下摄像头功能,例如:

提供增强的图像预览,以便用户更好地构图低光照片

  • 在低光下扫描二维码
  • 如果您启用低光增强,它会在光线不足时自动开启,并在光线充足时关闭。

应用可以在低光条件下从预览流录制,以保存提亮的视频。

注意:由于低光增强使用与夜间模式静止拍摄不同的机制,因此两张图像看起来不会完全相同。如果您只想拍摄一张图像,夜间模式静止拍摄会提供更好的结果,但低光增强能够实时显示增强图像。

应用内摄像头控制

Android 15 添加了一个扩展,可在支持的设备上提供对摄像头硬件及其算法的更多控制。

高级闪光强度调整,可在拍摄图像时精确控制 SINGLETORCH 模式下的闪光强度。

  • HDR 动态余量控制

Android 15 会选择适合底层设备功能和面板位深度的 HDR 动态余量。对于包含大量 SDR 内容的页面(例如显示单个 HDR 缩略图的消息应用),此行为可能会对 SDR 内容的感知亮度产生不利影响。Android 15 允许您使用 setDesiredHdrHeadroom 控制 HDR 动态余量,以在 SDR 和 HDR 内容之间取得平衡。

左侧屏幕上 SDR UI 元素的亮度看起来比右侧屏幕上的亮度更均匀,这模拟了 HDR 和 SDR 内容混合时可能出现的动态余量问题。通过调整 HDR 动态余量,您可以在 SDR 和 HDR 内容之间实现更好的平衡。

响度控制

Android 15 引入了对 CTA-2075 响度标准的支持,以帮助您避免音频响度不一致,并确保用户在切换内容时无需不断调整音量。系统利用输出设备(耳机和扬声器)的已知特性以及 AAC 音频内容中可用的响度元数据,智能地调整音频响度和动态范围压缩级别。

要启用此功能,您需要确保 AAC 内容中存在响度元数据,并在应用中启用平台功能。为此,您可以通过调用 LoudnessCodecController 对象的 create 工厂方法(传入关联的 AudioTrack 中的音频会话 ID)来实例化它;这会自动开始应用音频更新。您可以传入 OnLoudnessCodecUpdateListener 以在将响度参数应用于 MediaCodec 之前对其进行修改或过滤。

AndroidX media3 ExoPlayer 也将更新为使用 LoudnessCodecController API,以实现无缝应用集成。

// Media contains metadata of type MPEG_4 OR MPEG_D
val mediaCodec = 
val audioTrack = AudioTrack.Builder()
                                .setSessionId(sessionId)
                                .build()
...
// Create new loudness controller that applies the parameters to the MediaCodec
try {
   val lcController = LoudnessCodecController.create(mSessionId)
   // Starts applying audio updates for each added MediaCodec
}

虚拟 MIDI 2.0 设备

Android 13 增加了对连接到通过 USB 使用 MIDI 2.0 设备的支持,这些设备使用通用 MIDI 数据包 (UMP)。Android 15 将 UMP 支持扩展到虚拟 MIDI 应用,使作曲应用能够像使用 USB MIDI 2.0 设备一样,将合成器应用作为虚拟 MIDI 2.0 设备进行控制。

更高效的 AV1 软件解码

dav1d,VideoLAN 旗下流行的 AV1 软件解码器,现已可用于不支持硬件 AV1 解码的 Android 设备。dav1d 的性能比旧版 AV1 软件解码器高出多达 3 倍,使更多用户(包括一些低端和中端设备)能够播放高清 AV1。

dav1d logo

您的应用需要通过名称 "c2.android.av1-dav1d.decoder" 调用 dav1d 来选择使用它。dav1d 将在后续更新中成为默认的 AV1 软件解码器。此支持已标准化并向后移植到接收 Google Play 系统更新的 Android 11 设备。

开发者生产力与工具

尽管我们提高生产力的大部分工作都围绕着 Android StudioJetpack ComposeAndroid Jetpack 库等工具展开,但我们始终在寻找平台中的方法,以帮助您更轻松地实现您的愿景。

OpenJDK 17 更新

Android 15 继续更新 Android 核心库,以与最新 OpenJDK LTS 版本中的功能保持一致。

包含以下关键功能和改进:

NIO 缓冲区的便捷性改进

PDF 改进

Android 15 对 PdfRenderer API 进行了大幅改进。应用可以集成高级功能,例如渲染密码保护文件、注释、表单编辑搜索和带复制的选择。支持线性化 PDF 优化,以加快本地 PDF 查看速度并减少资源使用。 Jetpack PDF 库使用这些 API 来简化向您的应用添加 PDF 查看功能。

PDF 渲染的最新更新包括搜索嵌入式 PDF 文件等功能。

PdfRenderer 已移至一个模块,该模块可以使用 Google Play 系统更新独立于平台发布进行更新,并且我们通过创建兼容的 Android 15 之前的 API 表面版本(称为 PdfRendererPreV)来支持将这些更改回溯到 Android 11(API 级别 30)。

自动语言切换改进

Android 14 在音频中添加了设备端多语言识别和自动语言切换功能,但这可能导致单词丢失,尤其是在两种发音之间停顿较少时进行语言切换。Android 15 添加了额外的控制功能,以帮助应用根据其用例调整此切换。 EXTRA_LANGUAGE_SWITCH_INITIAL_ACTIVE_DURATION_TIME_MILLIS 将自动切换限制在音频会话开始时,而 EXTRA_LANGUAGE_SWITCH_MATCH_SWITCHES 则在达到指定切换次数后停用语言切换。如果预计会话期间只说一种语言且应自动检测,这些选项特别有用。

改进的 OpenType 可变字体 API

Android 15 改进了 OpenType 可变字体的可用性。您可以使用 buildVariableFamily API 从可变字体创建 FontFamily 实例,而无需指定字重轴。文本渲染器会覆盖 wght 轴的值以匹配显示的文本。

使用该 API 可以大大简化创建 Typeface 的代码。

Kotlin

Java

val newTypeface = Typeface.CustomFallbackBuilder(
            FontFamily.Builder(
                Font.Builder(assets, "RobotoFlex.ttf").build())
                    .buildVariableFamily())
    .build()

以前,要创建相同的 Typeface,您需要更多的代码。

Typeface newTypeface = Typeface.CustomFallbackBuilder(
            new FontFamily.Builder(
                new Font.Builder(assets, "RobotoFlex.ttf").build())
                    .buildVariableFamily())
    .build();

以下是使用旧 API 和新 API 创建的 Typeface 的渲染示例:

Java

val oldTypeface = Typeface.CustomFallbackBuilder(
            FontFamily.Builder(
                Font.Builder(assets, "RobotoFlex.ttf")
                    .setFontVariationSettings("'wght' 400")
                    .setWeight(400)
                    .build())
                .addFont(
                    Font.Builder(assets, "RobotoFlex.ttf")
                        .setFontVariationSettings("'wght' 100")
                        .setWeight(100)
                        .build()
                )
                .addFont(
                    Font.Builder(assets, "RobotoFlex.ttf")
                        .setFontVariationSettings("'wght' 200")
                        .setWeight(200)
                        .build()
                )
                .addFont(
                    Font.Builder(assets, "RobotoFlex.ttf")
                        .setFontVariationSettings("'wght' 300")
                        .setWeight(300)
                        .build()
                )
                .addFont(
                    Font.Builder(assets, "RobotoFlex.ttf")
                        .setFontVariationSettings("'wght' 500")
                        .setWeight(500)
                        .build()
                )
                .addFont(
                    Font.Builder(assets, "RobotoFlex.ttf")
                        .setFontVariationSettings("'wght' 600")
                        .setWeight(600)
                        .build()
                )
                .addFont(
                    Font.Builder(assets, "RobotoFlex.ttf")
                        .setFontVariationSettings("'wght' 700")
                        .setWeight(700)
                        .build()
                )
                .addFont(
                    Font.Builder(assets, "RobotoFlex.ttf")
                        .setFontVariationSettings("'wght' 800")
                        .setWeight(800)
                        .build()
                )
                .addFont(
                    Font.Builder(assets, "RobotoFlex.ttf")
                        .setFontVariationSettings("'wght' 900")
                        .setWeight(900)
                        .build()
                ).build()
        ).build()

以前,要创建相同的 Typeface,您需要更多的代码。

Typeface oldTypeface = new Typeface.CustomFallbackBuilder(
    new FontFamily.Builder(
        new Font.Builder(assets, "RobotoFlex.ttf")
            .setFontVariationSettings("'wght' 400")
            .setWeight(400)
            .build()
    )
    .addFont(
        new Font.Builder(assets, "RobotoFlex.ttf")
            .setFontVariationSettings("'wght' 100")
            .setWeight(100)
            .build()
    )
    .addFont(
        new Font.Builder(assets, "RobotoFlex.ttf")
            .setFontVariationSettings("'wght' 200")
            .setWeight(200)
            .build()
    )
    .addFont(
        new Font.Builder(assets, "RobotoFlex.ttf")
            .setFontVariationSettings("'wght' 300")
            .setWeight(300)
            .build()
    )
    .addFont(
        new Font.Builder(assets, "RobotoFlex.ttf")
            .setFontVariationSettings("'wght' 500")
            .setWeight(500)
            .build()
    )
    .addFont(
        new Font.Builder(assets, "RobotoFlex.ttf")
            .setFontVariationSettings("'wght' 600")
            .setWeight(600)
            .build()
    )
    .addFont(
        new Font.Builder(assets, "RobotoFlex.ttf")
            .setFontVariationSettings("'wght' 700")
            .setWeight(700)
            .build()
    )
    .addFont(
        new Font.Builder(assets, "RobotoFlex.ttf")
            .setFontVariationSettings("'wght' 800")
            .setWeight(800)
            .build()
    )
    .addFont(
        new Font.Builder(assets, "RobotoFlex.ttf")
            .setFontVariationSettings("'wght' 900")
            .setWeight(900)
            .build()
    )
    .build()
).build();

在此示例中,使用旧 API 创建的 Typeface 无法为 350、450、550 和 650 的 Font 实例创建准确的字重,因此渲染器会回退到最接近的字重。因此在这种情况下,会渲染 300 而不是 350,渲染 400 而不是 450,等等。相比之下,使用新 API 创建的 Typeface 会为给定字重动态创建 Font 实例,因此 350、450、550 和 650 也能够渲染出准确的字重。

An example of how Typeface rendering differs using new and old
APIs

细粒度换行控制

从 Android 15 开始,TextView 和底层断行器可以在同一行中保留给定部分文本,以提高可读性。您可以通过在字符串资源中使用 <nobreak> 标签或 createNoBreakSpan 来利用此断行自定义。同样,您可以通过使用 <nohyphen> 标签或 createNoHyphenationSpan 来防止单词断字。

例如,以下字符串资源不包含换行符,并且渲染时文本“Pixel 8 Pro.”会在不希望的位置断开:

相比之下,此字符串资源包含 <nobreak> 标签,该标签会包装短语“Pixel 8 Pro.”并防止换行:

<resources>
    <string name="pixel8pro">The power and brains behind Pixel 8 Pro.</string>
</resources>

这些字符串的渲染差异显示在以下图片中:

<resources>
    <string name="pixel8pro">The power and brains behind <nobreak>Pixel 8 Pro.</nobreak></string>
</resources>

一行文本的布局,其中短语“Pixel 8 Pro.”未使用 <nobreak> 标签包装。

相同一行文本的布局,其中短语“Pixel 8 Pro.”使用 <nobreak> 标签包装。
应用归档

Android 和 Google Play 去年宣布支持应用归档,允许用户通过部分删除设备上不常用的、通过 Google Play 上的 Android App Bundle 发布的应用程序来释放空间。Android 15 包含操作系统级别的应用归档和取消归档支持,使所有应用商店更容易实现此功能。

具有 REQUEST_DELETE_PACKAGES 权限的应用可以调用 PackageInstaller requestArchive 方法来请求归档已安装的应用包,这会删除 APK 和任何缓存文件,但会保留用户数据。归档的应用会通过 LauncherApps API 作为可显示的应用返回;用户会看到 UI 处理以突出显示这些应用已归档。如果用户点击归档的应用,负责的安装程序将收到 取消归档 的请求,并且可以通过 ACTION_PACKAGE_ADDED 广播监控恢复过程。

使用开发者选项在设备上启用 16 KB 模式

切换以 16KB 页面大小启动开发者选项,以 16 KB 模式启动设备。

从 Android 15 QPR1 开始,您可以使用某些设备上可用的开发者选项,以 16 KB 模式启动设备并执行设备端测试。在使用开发者选项之前,请前往“设置”->“系统”->“软件更新”并应用任何可用的更新。

此开发者选项适用于以下设备:

Pixel 8 和 8 Pro(Android 15 QPR1 或更高版本)

  • 警告:由于Android 15 QPR2 Beta 3 的已知问题,在安装 Android 15 QPR2 Beta 3 并以 16 KB 模式启动设备后,Pixel 8 设备上的触摸屏将无法工作。此问题不影响 Pixel 8 Pro 设备。

    Pixel 8a(Android 15 QPR1 或更高版本)

  • 警告:由于Android 15 QPR2 Beta 3 的已知问题,在安装 Android 15 QPR2 Beta 3 并以 16 KB 模式启动设备后,Pixel 8a 设备上的触摸屏将无法工作。

    Pixel 9、9 Pro 和 9 Pro XL(Android 15 QPR2 Beta 2 或更高版本)

  • 图形

Android 15 带来了最新的图形改进,包括 ANGLE 以及 Canvas 图形系统的全新功能。

Android GPU 访问现代化

Android 硬件自早期以来发展迅速,早期核心操作系统在单个 CPU 上运行,GPU 使用基于固定功能管道的 API 进行访问。自 Android 7.0(API 级别 24)以来,NDK 中提供了 Vulkan® 图形 API,它具有更低级别的抽象,能更好地反映现代 GPU 硬件,更好地扩展以支持多个 CPU 核心,并减少 CPU 驱动程序开销 — 从而提高应用性能。所有现代游戏引擎都支持 Vulkan。

Vulkan logo

Vulkan 是 Android 首选的 GPU 接口。因此,Android 15 包含 ANGLE 作为在 Vulkan 之上运行 OpenGL® ES 的可选层。转向 ANGLE 将标准化 Android OpenGL 实现,以提高兼容性,并在某些情况下提高性能。您可以通过在 Android 15 上启用开发者选项(设置 -> 系统 -> 开发者选项 -> 实验性:启用 ANGLE)来测试您的 OpenGL ES 应用与 ANGLE 的稳定性与性能。

Android ANGLE on Vulkan 路线图

作为简化我们 GPU 堆栈的一部分,未来我们将在更多新设备上将 ANGLE 作为 GL 系统驱动程序发布,并期望 OpenGL/ES 将仅通过 ANGLE 提供。话虽如此,我们计划继续支持所有设备上的 OpenGL ES

Roadmap of upcoming changes to the Android GPU APIs.

建议的后续步骤

使用开发者选项选择 OpenGL ES 的 ANGLE 驱动程序并测试您的应用。对于新项目,我们强烈建议将 Vulkan 用于 C/C++。

Canvas 改进

Android 15 继续对 Android 的 Canvas 图形系统进行现代化改造,增加了更多功能:

Matrix44 提供一个 4x4 矩阵,用于转换坐标,当您想要在 3D 中操作画布时应使用此矩阵。

  • clipShader 将当前剪辑与指定的着色器相交,而 clipOutShader 将剪辑设置为当前剪辑与着色器之差,每个都将着色器视为 alpha 遮罩。这有效地支持了复杂形状的绘制。
  • 性能和电池

Android 继续致力于帮助您提高应用性能和质量。Android 15 引入了 API,可帮助您更高效地执行应用中的任务,优化应用性能,并收集有关您应用的见解。

有关电池高效最佳实践、调试网络和功耗使用情况,以及 Android 15 和最新 Android 版本中我们如何提高后台工作电池效率的详细信息,请参阅 Google I/O 大会上的“提高 Android 后台工作电池效率”演讲。

ApplicationStartInfo API

在以前的 Android 版本中,应用启动一直是个谜。在您的应用中很难确定它是从冷启动、温启动还是热启动状态开始的。也很难知道您的应用在各种启动阶段花费了多长时间:派生进程、调用 onCreate、绘制第一帧等等。当您的 Application 类被实例化时,您无法知道应用是从广播、内容提供程序、作业、备份、启动完成、闹钟还是 Activity 启动的。

Android 15 上的 ApplicationStartInfo API 提供了所有这些以及更多功能。您甚至可以选择在流程中添加自己的时间戳,以帮助在一个地方收集计时数据。除了收集指标外,您还可以使用 ApplicationStartInfo 直接优化应用启动;例如,当您的应用由于广播而启动时,您可以消除 Application 类中与 UI 相关的库的昂贵实例化。

详细的应用大小信息

自 Android 8.0(API 级别 26)以来,Android 已包含 StorageStats.getAppBytes API,该 API 将应用的安装大小概括为单个字节数,它是 APK 大小、从 APK 中提取的文件大小以及设备上生成的文件(例如预先 (AOT) 编译的代码)的总和。这个数字在您的应用如何使用存储方面并不是很有洞察力。

Android 15 添加了 StorageStats.getAppBytesByDataType([type]) API,它让您可以深入了解您的应用如何占用所有这些空间,包括 APK 文件拆分、AOT 和加速相关代码、dex 元数据、库和引导配置文件。

应用管理分析

Android 15 包含 ProfilingManager 类,它允许您从应用内部收集分析信息,例如堆转储、堆配置文件、堆栈采样等。它通过提供的标签向您的应用提供回调,以识别输出文件,该文件会传递到您应用的文件目录。该 API 会进行速率限制,以最大程度地减少性能影响。

为了简化在应用中构建分析请求,我们建议使用相应的 Profiling AndroidX API,该 API 在 Core 1.15.0-rc01 或更高版本中可用。

SQLite 数据库改进

Android 15 引入了 SQLite API,这些 API 暴露了底层 SQLite 引擎的高级功能,可解决应用中可能出现的特定性能问题。这些 API 包含在 SQLite 更新到 3.44.3 版本中。

开发者应查阅SQLite 性能最佳实践,以充分利用其 SQLite 数据库,尤其是在处理大型数据库或运行延迟敏感查询时。

只读延迟事务:当发出只读事务(不包括写入语句)时,使用 beginTransactionReadOnly()beginTransactionWithListenerReadOnly(SQLiteTransactionListener) 来发出只读 DEFERRED 事务。此类事务可以彼此并行运行,如果数据库处于 WAL 模式,它们可以与 IMMEDIATEEXCLUSIVE 事务并行运行。

  • 行计数和 ID:添加了 API,用于检索更改的行数或最后插入的行 ID,而无需发出额外的查询。getLastChangedRowCount() 返回当前事务中最近的 SQL 语句插入、更新或删除的行数,而 getTotalChangedRowCount() 返回当前连接上的计数。getLastInsertRowId() 返回当前连接上最后插入的行的 rowid
  • 原始语句:发出原始 SQLite 语句,绕过便利封装器及其可能带来的任何额外处理开销。
  • Android 动态性能框架更新

Android 15 继续投资于 Android 动态性能框架 (ADPF),这是一组 API,允许游戏和性能密集型应用更直接地与 Android 设备的电源和散热系统交互。在受支持的设备上,Android 15 增加了 ADPF 功能:

提示会话的节能模式,用于指示其关联线程应优先考虑节能而不是性能,非常适合长时间运行的后台工作负载。

  • GPU 和 CPU 工作时长都可以在提示会话中报告,从而允许系统同时调整 CPU 和 GPU 频率,以最佳方式满足工作负载需求。
  • 热余量阈值,用于根据余量预测解释可能的热节流状态。
  • 要了解如何在您的应用和游戏中运用 ADPF 的更多信息,请参阅文档

Android 15 包含多种功能,可帮助应用开发者保护用户隐私。

隐私

屏幕录制检测

Android 15 添加了应用支持,以检测它们是否正在被录制。每当应用在屏幕录制中可见或不可见之间切换时,都会调用回调。如果注册进程的 UID 所拥有的活动正在被录制,则认为应用可见。这样,如果您的应用正在执行敏感操作,您可以告知用户他们正在被录制。

扩展的 IntentFilter 功能

val mCallback = Consumer<Int> { state ->
  if (state == SCREEN_RECORDING_STATE_VISIBLE) {
    // We're being recorded
  } else {
    // We're not being recorded
  }
}

override fun onStart() {
   super.onStart()
   val initialState =
      windowManager.addScreenRecordingCallback(mainExecutor, mCallback)
   mCallback.accept(initialState)
}

override fun onStop() {
    super.onStop()
    windowManager.removeScreenRecordingCallback(mCallback)
}

Android 15 通过 UriRelativeFilterGroup 内置了对更精确的 Intent 解析的支持,该组包含一组 UriRelativeFilter 对象,这些对象构成了一组必须满足的 Intent 匹配规则,包括 URL 查询参数、URL 片段以及阻止或排除规则。

这些规则可以在 AndroidManifest XML 文件中使用 <uri-relative-filter-group> 标签定义,该标签可以选择包含 android:allow 标签。这些标签可以包含使用现有数据标签属性以及 android:queryandroid:fragment 属性的 <data> 标签。

以下是 AndroidManifest 语法示例:

私密空间

<intent-filter android:autoVerify="true">
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.BROWSABLE" />
  <category android:name="android.intent.category.DEFAULT" />
  <data android:scheme="http" />
  <data android:scheme="https" />
  <data android:host="astore.com" />
  <uri-relative-filter-group>
    <data android:pathPrefix="/auth" />
    <data android:query="region=na" />
  </uri-relative-filter-group>
  <uri-relative-filter-group android:allow="false">
    <data android:pathPrefix="/auth" />
    <data android:query="mobileoptout=true" />
  </uri-relative-filter-group>
  <uri-relative-filter-group android:allow="false">
    <data android:pathPrefix="/auth" />
    <data android:fragmentPrefix="faq" />
  </uri-relative-filter-group>
</intent-filter>

私密空间可以解锁和锁定,以在设备上显示或隐藏敏感应用。

私密空间允许用户在其设备上创建一个独立空间,在那里他们可以通过额外的身份验证层保护敏感应用免受窥探。私密空间使用单独的用户配置文件。用户可以选择使用设备锁或私密空间的单独锁定因素。

私密空间中的应用会显示在启动器中的一个独立容器中,并且在私密空间锁定时会从最近使用的应用视图、通知、设置和其他应用中隐藏。用户生成和下载的内容(例如媒体或文件)以及帐户在私密空间和主空间之间是分开的。系统分享表照片选择器可用于在私密空间解锁时,授予应用访问跨空间内容的权限。

用户无法将现有应用及其数据移动到私密空间。相反,用户在私密空间中选择一个安装选项,以使用他们喜欢的任何应用商店安装应用。私密空间中的应用是作为主空间中任何应用的单独副本安装的(相同应用的新副本)。

当用户锁定私密空间时,配置文件会停止。当配置文件停止时,私密空间中的应用不再活跃,无法执行前台或后台活动,包括显示通知。

我们建议您使用私密空间测试您的应用,以确保您的应用按预期工作,特别是如果您的应用属于以下类别之一:

具有工作配置文件逻辑的应用,这些应用假设其应用中未在主配置文件中的任何已安装副本都在工作配置文件中。

当授予媒体权限的部分访问权限时,应用现在可以仅突出显示最近选择的照片和视频。此功能可以改善频繁请求访问照片和视频的应用的用户体验。要在您的应用中使用此功能,请在通过 ContentResolver 查询 MediaStore 时启用 QUERY_ARG_LATEST_SELECTION_ONLY 参数。

Android 上的隐私沙盒

Java

val externalContentUri = MediaStore.Files.getContentUri("external")

val mediaColumns = arrayOf(
   FileColumns._ID,
   FileColumns.DISPLAY_NAME,
   FileColumns.MIME_TYPE,
)

val queryArgs = bundleOf(
   // Return only items from the last selection (selected photos access)
   QUERY_ARG_LATEST_SELECTION_ONLY to true,
   // Sort returned items chronologically based on when they were added to the device's storage
   QUERY_ARG_SQL_SORT_ORDER to "${FileColumns.DATE_ADDED} DESC",
   QUERY_ARG_SQL_SELECTION to "${FileColumns.MEDIA_TYPE} = ? OR ${FileColumns.MEDIA_TYPE} = ?",
   QUERY_ARG_SQL_SELECTION_ARGS to arrayOf(
       FileColumns.MEDIA_TYPE_IMAGE.toString(),
       FileColumns.MEDIA_TYPE_VIDEO.toString()
   )
)

以前,要创建相同的 Typeface,您需要更多的代码。

Uri externalContentUri = MediaStore.Files.getContentUri("external");

String[] mediaColumns = {
    FileColumns._ID,
    FileColumns.DISPLAY_NAME,
    FileColumns.MIME_TYPE
};

Bundle queryArgs = new Bundle();
queryArgs.putBoolean(MediaStore.QUERY_ARG_LATEST_SELECTION_ONLY, true);
queryArgs.putString(MediaStore.QUERY_ARG_SQL_SORT_ORDER, FileColumns.DATE_ADDED + " DESC");
queryArgs.putString(MediaStore.QUERY_ARG_SQL_SELECTION, FileColumns.MEDIA_TYPE + " = ? OR " + FileColumns.MEDIA_TYPE + " = ?");
queryArgs.putStringArray(MediaStore.QUERY_ARG_SQL_SELECTION_ARGS, new String[] {
    String.valueOf(FileColumns.MEDIA_TYPE_IMAGE),
    String.valueOf(FileColumns.MEDIA_TYPE_VIDEO)
});

Android 15 包含了最新的 Android 广告服务扩展,其中整合了最新版本的Android 隐私沙盒。此新增功能是我们开发技术工作的一部分,旨在提高用户隐私并为移动应用提供有效、个性化的广告体验。我们的隐私沙盒页面提供了有关 Android 隐私沙盒开发者预览版和 Beta 版计划的更多信息,以帮助您入门。

健康连接

Android 15 集成了Android Health Connect 的最新扩展,这是一个安全且集中的平台,用于管理和共享应用收集的健康和健身数据。此更新增加了对健身营养、皮肤温度、训练计划等更多数据类型的支持。

皮肤温度跟踪允许用户从可穿戴设备或其他跟踪设备存储和共享更准确的温度数据。

训练计划是结构化的锻炼计划,旨在帮助用户实现其健身目标。训练计划支持包括各种完成目标和表现目标:

完成目标包括燃烧卡路里距离持续时间重复次数步数

应用屏幕共享

Android 15 支持应用屏幕共享,以便用户可以只共享或录制一个应用窗口,而不是整个设备屏幕。此功能最初在 Android 14 QPR2 中启用,包括 MediaProjection 回调,允许您的应用自定义应用屏幕共享体验。请注意,对于目标为 Android 14(API 级别 34)或更高版本的应用,每个 MediaProjection 捕获会话都需要用户同意

用户体验和系统 UI

Android 15 为应用开发者和用户提供了更多控制和灵活性,以配置他们的设备以满足其需求。

要了解如何利用 Android 15 的最新改进来提升应用的用户体验,请参阅 Google I/O 大会上的“改善 Android 应用的用户体验”演讲。

使用生成的预览 API 呈现更丰富的微件预览

在 Android 15 之前,提供微件选择器预览的唯一方法是指定静态图片或布局资源。这些预览通常与实际微件放置在主屏幕上时的外观大相径庭。此外,无法使用 Jetpack Glance 创建静态资源,因此 Glance 开发者必须截取其微件的屏幕截图或创建 XML 布局才能获得微件预览。

Android 15 增加了对生成的预览的支持。这意味着应用微件提供程序可以生成 RemoteViews 作为选择器预览,而不是静态资源。

应用可以将远程视图提供给微件选择器,以便它们可以更新选择器中的内容,使其更能代表用户将看到的内容。

推送 API

应用可以通过推送 API 提供生成的预览。应用可以在其生命周期的任何点提供预览,并且不会收到来自主机提供预览的明确请求。预览会持久存储在 AppWidgetService 中,主机可以按需请求它们。以下示例加载 XML 微件布局资源并将其设置为预览:

预期的流程是:

AppWidgetManager.getInstance(appContext).setWidgetPreview(
   ComponentName(
       appContext,
       SociaLiteAppWidgetReceiver::class.java
   ),
   AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN,
   RemoteViews("com.example", R.layout.widget_preview)
)

在任何时候,微件提供程序都会调用 setWidgetPreview。提供的预览会与其他提供程序信息一起持久存储在 AppWidgetService 中。

  1. setWidgetPreview 通过 AppWidgetHost.onProvidersChanged 回调通知主机预览已更新。作为响应,微件主机重新加载其所有提供程序信息。
  2. 显示微件预览时,主机检查 AppWidgetProviderInfo.generatedPreviewCategories,如果所选类别可用,则调用 AppWidgetManager.getWidgetPreview 以返回此提供程序保存的预览。
  3. 何时调用 setWidgetPreview

由于没有提供预览的回调,应用可以选择在运行时随时发送预览。更新预览的频率取决于微件的用例。

以下列表描述了预览用例的两个主要类别:

在其微件预览中显示真实数据(例如个性化或最新信息)的提供程序。这些提供程序可以在用户登录或在其应用中完成初始配置后设置预览。在此之后,他们可以设置一个定期任务,以他们选择的频率更新预览。此类微件的示例可以是照片、日历、天气或新闻微件。

  • 在预览中显示静态信息或不显示任何数据的快速操作微件的提供程序。这些提供程序可以在应用首次启动时设置一次预览。此类微件的示例包括驱动器快速操作微件或 Chrome 快捷方式微件。
  • 某些提供程序可能会在中心模式选择器上显示静态预览,但在主屏幕选择器上显示真实信息。这些提供程序应遵循这两种用例的指导来设置预览。

画中画

Android 15 引入了画中画 (PiP) 的更改,确保进入 PiP 模式时过渡更流畅。这对于在主 UI 上叠加 UI 元素(并进入 PiP)的应用将很有益。

开发者使用 onPictureInPictureModeChanged 回调来定义切换叠加 UI 元素可见性的逻辑。此回调在 PiP 进入或退出动画完成时触发。从 Android 15 开始,PictureInPictureUiState 类包含另一个状态。

有了这个 UI 状态,面向 Android 15(API 级别 35)的应用将观察到 Activity#onPictureInPictureUiStateChanged 回调在 PiP 动画开始时立即使用 isTransitioningToPip() 调用。当应用处于 PiP 模式时,许多 UI 元素是不相关的,例如包含建议、即将推出的视频、评级和标题等信息的视图或布局。当应用进入 PiP 模式时,使用 onPictureInPictureUiStateChanged 回调隐藏这些 UI 元素。当应用从 PiP 窗口进入全屏模式时,使用 onPictureInPictureModeChanged 回调取消隐藏这些元素,如以下示例所示:

这种不相关 UI 元素(针对 PiP 窗口)的快速可见性切换有助于确保 PiP 进入动画更流畅、无闪烁。

override fun onPictureInPictureUiStateChanged(pipState: PictureInPictureUiState) {
        if (pipState.isTransitioningToPip()) {
          // Hide UI elements
        }
    }
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean) {
        if (isInPictureInPictureMode) {
          // Unhide UI elements
        }
    }

改进的请勿打扰规则

AutomaticZenRule 允许应用自定义注意力管理(请勿打扰)规则并决定何时激活或停用它们。Android 15 大幅增强了这些规则,旨在改善用户体验。以下增强功能包括:

AutomaticZenRule 添加类型,允许系统对某些规则应用特殊处理。

  • AutomaticZenRule 添加图标,有助于使模式更具辨识度。
  • AutomaticZenRule 添加一个 triggerDescription 字符串,描述该规则应何时对用户激活的条件。
  • AutomaticZenRule 添加了 ZenDeviceEffects,允许规则触发灰度显示、夜间模式或壁纸变暗等功能。
  • 为通知渠道设置 VibrationEffect

Android 15 支持使用 NotificationChannel.setVibrationEffect 按渠道设置传入通知的丰富振动效果,因此您的用户无需查看设备即可区分不同类型的通知。

媒体投射状态栏芯片和自动停止

注意:此功能包含在运行 Android 15 QPR1 或更高版本的设备上。

用于屏幕共享、投射和录制的状态栏芯片。

大屏幕和外形规格

Android 15 为您的应用提供支持,以充分利用 Android 的各种外形规格,包括大屏幕、翻盖式设备和可折叠设备。

改进的大屏幕多任务处理

Android 15 为用户提供了在大屏幕设备上进行多任务处理的更好方法。例如,用户可以保存他们最喜欢的分屏应用组合以便快速访问,并将任务栏固定在屏幕上以便在应用之间快速切换。这意味着确保您的应用具有自适应性比以往任何时候都更加重要。

Google I/O 提供了关于“构建自适应 Android 应用”“使用 Material 3 自适应库构建 UI”的会话,这些可以提供帮助,并且我们的文档中还有更多内容可以帮助您设计大屏幕

副屏支持

您的应用可以声明一个属性,Android 15 使用该属性允许您的 ApplicationActivity 在受支持的翻盖设备的较小副屏上显示。这些屏幕太小,不适合作为 Android 应用的兼容运行目标,但您的应用可以选择支持它们,从而使您的应用在更多地方可用。

Android 15 更新了平台,使您的应用能够访问通信和无线技术的最新进展。

连接性

卫星支持

Android 15 继续扩展对卫星连接的平台支持,并包含一些 UI 元素,以确保在卫星连接领域提供一致的用户体验。

应用可以使用 ServiceState.isUsingNonTerrestrialNetwork() 来检测设备何时连接到卫星,从而让他们更清楚地了解为什么可能无法使用完整的网络服务。此外,Android 15 还支持 SMS 和 MMS 应用以及预加载的 RCS 应用使用卫星连接发送和接收消息。

当设备连接到卫星时,会出现通知。

更流畅的 NFC 体验

Android 15 致力于使“轻触支付”体验更无缝、更可靠,同时继续支持 Android 强大的 NFC 应用生态系统。在受支持的设备上,应用可以请求 NfcAdapter 进入观察模式,在该模式下,设备会监听但不响应 NFC 阅读器,将应用的 NFC 服务 PollingFrame 对象发送以供处理。PollingFrame 对象可用于在与 NFC 阅读器进行首次通信之前进行身份验证,从而在许多情况下实现一键式交易。

此外,应用可以在受支持的设备上注册过滤器,以便它们能够收到轮询循环活动的通知,从而实现与多个支持 NFC 的应用进行流畅操作。

钱包角色

Android 15 引入了“钱包”角色,允许与用户首选的钱包应用更紧密地集成。此角色取代了 NFC 默认非接触式支付设置。用户可以通过导航到设置 > 应用 > 默认应用来管理钱包角色持有者。

“钱包”角色用于路由支付类别中注册的 AID 的 NFC 点击。除非同一 AID 的另一个应用正在前台运行,否则点击总是指向“钱包”角色持有者。

此角色还用于确定激活时钱包快速访问磁贴应放置在何处。当角色设置为“无”时,快速访问磁贴不可用,并且支付类别的 NFC 点击仅传递到前台应用。

Android 15 帮助您增强应用安全性,保护应用数据,并为用户提供更多数据透明度和控制权。请参阅 Google I/O 大会上的“保护 Android 用户安全”演讲,了解我们为改善用户保护和保护您的应用免受新威胁所做的更多工作。

安全

将凭据管理器与自动填充集成

从 Android 15 开始,开发者可以将用户名或密码字段等特定视图与凭据管理器请求关联起来,从而更轻松地在登录过程中提供量身定制的用户体验。当用户关注其中一个视图时,相应的请求将发送到凭据管理器。生成的凭据将跨提供程序聚合,并显示在自动填充回退 UI 中,例如内联建议或下拉建议。Jetpack androidx.credentials 库是开发者首选的端点,并将很快可用,以进一步增强 Android 15 及更高版本中的此功能。

将单点触控注册和登录与生物识别提示集成

凭据管理器将生物识别提示集成到凭据创建和登录流程中,从而消除了提供程序管理生物识别提示的需要。因此,凭据提供程序只需关注创建和获取流程的结果,并辅以生物识别流程结果。这种简化的流程创建了更高效、更精简的凭据创建和检索过程。

端到端加密的密钥管理

我们在 Android 15 中引入了 E2eeContactKeysManager,它通过提供用于存储加密公钥的 OS 级别 API,促进您的 Android 应用中的端到端加密 (E2EE)。

E2eeContactKeysManager 旨在与平台联系人应用集成,为用户提供集中管理和验证其联系人公钥的方式。

对内容 URI 的权限检查

Android 15 引入了一组对内容 URI 执行权限检查的 API:

Context.checkContentUriPermissionFull:它对内容 URI 执行完整的权限检查。

  • Activity 清单属性 requireContentUriPermissionFromCaller:它在活动启动时对提供的内容 URI 强制执行指定的权限。
  • Activity 调用者的 ComponentCaller:它代表启动活动的应用程序。
  • Android 15 增加了提高用户无障碍功能的功能。

无障碍功能

改进的盲文支持

在 Android 15 中,我们使 TalkBack 能够支持通过 USB 和安全蓝牙使用 HID 标准的盲文显示器。

此标准与鼠标和键盘所用的标准类似,将有助于 Android 随着时间的推移支持更广泛的盲文显示器。

国际化

Android 15 增加了功能和能力,可在设备使用不同语言时提升用户体验。

CJK 可变字体

从 Android 15 开始,中文、日文和韩文 (CJK) 语言的字体文件 NotoSansCJK 现在是一种可变字体。可变字体为 CJK 语言的创意排版开辟了可能性。设计师可以探索更广泛的样式,并创建以前难以或不可能实现的视觉效果出众的布局。

中文、日文和韩文 (CJK) 语言的可变字体在不同字体宽度下的显示方式。

字间对齐

从 Android 15 开始,文本可以通过使用 JUSTIFICATION_MODE_INTER_CHARACTER 利用字母间距进行对齐。单词间对齐首次在 Android 8.0(API 级别 26)中引入,而字间对齐为使用空白字符进行分段的语言(例如中文、日文等)提供了类似的功能。

使用 JUSTIFICATION_MODE_NONE 的日文文本布局。

使用 JUSTIFICATION_MODE_NONE 的英文文本布局。
使用 JUSTIFICATION_MODE_INTER_WORD 的日文文本布局。


使用 JUSTIFICATION_MODE_INTER_WORD 的英文文本布局。
使用 JUSTIFICATION_MODE_INTER_CHARACTER 的日文文本布局。


使用 JUSTIFICATION_MODE_INTER_CHARACTER 的英文文本布局。
自动换行配置

Android 从 Android 13(API 级别 33)开始支持日语和韩语的基于短语的换行。然而,虽然基于短语的换行可以提高短文本行的可读性,但它们对长文本行效果不佳。在 Android 15 中,应用可以使用 LINE_BREAK_WORD_STYLE_AUTO 选项仅对短文本行应用基于短语的换行。此选项会为文本选择最佳单词样式选项。

对于短文本行,使用基于短语的换行,其功能与 LINE_BREAK_WORD_STYLE_PHRASE 相同,如下图所示:

对于短文本行,LINE_BREAK_WORD_STYLE_AUTO 应用基于短语的换行以提高文本的可读性。这与应用 LINE_BREAK_WORD_STYLE_PHRASE 相同。

对于长文本行,LINE_BREAK_WORD_STYLE_AUTO 使用无换行单词样式,其功能与 LINE_BREAK_WORD_STYLE_NONE 相同,如下图所示:

对于长文本行,LINE_BREAK_WORD_STYLE_AUTO 应用无换行单词样式以提高文本的可读性。这与应用 LINE_BREAK_WORD_STYLE_NONE 相同。

其他日文变体假名字体

在 Android 15 中,旧日文平假名(称为变体假名)的字体文件默认捆绑。变体假名字符的独特形状可以为艺术品或设计增添独特的风格,同时也有助于准确传输和理解古老的日文文献。

日文变体假名字体的字符和文本样式。

VideoLAN 圆锥体版权所有 (c) 1996-2010 VideoLAN。任何人都可以使用或修改此徽标或其修改版本,以指代 VideoLAN 项目或 VideoLAN 团队开发的任何产品,但这不表示项目认可。

Vulkan 和 Vulkan 徽标是 Khronos Group Inc. 的注册商标。

OpenGL 是 Hewlett Packard Enterprise 的注册商标,OpenGL ES 徽标是 Hewlett Packard Enterprise 的商标,经 Khronos 许可使用。

本页面上的内容和代码示例受内容许可中描述的许可条款约束。Java 和 OpenJDK 是 Oracle 和/或其关联公司的商标或注册商标。