Cambios en el comportamiento: apps orientadas a Android 15 o versiones posteriores

Al igual que las versiones anteriores, Android 15 incluye cambios de comportamiento que podrían afectar tu app. Los siguientes cambios se aplican exclusivamente a las apps orientadas a Android 15 o versiones posteriores. Si tu app está orientada a Android 15 o versiones posteriores, debes modificarla para que admita estos comportamientos correctamente, cuando corresponda.

Asegúrate de revisar también la lista de cambios de comportamiento que afectan a todas las apps que se ejecutan en Android 15, independientemente de targetSdkVersion de la app.

Funcionalidad principal

Android 15 modifica o expande varias funciones principales del sistema Android.

Cambios en los servicios en primer plano

Realizaremos los siguientes cambios en los servicios en primer plano con Android 15.

Comportamiento del tiempo de espera del servicio en primer plano de sincronización de datos

对于以 Android 15(API 级别 35)或更高版本为目标平台的应用,Android 15 为 dataSync 引入了新的超时行为。此行为也适用于新的 mediaProcessing 前台服务类型

系统允许应用的 dataSync 服务在 24 小时内共运行 6 小时,之后系统会调用正在运行的服务的 Service.onTimeout(int, int) 方法(在 Android 15 中引入)。此时,服务有几秒钟的时间来调用 Service.stopSelf()。调用 Service.onTimeout() 后,该服务将不再被视为前台服务。如果服务未调用 Service.stopSelf(),系统会抛出内部异常。系统会在 Logcat 中记录此异常,并显示以下消息:

Fatal Exception: android.app.RemoteServiceException: "A foreground service of
type dataSync did not stop within its timeout: [component name]"

为避免此行为变更出现问题,您可以执行以下一项或多项操作:

  1. 让您的服务实现新的 Service.onTimeout(int, int) 方法。当应用收到回调时,请务必在几秒钟内调用 stopSelf()。(如果您不立即停止应用,系统会生成故障。)
  2. 确保应用的 dataSync 服务在任何 24 小时内总运行时间不超过 6 小时(除非用户与应用互动,重置计时器)。
  3. 仅在有直接用户互动时启动 dataSync 前台服务;由于服务启动时应用位于前台,因此您的服务在应用进入后台后有完整的 6 小时时间。
  4. 请使用替代 API,而不是使用 dataSync 前台服务。

如果您的应用的 dataSync 前台服务在过去 24 小时内运行了 6 小时,则您无法启动其他 dataSync 前台服务,除非用户已将您的应用切换到前台(这会重置计时器)。如果您尝试启动其他 dataSync 前台服务,系统会抛出 ForegroundServiceStartNotAllowedException,并显示类似“前台服务类型 dataSync 的时间限制已用尽”的错误消息。

测试

如需测试应用的行为,即使您的应用并非以 Android 15 为目标平台(只要该应用在 Android 15 设备上运行),您也可以启用数据同步超时。如需启用超时,请运行以下 adb 命令:

adb shell am compat enable FGS_INTRODUCE_TIME_LIMITS your-package-name

您还可以调整超时期限,以便更轻松地测试应用在达到上限时的行为方式。如需设置新的超时期限,请运行以下 adb 命令:

adb shell device_config put activity_manager data_sync_fgs_timeout_duration duration-in-milliseconds

Nuevo tipo de servicio en primer plano de procesamiento de contenido multimedia

Android 15 introduces a new foreground service type, mediaProcessing. This service type is appropriate for operations like transcoding media files. For example, a media app might download an audio file and need to convert it to a different format before playing it. You can use a mediaProcessing foreground service to make sure the conversion continues even while the app is in the background.

The system permits an app's mediaProcessing services to run for a total of 6 hours in a 24-hour period, after which the system calls the running service's Service.onTimeout(int, int) method (introduced in Android 15). At this time, the service has a few seconds to call Service.stopSelf(). If the service does not call Service.stopSelf(), the system throws an internal exception. The exception is logged in Logcat with the following message:

Fatal Exception: android.app.RemoteServiceException: "A foreground service of
type mediaProcessing did not stop within its timeout: [component name]"

