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á segmentada para 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 en el comportamiento que afectan a todas las apps que se ejecutan en Android 15, independientemente de targetSdkVersion de tu app.

Funcionalidad principal

Android 15 modifica o expande varias capacidades 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 presenta un nuevo comportamiento de tiempo de espera para dataSync en el caso de las apps que se orientan a Android 15 (nivel de API 35) o versiones posteriores. Este comportamiento también se aplica al nuevo tipo de servicio en primer plano mediaProcessing.

El sistema permite que los servicios dataSync de una app se ejecuten durante un total de 6 horas en un período de 24 horas, después de lo cual el sistema llama al método Service.onTimeout(int, int) del servicio en ejecución (presentado en Android 15). En este momento, el servicio tiene unos segundos para llamar a Service.stopSelf(). Cuando se llama a Service.onTimeout(), el servicio ya no se considera un servicio en primer plano. Si el servicio no llama a Service.stopSelf(), el sistema arroja una excepción interna. La excepción se registra en Logcat con el siguiente mensaje:

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

Para evitar problemas con este cambio de comportamiento, puedes realizar una o más de las siguientes acciones:

  1. Haz que tu servicio implemente el nuevo método Service.onTimeout(int, int). Cuando tu app reciba la devolución de llamada, asegúrate de llamar a stopSelf() en unos pocos segundos. (Si no detienes la app de inmediato, el sistema genera una falla).
  2. Asegúrate de que los servicios dataSync de tu app no se ejecuten durante más de un total de 6 horas en cualquier período de 24 horas (a menos que el usuario interactúe con la app y restablezca el temporizador).
  3. Inicia servicios en primer plano de dataSync solo como resultado de la interacción directa del usuario. Como tu app está en primer plano cuando se inicia el servicio, este tiene las seis horas completas después de que la app pasa a segundo plano.
  4. En lugar de usar un servicio en primer plano de dataSync, usa una API alternativa.

Si los servicios en primer plano dataSync de tu app se ejecutaron durante 6 horas en las últimas 24, no puedes iniciar otro servicio en primer plano dataSync a menos que el usuario haya llevado tu app al primer plano (lo que restablece el temporizador). Si intentas iniciar otro servicio en primer plano dataSync, el sistema arrojará una ForegroundServiceStartNotAllowedException con un mensaje de error como "El límite de tiempo ya se agotó para el tipo de servicio en primer plano dataSync".

Prueba

Para probar el comportamiento de tu app, puedes habilitar los tiempos de espera de sincronización de datos, incluso si tu app no se orienta a Android 15 (siempre que la app se ejecute en un dispositivo Android 15). Para habilitar los tiempos de espera, ejecuta el siguiente comando adb:

adb shell am compat enable FGS_INTRODUCE_TIME_LIMITS your-package-name

También puedes ajustar el tiempo de espera para facilitar la prueba del comportamiento de tu app cuando se alcanza el límite. Para establecer un nuevo período de tiempo de espera, ejecuta el siguiente comando 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 presenta un nuevo tipo de servicio en primer plano: mediaProcessing. Este tipo de servicio es adecuado para operaciones como la transcodificación de archivos multimedia. Por ejemplo, una app de música puede descargar un archivo de audio y necesitar convertirlo a un formato diferente antes de reproducirlo. Puedes usar un servicio en primer plano mediaProcessing para asegurarte de que la conversión continúe incluso mientras la app está en segundo plano.

El sistema permite que los servicios mediaProcessing de una app se ejecuten durante un total de 6 horas en un período de 24 horas, después del cual el sistema llama al método Service.onTimeout(int, int) del servicio en ejecución (presentado en Android 15). En este momento, el servicio tiene unos segundos para llamar a Service.stopSelf(). Si el servicio no llama a Service.stopSelf(), el sistema arroja una excepción interna. La excepción se registra en Logcat con el siguiente mensaje:

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