To avoid having the exception, you can do one of the following:

  1. Have your service implement the new Service.onTimeout(int, int) method. When your app receives the callback, make sure to call stopSelf() within a few seconds. (If you don't stop the app right away, the system generates a failure.)
  2. Make sure your app's mediaProcessing services don't run for more than a total of 6 hours in any 24-hour period (unless the user interacts with the app, resetting the timer).
  3. Only start mediaProcessing foreground services as a result of direct user interaction; since your app is in the foreground when the service starts, your service has the full six hours after the app goes to the background.
  4. Instead of using a mediaProcessing foreground service, use an alternative API, like WorkManager.

If your app's mediaProcessing foreground services have run for 6 hours in the last 24, you cannot start another mediaProcessing foreground service unless the user has brought your app to the foreground (which resets the timer). If you try to start another mediaProcessing foreground service, the system throws ForegroundServiceStartNotAllowedException with an error message like "Time limit already exhausted for foreground service type mediaProcessing".

For more information about the mediaProcessing service type, see Changes to foreground service types for Android 15: Media processing.

Testing

To test your app's behavior, you can enable media processing timeouts even if your app is not targeting Android 15 (as long as the app is running on an Android 15 device). To enable timeouts, run the following adb command:

adb shell am compat enable FGS_INTRODUCE_TIME_LIMITS your-package-name

You can also adjust the timeout period, to make it easier to test how your app behaves when the limit is reached. To set a new timeout period, run the following adb command:

adb shell device_config put activity_manager media_processing_fgs_timeout_duration duration-in-milliseconds

Restricciones para los receptores de emisión BOOT_COMPLETED que inician servicios en primer plano

Hay nuevas restricciones para los receptores de emisión de BOOT_COMPLETED que se lanzan servicios en primer plano. Los receptores BOOT_COMPLETED no pueden iniciar los siguientes tipos de servicios en primer plano:

Si un receptor BOOT_COMPLETED intenta iniciar cualquiera de esos tipos de primer plano. servicios, el sistema arroja una ForegroundServiceStartNotAllowedException.

Prueba

Para probar el comportamiento de tu app, puedes habilitar estas nuevas restricciones, incluso si tu app no está segmentada para Android 15 (siempre que la app se ejecute en un dispositivo con Android 15). Ejecuta el siguiente comando adb:

adb shell am compat enable FGS_BOOT_COMPLETED_RESTRICTIONS your-package-name

Para enviar una transmisión de BOOT_COMPLETED sin reiniciar el dispositivo, haz lo siguiente: Ejecuta el siguiente comando adb:

adb shell am broadcast -a android.intent.action.BOOT_COMPLETED your-package-name

Restricciones para iniciar servicios en primer plano mientras una app tiene el permiso SYSTEM_ALERT_WINDOW

Anteriormente, si una app tenía el permiso SYSTEM_ALERT_WINDOW, podía iniciar un servicio en primer plano, incluso si la app estaba en segundo plano (como se explica en exenciones de las restricciones de inicio en segundo plano).

Si una app está orientada a Android 15, esta exención ahora es más limitada. Ahora, la app debe tener el permiso SYSTEM_ALERT_WINDOW y también tener una ventana superpuesta visible. Es decir, la app primero debe iniciar una ventana TYPE_APPLICATION_OVERLAY y la ventana debe ser visible antes de iniciar un servicio en primer plano.

Si tu app intenta iniciar un servicio en primer plano desde segundo plano sin cumplir con estos nuevos requisitos (y no tiene otra exención), el sistema arroja ForegroundServiceStartNotAllowedException.

Si tu app declara el permiso SYSTEM_ALERT_WINDOW y, luego, inicia servicios en primer plano desde el segundo plano, es posible que se vea afectada por este cambio. Si tu app obtiene una ForegroundServiceStartNotAllowedException, verifica el orden de operaciones de tu app y asegúrate de que ya tenga una ventana de superposición activa antes de intentar iniciar un servicio en primer plano desde el segundo plano. Puedes verificar si tu ventana de superposición es visible actualmente llamando a View.getWindowVisibility() o puedes anular View.onWindowVisibilityChanged() para recibir notificaciones cada vez que cambie la visibilidad.

Prueba

Para probar el comportamiento de tu app, puedes habilitar estas nuevas restricciones, incluso si tu app no está segmentada para Android 15 (siempre que la app se ejecute en un dispositivo con Android 15). Para habilitar estas nuevas restricciones para iniciar servicios en primer plano desde segundo plano, ejecuta el siguiente comando adb:

adb shell am compat enable FGS_SAW_RESTRICTIONS your-package-name

Se modificaron los momentos en que las apps pueden modificar el estado global del modo No interrumpir.

Las apps que se orientan a Android 15 (nivel de API 35) y versiones posteriores ya no pueden cambiar el estado o la política globales de No interrumpir (ND) en un dispositivo (ya sea modificando la configuración del usuario o desactivando el modo ND). En su lugar, las apps deben contribuir con un AutomaticZenRule, que el sistema combina en una política global con el esquema existente de política más restrictiva. Las llamadas a las APIs existentes que antes afectaban el estado global (setInterruptionFilter, setNotificationPolicy) generan la creación o actualización de un AutomaticZenRule implícito, que se activa o desactiva según el ciclo de llamadas de esas llamadas a la API.

Ten en cuenta que este cambio solo afecta el comportamiento observable si la app llama a setInterruptionFilter(INTERRUPTION_FILTER_ALL) y espera que esa llamada desactive un AutomaticZenRule que sus propietarios activaron anteriormente.

Cambios en la API de OpenJDK

Android 15 continúa la tarea de actualizar las bibliotecas principales de Android para alinearlas con las funciones de las versiones más recientes de LTS de OpenJDK.

Algunos de estos cambios pueden afectar la compatibilidad de las apps que se orientan a Android 15 (nivel de API 35):

  • Cambios en las APIs de formato de cadenas: La validación del índice de argumentos, las marcas, la precisión y el ancho ahora es más estricta cuando se usan las siguientes APIs de String.format() y Formatter.format():

    Por ejemplo, se genera la siguiente excepción cuando se usa un índice de argumento de 0 (%0 en la cadena de formato):

    IllegalFormatArgumentIndexException: Illegal format argument index = 0
    

    En este caso, el problema se puede solucionar con un índice de argumento de 1 (%1 en la cadena de formato).

  • Cambios en el tipo de componente de Arrays.asList(...).toArray(): Cuando se usa Arrays.asList(...).toArray(), el tipo de componente del array resultante ahora es Object, no el tipo de los elementos del array subyacente. Por lo tanto, el siguiente código arroja un ClassCastException:

    String[] elements = (String[]) Arrays.asList("one", "two").toArray();
    

    En este caso, para conservar String como el tipo de componente en el array resultante, puedes usar Collection.toArray(Object[]) en su lugar:

    String[] elements = Arrays.asList("two", "one").toArray(new String[0]);
    
  • Cambios en el manejo de códigos de idioma: Cuando usas la API de Locale, los códigos de idioma para hebreo, yiddish e indonesio ya no se convierten a sus formas obsoletas (hebreo: iw, yiddish: ji e indonesio: in). Cuando especifiques el código de idioma de una de estas configuraciones regionales, usa los códigos de ISO 639-1 (hebreo: he, yiddish: yi e indonesio: id).

  • Cambios en las secuencias de int aleatorias: Después de los cambios realizados en https://bugs.openjdk.org/browse/JDK-8301574, los siguientes métodos Random.ints() ahora muestran una secuencia de números diferente a la de los métodos Random.nextInt():

    Por lo general, este cambio no debería provocar un comportamiento que interrumpa la app, pero tu código no debería esperar que la secuencia generada a partir de los métodos Random.ints() coincida con Random.nextInt().

La nueva API de SequencedCollection puede afectar la compatibilidad de tu app después de que actualices compileSdk en la configuración de compilación de tu app para usar Android 15 (nivel de API 35):

  • Colisión con las funciones de extensión MutableList.removeFirst() y MutableList.removeLast() en kotlin-stdlib

    El tipo List en Java se asigna al tipo MutableList en Kotlin. Debido a que las APIs de List.removeFirst() y List.removeLast() se introdujeron en Android 15 (nivel de API 35), el compilador de Kotlin resuelve las llamadas a función, por ejemplo, list.removeFirst(), de forma estática a las nuevas APIs de List en lugar de a las funciones de extensión en kotlin-stdlib.

    Si una app se vuelve a compilar con compileSdk establecido en 35 y minSdk establecido en 34 o una versión anterior, y luego se ejecuta en Android 14 y versiones anteriores, se genera un error de tiempo de ejecución:

    java.lang.NoSuchMethodError: No virtual method
    removeFirst()Ljava/lang/Object; in class Ljava/util/ArrayList;
    

    La opción de lint NewApi existente en el complemento de Android para Gradle puede detectar estos nuevos usos de la API.

    ./gradlew lint
    
    MainActivity.kt:41: Error: Call requires API level 35 (current min is 34): java.util.List#removeFirst [NewApi]
          list.removeFirst()
    

    Para corregir la excepción de tiempo de ejecución y los errores de lint, las llamadas a las funciones removeFirst() y removeLast() se pueden reemplazar por removeAt(0) y removeAt(list.lastIndex), respectivamente, en Kotlin. Si usas Android Studio Ladybug | 2024.1.3 o versiones posteriores, también proporciona una opción de solución rápida para estos errores.

    Considera quitar @SuppressLint("NewApi") y lintOptions { disable 'NewApi' } si se inhabilitó la opción lint.

  • Colisión con otros métodos en Java

    Se agregaron métodos nuevos a los tipos existentes, por ejemplo, List y Deque. Es posible que estos métodos nuevos no sean compatibles con los métodos que tienen el mismo nombre y los mismos tipos de argumentos en otras interfaces y clases. En el caso de una colisión de firma de método con incompatibilidad, el compilador javac muestra un error de tiempo de compilación. Por ejemplo:

    Ejemplo de error 1:

    javac MyList.java
    
    MyList.java:135: error: removeLast() in MyList cannot implement removeLast() in List
      public void removeLast() {
                  ^
      return type void is not compatible with Object
      where E is a type-variable:
        E extends Object declared in interface List
    

    Ejemplo de error 2:

    javac MyList.java
    
    MyList.java:7: error: types Deque<Object> and List<Object> are incompatible;
    public class MyList implements  List<Object>, Deque<Object> {
      both define reversed(), but with unrelated return types
    1 error
    

    Ejemplo de error 3:

    javac MyList.java
    
    MyList.java:43: error: types List<E#1> and MyInterface<E#2> are incompatible;
    public static class MyList implements List<Object>, MyInterface<Object> {
      class MyList inherits unrelated defaults for getFirst() from types List and MyInterface
      where E#1,E#2 are type-variables:
        E#1 extends Object declared in interface List
        E#2 extends Object declared in interface MyInterface
    1 error
    

    Para corregir estos errores de compilación, la clase que implementa estas interfaces debe reemplazar el método con un tipo de devolución compatible. Por ejemplo:

    @Override
    public Object getFirst() {
        return List.super.getFirst();
    }
    

Seguridad

Android 15 incluye cambios que promueven la seguridad del sistema para ayudar a proteger a las apps y a los usuarios de las apps maliciosas.

Versiones de TLS restringidas

Android 15 restringe el uso de las versiones 1.0 y 1.1 de TLS. Estas versiones ya habían dejado de estar disponibles en Android, pero ahora no se permiten para las apps que se orientan a Android 15.

Inicios de actividades en segundo plano seguros

Android 15 可保护用户免受恶意应用的侵害,并让用户更好地控制 来防止恶意后台应用 将其他应用置于前台、提升其权限以及滥用 用户互动自以下时间以来,后台活动启动一直受到限制 Android 10(API 级别 29)。

禁止与堆栈中的顶部 UID 不匹配的应用启动 activity

恶意应用可以在同一任务中启动另一个应用的 activity,然后 叠加在上面,营造出像该应用一样的错觉。这个“任务” 劫持"攻击绕过了当前的后台启动限制, 会发生在同一个可见任务中。为了降低这种风险,Android 15 新增了 用于阻止与堆栈中的顶层 UID 不匹配的应用启动的标志 活动。如需选择启用应用的所有活动,请更新 allowCrossUidActivitySwitchFromBelow 属性:AndroidManifest.xml

<application android:allowCrossUidActivitySwitchFromBelow="false" >

如果满足以下所有条件,则启用新的安全措施:

  • 执行启动的应用以 Android 15 为目标平台。
  • 任务堆栈顶部的应用以 Android 15 为目标平台。
  • 所有可见活动都已选择启用新保护措施

如果启用了安全措施,应用可能会返回主屏幕,而不是返回 最后一个可见应用(如果他们自行完成任务)。

其他变更

除了限制 UID 匹配之外,这些其他变更也 包括:

  • 更改 PendingIntent 创作者,以阻止后台活动启动,具体方法是: 默认。这有助于防止应用意外创建 可能被恶意操作者滥用的 PendingIntent
  • 请勿将应用调到前台,除非 PendingIntent 发送者 允许它。此变更旨在防止恶意应用滥用 在后台启动 activity 的功能。默认情况下,应用 允许将任务堆栈转到前台,除非创建者允许 后台活动启动权限或发送者有后台活动 启动权限
  • 控制任务堆栈的顶层 activity 完成其任务的方式。如果 顶层 activity 完成一项任务后,Android 会返回到之前执行的 上次活动时间。此外,如果非顶层 activity 完成其任务,Android 将 返回主屏幕;因此不会阻碍这个非顶层的 活动。
  • 防止将其他应用中的任意 activity 启动到您自己的 activity 任务。这项变更旨在防止恶意应用 看起来像是来自其他应用的活动
  • 禁止将不可见窗口视为后台活动 发布。这有助于防止恶意应用滥用后台 activity 来向用户显示不需要或恶意的内容。

Intents más seguros

Android 15 presenta nuevas medidas de seguridad opcionales para que los intents sean más seguros y sólidos. El objetivo de estos cambios es evitar posibles vulnerabilidades y el uso inadecuado de intents que pueden aprovechar las apps maliciosas. Hay dos mejoras principales en la seguridad de los intents en Android 15:

  • Coincidir con filtros de intents de destino: Los intents que se orientan a componentes específicos deben coincidir de forma precisa con las especificaciones de filtros de intents del destino. Si envías un intent para iniciar la actividad de otra app, el componente de intent de destino debe alinearse con los filtros de intents declarados de la actividad receptora.
  • Los intents deben tener acciones: Los intents sin una acción ya no coincidirán con ningún filtro de intents. Esto significa que los intents que se usan para iniciar actividades o servicios deben tener una acción claramente definida.

Para verificar cómo responde tu app a estos cambios, usa StrictMode en tu app. Para ver registros detallados sobre los incumplimientos de uso de Intent, agrega el siguiente método:

Kotlin


fun onCreate() {
    StrictMode.setVmPolicy(VmPolicy.Builder()
        .detectUnsafeIntentLaunch()
        .build()
    )
}

Java


public void onCreate() {
    StrictMode.setVmPolicy(new VmPolicy.Builder()
            .detectUnsafeIntentLaunch()
            .build());
}

Experiencia del usuario y IU del sistema

Android 15 incluye algunos cambios que tienen como objetivo crear una experiencia del usuario más intuitiva y coerente.

Cambios en la inserción de ventana

Hay dos cambios relacionados con los insertos de ventana en Android 15: el modo de pantalla completa se aplica de forma predeterminada y también hay cambios de configuración, como la configuración predeterminada de las barras del sistema.

Aplicación de borde a borde

如果应用以 Android 15(API 级别 35)为目标平台,则在搭载 Android 15 的设备上默认以无边框显示。

以 Android 14 为目标平台且在 Android 15 设备上未采用边到边设计的应用。


以 Android 15(API 级别 35)为目标平台且在 Android 15 设备上采用边到边设计的应用。此应用主要使用会自动应用边衬区的 Material 3 Compose 组件。此屏幕不会受到 Android 15 强制执行的无边框措施的不利影响。

这是一项重大变更,可能会对应用的界面产生负面影响。这些变更会影响以下界面区域:

  • 手势处理程序导航栏
    • 默认透明。
    • 底部偏移量处于停用状态,因此除非应用边衬区,否则内容会绘制在系统导航栏后面。
    • setNavigationBarColorR.attr#navigationBarColor 已废弃,不会影响手势导航。
    • setNavigationBarContrastEnforcedR.attr#navigationBarContrastEnforced 对手势导航的影响仍然不变。
  • “三按钮”导航
    • 默认情况下,不透明度设置为 80%,颜色可能与窗口背景相匹配。
    • 底部偏移量处于停用状态,因此除非应用边衬区,否则内容会绘制在系统导航栏后面。
    • 默认情况下,setNavigationBarColorR.attr#navigationBarColor 会设置为与窗口背景相匹配。窗口背景必须是彩色可绘制对象,此默认值才能应用。此 API 已废弃,但仍会影响三按钮导航栏。
    • setNavigationBarContrastEnforcedR.attr#navigationBarContrastEnforced 默认均为 true,这会在三按钮导航栏中添加 80% 不透明的背景。
  • 状态栏
    • 默认透明。
    • 顶部偏移量处于停用状态,因此除非应用边衬区,否则内容会绘制在状态栏后面。
    • setStatusBarColorR.attr#statusBarColor 已废弃,对 Android 15 没有任何影响。
    • setStatusBarContrastEnforcedR.attr#statusBarContrastEnforced 已废弃,但对 Android 15 仍有影响。
  • 刘海屏
    • 非浮动窗口的 layoutInDisplayCutoutMode 必须为 LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYSSHORT_EDGESNEVERDEFAULT 会被解读为 ALWAYS,以便用户不会看到由显示屏缺口导致的黑条,并且显示屏会从边到边显示内容。

以下示例展示了应用在以 Android 15(API 级别 35)为目标平台之前和之后,以及应用内边距之前和之后的效果。

以 Android 14 为目标平台且在 Android 15 设备上未采用边到边设计的应用。
以 Android 15(API 级别 35)为目标平台且在 Android 15 设备上为端到端的应用。不过,由于 Android 15 强制执行边到边显示,许多元素现在都被状态栏、三按钮导航栏或显示屏缺口遮挡。隐藏的界面包括 Material 2 顶部应用栏、悬浮操作按钮和列表项。
以 Android 15(API 级别 35)为目标平台的应用在 Android 15 设备上从边到边,并应用内嵌,以免界面被隐藏。
如何检查应用是否已采用边到边设计

如果您的应用已经是边到边且应用了内边距,则除以下情况外,您大多不会受到影响。不过,即使您认为自己没有受到影响,我们也建议您测试应用。

  • 您有一个非浮动窗口,例如使用 SHORT_EDGESNEVERDEFAULT(而非 LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS)的 Activity。如果您的应用在启动时崩溃,这可能是因为您的启动画面存在问题。您可以将核心启动画面依赖项升级到 1.2.0-alpha01 或更高版本,也可以设置 window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutInDisplayCutoutMode.always
  • 有些流量较低的屏幕可能存在遮挡界面的情况。验证这些访问次数较少的屏幕是否存在遮挡的界面。流量较低的屏幕包括:
    • 初始配置或登录屏幕
    • “设置”页面
如果您的应用尚未采用边到边设计,应检查哪些方面

如果您的应用尚未采用边到边设计,您很可能受到影响。除了已经采用边到边设计的应用的场景之外,您还应考虑以下情况:

  • 如果您的应用在 Compose 中使用 Material 3 组件 (androidx.compose.material3),例如 TopAppBarBottomAppBarNavigationBar,这些组件可能不会受到影响,因为它们会自动处理边衬区。
  • 如果应用使用的是 Compose 中的 Material 2 组件 (androidx.compose.material),这些组件本身并不会自动处理边衬区。不过,您可以获得边衬区的访问权限,然后手动应用边衬区。在 androidx.compose.material 1.6.0 及更高版本中,使用 windowInsets 参数可为 BottomAppBarTopAppBarBottomNavigationNavigationRail 手动应用边衬区。同样,请为 Scaffold 使用 contentWindowInsets 参数。
  • 如果应用使用了 View 和 Material 组件 (com.google.android.material),则大多数基于 View 的 Material 组件(例如 BottomNavigationViewBottomAppBarNavigationRailViewNavigationView)都会处理边衬区,因此不需要执行额外的操作。不过,如果使用的是 AppBarLayout,则需要添加 android:fitsSystemWindows="true"
  • 对于自定义可组合项,请手动将边衬区应用为内边距。如果您的内容位于 Scaffold 中,您可以使用 Scaffold 内边距值使用内边距。否则,请使用 WindowInsets 之一应用内边距。
  • 如果应用使用的是 View 和 BottomSheetSideSheet 或自定义容器,请使用 ViewCompat.setOnApplyWindowInsetsListener 应用内边距。对于 RecyclerView,请使用此监听器应用内边距,同时添加 clipToPadding="false"
如果您的应用必须提供自定义后台保护,应检查哪些方面

如果您的应用必须为三按钮导航栏或状态栏提供自定义背景保护,则应使用 WindowInsets.Type#tappableElement() 在系统栏后面放置可组合项或视图,以获取三按钮导航栏高度或 WindowInsets.Type#statusBars

其他端到端资源

如需了解有关应用内边距的其他注意事项,请参阅边到边视图边到边 Compose 指南。

已弃用的 API

以下 API 已废弃,但并未停用:

以下 API 已废弃并停用:

Configuración estable

Si tu app está orientada a Android 15 (nivel de API 35) o versiones posteriores, Configuration ya no excluye las barras del sistema. Si usas el tamaño de pantalla en la clase Configuration para el cálculo del diseño, debes reemplazarlo por mejores alternativas, como un ViewGroup, WindowInsets o WindowMetricsCalculator apropiados según tus necesidades.

Configuration está disponible desde la API 1. Por lo general, se obtiene de Activity.onConfigurationChanged. Proporciona información como la densidad, orientación y tamaño de la ventana. Una característica importante de los tamaños de ventana que se muestran desde Configuration es que anteriormente excluía las barras del sistema.

El tamaño de configuración suele usarse para la selección de recursos, como /res/layout-h500dp, y este sigue siendo un caso de uso válido. Sin embargo, siempre se desaconsejó su uso para el cálculo del diseño. Si es así, debes alejarte ahora. Debes reemplazar el uso de Configuration por algo más adecuado según tu caso de uso.

Si lo usas para calcular el diseño, usa un ViewGroup adecuado, como CoordinatorLayout o ConstraintLayout. Si lo usas para determinar la altura de la barra de navegación del sistema, usa WindowInsets. Si quieres conocer el tamaño actual de la ventana de tu app, usa computeCurrentWindowMetrics.

En la siguiente lista, se describen los campos afectados por este cambio:

El atributo elegantTextHeight tiene el valor predeterminado de verdadero.

En el caso de las apps orientadas a Android 15 (nivel de API 35), el atributo elegantTextHeight TextView se convierte en true de forma predeterminada, lo que reemplaza la fuente compacta que se usa de forma predeterminada con algunas secuencias de comandos que tienen métricas verticales grandes por una que es mucho más legible. La fuente compacta se introdujo para evitar que se rompan los diseños. Android 13 (nivel de API 33) evita muchos de estos daños, ya que permite que el diseño de texto estire la altura vertical con el atributo fallbackLineSpacing.

En Android 15, la fuente compacta aún permanece en el sistema, por lo que tu app puede establecer elegantTextHeight en false para obtener el mismo comportamiento que antes, pero es probable que no sea compatible con las próximas versiones. Por lo tanto, si tu app admite las siguientes secuencias de comandos: árabe, lao, birmano, tamil, guyaratí, canarés, malabar, odía, telugu o tailandés, configura elegantTextHeight como true para probarla.

Comportamiento de elegantTextHeight para apps orientadas a Android 14 (nivel de API 34) y versiones anteriores.
Comportamiento de elegantTextHeight para apps orientadas a Android 15.

Cambios en el ancho de TextView para formas de letras complejas

En versiones anteriores de Android, algunas fuentes o idiomas en cursiva que tienen formas complejas podrían dibujar las letras en el área del carácter anterior o siguiente. En algunos casos, esas letras se cortaron al principio o al final. A partir de Android 15, un TextView asigna ancho para dibujar suficiente espacio para esas letras y permite que las apps soliciten paddings adicionales a la izquierda para evitar los recortes.

Como este cambio afecta la forma en que un TextView decide el ancho, TextView asigna más ancho de forma predeterminada si la app se orienta a Android 15 (nivel de API 35) o versiones posteriores. Para habilitar o inhabilitar este comportamiento, llama a la API de setUseBoundsForWidth en TextView.

Como agregar padding izquierdo puede causar un desajuste en los diseños existentes, el padding no se agrega de forma predeterminada, incluso en el caso de las apps orientadas a Android 15 o versiones posteriores. Sin embargo, puedes agregar padding adicional para evitar el recorte llamando a setShiftDrawingOffsetForStartOverhang.

En los siguientes ejemplos, se muestra cómo estos cambios pueden mejorar el diseño de texto para algunas fuentes y algunos idiomas.

Diseño estándar para texto en inglés en una fuente cursiva. Algunas de las letras están cortadas. Este es el XML correspondiente:

<TextView
    android:fontFamily="cursive"
    android:text="java" />
Diseño para el mismo texto en inglés con ancho y padding adicionales. Este es el XML correspondiente:

<TextView
    android:fontFamily="cursive"
    android:text="java"
    android:useBoundsForWidth="true"
    android:shiftDrawingOffsetForStartOverhang="true" />
Diseño estándar para texto tailandés. Algunas de las letras están cortadas. Este es el XML correspondiente:

<TextView
    android:text="คอมพิวเตอร์" />
Diseño para el mismo texto tailandés con ancho y padding adicionales. Este es el XML correspondiente:

<TextView
    android:text="คอมพิวเตอร์"
    android:useBoundsForWidth="true"
    android:shiftDrawingOffsetForStartOverhang="true" />

Altura de línea predeterminada que se adapta a la configuración regional para EditText

En versiones anteriores de Android, el diseño de texto estiraba la altura del texto para que coincida con la altura de línea de la fuente que coincidía con la configuración regional actual. Por ejemplo, si el contenido estaba en japonés, la altura de la línea de la fuente japonesa es ligeramente mayor que la de una fuente latina, por lo que la altura del texto se hizo un poco más grande. Sin embargo, a pesar de estas diferencias en las alturas de las líneas, el elemento EditText tenía un tamaño uniforme, independientemente de la configuración regional que se usara, como se ilustra en la siguiente imagen:

Tres cuadros que representan elementos EditText que pueden contener texto en inglés (en), japonés (ja) y birmano (my). La altura de EditText es la misma, a pesar de que estos lenguajes tienen alturas de línea diferentes entre sí.

En el caso de las apps que se orientan a Android 15 (nivel de API 35), ahora se reserva una altura de línea mínima para EditText para que coincida con la fuente de referencia de la configuración regional especificada, como se muestra en la siguiente imagen:

Tres cuadros que representan elementos EditText que pueden contener texto en inglés (en), japonés (ja) y birmano (my). La altura de EditText ahora incluye espacio para adaptarse a la altura de línea predeterminada de las fuentes de estos idiomas.

Si es necesario, tu app puede restablecer el comportamiento anterior especificando el atributo useLocalePreferredLineHeightForMinimum en false, y puede establecer métricas verticales mínimas personalizadas con la API de setMinimumFontMetrics en Kotlin y Java.

Cámara y contenido multimedia

Android 15 realiza los siguientes cambios en el comportamiento de la cámara y el contenido multimedia para las apps orientadas a Android 15 o versiones posteriores.

Restricciones para solicitar el foco de audio

Las apps que se orientan a Android 15 (nivel de API 35) deben ser la app principal o ejecutar un servicio en primer plano para solicitar el enfoque de audio. Si una app intenta solicitar el enfoque cuando no cumple con uno de estos requisitos, la llamada muestra AUDIOFOCUS_REQUEST_FAILED.

Puedes obtener más información sobre el enfoque de audio en Administra el enfoque de audio.

Actualización de restricciones que no pertenecen al SDK

Android 15 incluye listas actualizadas de este tipo de interfaces que están basadas en la colaboración con desarrolladores de Android y las pruebas internas más recientes. Siempre que sea posible, nos aseguramos de que las alternativas públicas estén disponibles antes de restringir las interfaces que no pertenecen al SDK.

Si tu app no está orientada a Android 15, es posible que algunos de estos cambios no te afecten de inmediato. Sin embargo, si bien es posible que tu app acceda a algunas interfaces que no pertenecen al SDK según el nivel de API objetivo, usar cualquier método o campo que no pertenezca al SDK siempre implica un gran riesgo de error para tu app.

Si no sabes con seguridad si tu app usa este tipo de interfaces, puedes probarla para verificarlo. Si tu app depende de interfaces que no pertenecen al SDK, deberías planificar una migración hacia otras alternativas SDK, Sin embargo, sabemos que algunas apps tienen casos de uso válidos para usarlas. Si no encuentras una alternativa al uso de una interfaz que no pertenece al SDK para una función de tu app, deberías solicitar una nueva API pública.

Para obtener más información sobre los cambios implementados en esta versión de Android, consulta Actualizaciones a las restricciones de interfaces que no pertenecen al SDK en Android 15. Para obtener más información sobre interfaces que no pertenecen al SDK en general, consulta Restricciones en interfaces que no pertenecen al SDK.