Para evitar tener la excepción, puedes realizar una de las siguientes acciones:

  1. Haz que tu servicio implemente el nuevo método Service.onTimeout(int, int). Cuando tu app reciba la devolución de llamada, asegúrate de llamar a stopSelf() en unos pocos segundos. (Si no detienes la app de inmediato, el sistema genera una falla).
  2. Asegúrate de que los servicios mediaProcessing de tu app no se ejecuten durante más de un total de 6 horas en cualquier período de 24 horas (a menos que el usuario interactúe con la app y restablezca el temporizador).
  3. Inicia servicios en primer plano de mediaProcessing solo como resultado de la interacción directa del usuario. Como tu app está en primer plano cuando se inicia el servicio, este tiene las seis horas completas después de que la app pasa a segundo plano.
  4. En lugar de usar un servicio en primer plano de mediaProcessing, usa una API alternativa, como WorkManager.

Si los servicios en primer plano de mediaProcessing de tu app se ejecutaron durante 6 horas durante las últimas 24, no puedes iniciar otro servicio mediaProcessing en primer plano, a menos que el usuario haya llevado la app al primer plano (lo que restablece el temporizador). Si intentas iniciar otro servicio en primer plano mediaProcessing, el sistema arroja ForegroundServiceStartNotAllowedException con un mensaje de error como "Se agotó el tiempo límite para el tipo de servicio en primer plano mediaProcessing".

Para obtener más información sobre el tipo de servicio mediaProcessing, consulta Cambios en los tipos de servicios en primer plano para Android 15: Procesamiento de contenido multimedia.

Prueba

Para probar el comportamiento de tu app, puedes habilitar los tiempos de espera de procesamiento de contenido multimedia, incluso si tu app no está orientada a Android 15 (siempre que se ejecute en un dispositivo con Android 15). Para habilitar los tiempos de espera, ejecuta el siguiente comando adb:

adb shell am compat enable FGS_INTRODUCE_TIME_LIMITS your-package-name

También puedes ajustar el tiempo de espera para facilitar la prueba del comportamiento de tu app cuando se alcanza el límite. Para establecer un nuevo tiempo de espera, ejecuta el siguiente comando adb:

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

在启动 BOOT_COMPLETED 广播接收器方面存在新限制 前台服务。BOOT_COMPLETED 接收器能启动 以下类型的前台服务:

如果 BOOT_COMPLETED 接收器尝试启动任何上述类型的前台 服务,系统会抛出 ForegroundServiceStartNotAllowedException

测试

如需测试应用的行为,您可以启用这些新限制,即使您的应用并未以 Android 15 为目标平台(只要应用在 Android 15 设备上运行)也是如此。运行以下 adb 命令:

adb shell am compat enable FGS_BOOT_COMPLETED_RESTRICTIONS your-package-name

如需在不重启设备的情况下发送 BOOT_COMPLETED 广播,请运行以下 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

以前,如果应用拥有 SYSTEM_ALERT_WINDOW 权限,即使应用当前在后台运行,也可以启动前台服务(如免于后台启动限制中所述)。

如果应用以 Android 15 为目标平台,则此豁免范围现在更窄。现在,应用需要具有 SYSTEM_ALERT_WINDOW 权限,并且需要有一个可见的叠加窗口。也就是说,应用需要先启动 TYPE_APPLICATION_OVERLAY 窗口,并且该窗口需要处于可见状态,然后您才能启动前台服务。

如果您的应用尝试从后台启动前台服务,但不符合这些新要求(并且没有其他豁免情况),系统会抛出 ForegroundServiceStartNotAllowedException

如果您的应用声明了 SYSTEM_ALERT_WINDOW 权限并从后台启动前台服务,则可能会受到此变更的影响。如果您的应用获得了 ForegroundServiceStartNotAllowedException,请检查应用的操作顺序,并确保应用在尝试从后台启动前台服务之前已具有有效的叠加层窗口。您可以通过调用 View.getWindowVisibility() 检查叠加层窗口当前是否可见,也可以替换 View.onWindowVisibilityChanged(),以便在可见性发生变化时收到通知。

测试

如需测试应用的行为,您可以启用这些新限制,即使您的应用并未以 Android 15 为目标平台(只要应用在 Android 15 设备上运行)也是如此。如需针对从后台启动前台服务启用这些新限制,请运行以下 adb 命令:

adb shell am compat enable FGS_SAW_RESTRICTIONS your-package-name

Cambios en el momento 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 segmentan Android 15 (nivel de API 35):

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

    Por ejemplo, se arroja 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 usando 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 se usa la API de Locale, los códigos de idioma para hebreo, yidis e indonesio ya no se convierten a sus formas obsoletas (hebreo: iw, yidis: ji e indonesio: in). Cuando especifiques el código de idioma para una de estas configuraciones regionales, usa los códigos de ISO 639-1 (hebreo: he, yidis: yi e indonesio: id).

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

    En general, este cambio no debería generar 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 funciones, 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 inferior, y luego se ejecuta en Android 14 y versiones anteriores, se arroja 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 NewApi lint 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 en 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 una versión posterior, también se proporciona una opción de corrección rápida para estos errores.

    Considera quitar @SuppressLint("NewApi") y lintOptions { disable 'NewApi' } si se inhabilitó la opción de 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 genera un error en el 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 anular 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 las apps y 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.

Inicio seguro de actividades en segundo plano

Android 15 protege a los usuarios de las apps maliciosas y les brinda más control sobre sus dispositivos, ya que agrega cambios que impiden que las apps maliciosas en segundo plano lleven otras apps al primer plano, eleven sus privilegios y abusen de la interacción del usuario. Los inicios de actividad en segundo plano están restringidos desde Android 10 (nivel de API 29).

Otros cambios

  • Cambia los creadores de PendingIntent para bloquear los inicios de actividad en segundo plano de forma predeterminada. Esto ayuda a evitar que las apps creen accidentalmente un objeto PendingIntent que podría ser utilizado de forma abusiva por agentes maliciosos.
  • No pases una app al primer plano, a menos que el remitente PendingIntent lo permita. El objetivo de este cambio es evitar que las apps maliciosas abusen de la capacidad de iniciar actividades en segundo plano. De forma predeterminada, las apps no pueden llevar la pila de tareas a primer plano, a menos que el creador permita privilegios de inicio de actividad en segundo plano o el remitente tenga privilegios de inicio de actividad en segundo plano.
  • Controla cómo la actividad superior de una pila de tareas puede finalizar su tarea. Si la actividad superior finaliza una tarea, Android volverá a la tarea que estuvo activa por última vez. Además, si una actividad que no está en primer plano finaliza su tarea, Android volverá a la pantalla principal y no bloqueará la finalización de esta actividad que no está en primer plano.
  • Evita iniciar actividades arbitrarias desde otras apps en tu propia tarea. Este cambio evita que las apps maliciosas realicen phishing a los usuarios creando actividades que parecen ser de otras apps.
  • Bloquea las ventanas no visibles para que no se consideren para los inicios de actividad en segundo plano. Esto ayuda a evitar que las apps maliciosas abusen de los inicios de actividad en segundo plano para mostrar contenido no deseado o malicioso a los usuarios.

Intents más seguros

Android 15 introduce StrictMode para intents.

Para ver registros detallados sobre los incumplimientos del uso de Intent, usa 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 la IU del sistema

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

Cambios en la inserción de ventana

Android 15 中与窗口内边距相关的两项变更:默认强制执行边到边,此外还有配置变更,例如系统栏的默认配置。

全面实施政策

如果应用以 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 Auto 上可能会显示不同的内容。

以 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_ALWAYSActivity。如果您的应用在启动时崩溃,这可能是由启动画面引起的。您可以将核心启动画面依赖项升级到 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 参数。
  • 如果应用使用了视图和 Material 组件 (com.google.android.material),则大多数基于视图的 Material 组件(例如 BottomNavigationViewBottomAppBarNavigationRailViewNavigationView)都会处理边衬区,因此不需要执行额外的操作。不过,如果使用的是 AppBarLayout,则需要添加 android:fitsSystemWindows="true"
  • 对于自定义可组合项,请手动应用边衬区作为内边距。如果您的内容位于 Scaffold 中,则可以使用 Scaffold 内边距值来使用边衬区。否则,请使用 WindowInsets 之一应用内边距。
  • 如果应用使用的是视图和 BottomSheetSideSheet 或自定义容器,请使用 ViewCompat.setOnApplyWindowInsetsListener 应用内边距。对于 RecyclerView,请使用此监听器应用内边距,同时添加 clipToPadding="false"
如果应用必须提供自定义后台保护,您需要检查哪些方面

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

其他全屏显示资源

如需了解有关应用边衬区的其他注意事项,请参阅全屏视图全屏 Compose 指南。

已弃用的 API

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

以下 API 已弃用并停用:

稳定配置

Si tu app se segmenta para 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 apropiado 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, la orientación y los tamaños de las ventanas. Una característica importante sobre los tamaños de ventana que devuelve Configuration es que antes excluía las barras del sistema.

El tamaño de la configuración se suele usar 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ó usarlo para el cálculo del diseño. Si es así, debes alejarte de él 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 establecido en verdadero.

对于以 Android 15(API 级别 35)为目标平台的应用,elegantTextHeight TextView 属性默认会变为 true,将默认使用的紧凑字体替换为一些具有较大垂直测量的脚本,使其更易于阅读。紧凑字体旨在防止布局中断;Android 13(API 级别 33)允许文本布局利用 fallbackLineSpacing 属性拉伸垂直高度,从而防止许多此类中断。

在 Android 15 中,系统中仍保留了紧凑字体,因此您的应用可以将 elegantTextHeight 设置为 false 以获得与之前相同的行为,但即将发布的版本不太可能支持此字体。因此,如果您的应用支持以下脚本:阿拉伯语、老挝语、缅甸语、泰米尔语、古吉拉特语、卡纳达语、马拉雅拉姆语、奥里亚语、泰卢固语或泰语,请将 elegantTextHeight 设置为 true 以测试您的应用。

针对以 Android 14(API 级别 34)及更低版本为目标平台的应用的 elegantTextHeight 行为。
以 Android 15 为目标平台的应用的 elegantTextHeight 行为。

El ancho de TextView cambia para formas de letras complejas

在以前的 Android 版本中,某些具有复杂形状的手写字体或语言可能会在上一个或下一个字符的区域绘制字母。在某些情况下,此类字母会在开头或结尾处被剪裁。从 Android 15 开始,TextView 会分配宽度,以便为此类字母绘制足够的空间,并允许应用请求向左额外添加内边距以防止剪裁。

由于此更改会影响 TextView 确定宽度的方式,因此如果应用以 Android 15(API 级别 35)或更高版本为目标平台,TextView 会默认分配更多宽度。您可以通过对 TextView 调用 setUseBoundsForWidth API 来启用或停用此行为。

由于添加左内边距可能会导致现有布局未对齐,因此默认情况下不会添加内边距,即使以 Android 15 或更高版本为目标平台的应用也是如此。不过,您可以通过调用 setShiftDrawingOffsetForStartOverhang 添加额外的内边距以防止剪裁。

以下示例展示了这些更改如何改进某些字体和语言的文本布局。

采用手写体字体的英语文本的标准布局。部分字母被截断。对应的 XML 如下:

<TextView
    android:fontFamily="cursive"
    android:text="java" />
相同英语文本的布局,增加了宽度和内边距。以下是相应的 XML:

<TextView
    android:fontFamily="cursive"
    android:text="java"
    android:useBoundsForWidth="true"
    android:shiftDrawingOffsetForStartOverhang="true" />
泰语文本的标准布局。部分字母被截断。 以下是相应的 XML:

<TextView
    android:text="คอมพิวเตอร์" />
相同泰语文本的布局,增加了宽度和内边距。以下是相应的 XML:

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

Altura de línea predeterminada compatible con 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 los medios para las apps que se segmentan para 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á segmentada para Android 15, es posible que algunos de estos cambios no te afecten de inmediato. Sin embargo, aunque tu app puede acceder a algunas interfaces que no pertenecen al SDK según el nivel de API objetivo de tu app, 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 verificar. 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 usar interfaces que no pertenecen al SDK. 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.