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 amplía varias funciones principales del sistema Android.
Cambios en los servicios en primer plano
Realizamos los siguientes cambios en los servicios en primer plano con Android 15.
- Comportamiento de tiempo de espera del servicio en primer plano de sincronización de datos
- Nuevo tipo de servicio en primer plano de procesamiento de contenido multimedia
- Restricciones en los receptores de emisión
BOOT_COMPLETED
que inician servicios en primer plano - Restricciones para iniciar servicios en primer plano mientras una app tiene el permiso
SYSTEM_ALERT_WINDOW
Comportamiento de tiempo de espera del servicio en primer plano de la sincronización de datos
Android 15 introduce un nuevo comportamiento de tiempo de espera en dataSync
para las apps orientadas 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:
- 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 astopSelf()
en unos pocos segundos. (Si no detienes la app de inmediato, el sistema genera una falla). - 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). - Solo inicia los servicios en primer plano de
dataSync
como resultado de la interacción directa del usuario. Dado que tu app está en primer plano cuando se inicia el servicio, el servicio tiene seis horas completas después de que la app pasa a segundo plano. - 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 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:
- Have your service implement the new
Service.onTimeout(int, int)
method. When your app receives the callback, make sure to callstopSelf()
within a few seconds. (If you don't stop the app right away, the system generates a failure.) - 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). - 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. - 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 en los receptores de emisión de BOOT_COMPLETED
que lanzan 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:
dataSync
camera
mediaPlayback
phoneCall
mediaProjection
microphone
(esta restricción se aplicó pormicrophone
desde el Android 14)
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 orientadas a Android 15 ya no pueden cambiar el estado global ni la política de No interrumpir (No interrumpir) en un dispositivo (ya sea modificando la configuración del usuario o desactivando el modo No interrumpir). En cambio, las apps deben aportar un AutomaticZenRule
, que el sistema combina en una política global con el esquema existente más restrictivo de la política. Las llamadas a las APIs existentes que anteriormente afectaron el estado global (setInterruptionFilter
, setNotificationPolicy
) generan la creación o actualización de un AutomaticZenRule
implícito, que se activa y 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 una AutomaticZenRule
que sus propietarios hayan activado.
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 orientadas 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()
yFormatter.format()
:String.format(String, Object[])
String.format(Locale, String, Object[])
Formatter.format(String, Object[])
Formatter.format(Locale, String, Object[])
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 usaArrays.asList(...).toArray()
, el tipo de componente del array resultante ahora esObject
, no el tipo de los elementos del array subyacente. Por lo tanto, el siguiente código arroja unClassCastException
:String[] elements = (String[]) Arrays.asList("one", "two").toArray();
En este caso, para conservar
String
como el tipo de componente en el array resultante, puedes usarCollection.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, 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étodosRandom.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 conRandom.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()
yMutableList.removeLast()
enkotlin-stdlib
El tipo
List
en Java se asigna al tipoMutableList
en Kotlin. Debido a que las APIs deList.removeFirst()
yList.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 deList
en lugar de a las funciones de extensión enkotlin-stdlib
.Si una app se vuelve a compilar con
compileSdk
establecido en35
yminSdk
establecido en34
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()
yremoveLast()
se pueden reemplazar porremoveAt(0)
yremoveAt(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")
ylintOptions { 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
yDeque
. 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 compiladorjavac
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 ListEjemplo 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 errorEjemplo 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 errorPara 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.
Inicios de actividades en segundo plano seguros
Android 15 protege a los usuarios de apps maliciosas y les da más control sobre sus dispositivos agregando cambios que eviten que las apps llevar otras apps al primer plano, elevar sus privilegios y fomentar el abuso la interacción del usuario. Se restringieron los inicios de actividades en segundo plano desde Android 10 (nivel de API 29)
Impedir que se inicien actividades las apps que no coincidan con el UID superior de la pila
Las apps maliciosas pueden iniciar la actividad de otra app dentro de la misma tarea y, luego,
se superponen para crear la ilusión de ser esa aplicación. Esta "tarea
usurpación" este ataque elude las restricciones actuales
de inicio en segundo plano
ocurre dentro de la misma tarea visible. Para mitigar este riesgo, Android 15 agrega un
marca que impide que se inicien las apps que no coinciden con el UID superior de la pila
de datos. Para aceptar todas las actividades de tu app, actualiza la
allowCrossUidActivitySwitchFromBelow
en el archivo AndroidManifest.xml
de tu app:
<application android:allowCrossUidActivitySwitchFromBelow="false" >
Las nuevas medidas de seguridad estarán activas si se cumplen todas las condiciones que figuran a continuación:
- La app que realiza el lanzamiento está orientada a Android 15.
- La app que está sobre la pila de tareas se orienta a Android 15.
- Toda actividad visible se haya habilitado para las nuevas protecciones
Si las medidas de seguridad están habilitadas, las apps podrían regresar a la página principal, en lugar de la última app visible, si terminan sus propias tareas.
Otros cambios
Además de la restricción para la coincidencia de UID, estos otros cambios también incluidos:
- Cambia a
PendingIntent
creadores para que bloqueen los lanzamientos de actividades en segundo plano de la siguiente manera: predeterminada. Esto ayuda a evitar que las apps creen accidentalmentePendingIntent
que podrían ser abusadas por agentes maliciosos. - No lleves una app al primer plano, a menos que sea el remitente de
PendingIntent
. lo permite. El objetivo de este cambio es evitar que apps maliciosas abusen de los capacidad para iniciar actividades en segundo plano. De forma predeterminada, las apps no llevar la pila de tareas al primer plano, a menos que el creador permita privilegios de inicio de la actividad en segundo plano o si el remitente tiene actividad en segundo plano iniciar privilegios. - Controla cómo la actividad principal de una pila de tareas puede finalizar su tarea. Si el botón una actividad principal finaliza una tarea, Android regresará a la tarea que se haya la última vez que estuvo activo. Además, si una actividad que no es principal finaliza su tarea, Android vuelve a la pantalla de inicio, no bloqueará el acabado de este no superior actividad.
- Evita iniciar actividades arbitrarias de otras apps en la tuya tarea. Este cambio evita que las apps maliciosas creen ataques de phishing a través de la creación actividades que parecen ser de otras aplicaciones.
- Bloquear ventanas no visibles para que no se consideren para la actividad en segundo plano inicio de sesión. Esto ayuda a evitar que apps maliciosas abusen de los recursos en segundo plano se inicia actividad para mostrar contenido malicioso o no deseado a los usuarios.
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 las inserciones de ventana en Android 15: de borde a borde 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
Apps are edge-to-edge by default on devices running Android 15 if the app is targeting Android 15 (API level 35).
This is a breaking change that might negatively impact your app's UI. The changes affect the following UI areas:
- Gesture handle navigation bar
- Transparent by default.
- Bottom offset is disabled so content draws behind the system navigation bar unless insets are applied.
setNavigationBarColor
andR.attr#navigationBarColor
are deprecated and don't affect gesture navigation.setNavigationBarContrastEnforced
andR.attr#navigationBarContrastEnforced
continue to have no effect on gesture navigation.
- 3-button navigation
- Opacity set to 80% by default, with color possibly matching the window background.
- Bottom offset disabled so content draws behind the system navigation bar unless insets are applied.
setNavigationBarColor
andR.attr#navigationBarColor
are set to match the window background by default. The window background must be a color drawable for this default to apply. This API is deprecated but continues to affect 3-button navigation.setNavigationBarContrastEnforced
andR.attr#navigationBarContrastEnforced
is true by default, which adds an 80% opaque background across 3-button navigation.
- Status bar
- Transparent by default.
- The top offset is disabled so content draws behind the status bar unless insets are applied.
setStatusBarColor
andR.attr#statusBarColor
are deprecated and have no effect on Android 15.setStatusBarContrastEnforced
andR.attr#statusBarContrastEnforced
are deprecated but still have an effect on Android 15.
- Display cutout
layoutInDisplayCutoutMode
of non-floating windows must beLAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
.SHORT_EDGES
,NEVER
, andDEFAULT
are interpreted asALWAYS
so that users don't see a black bar caused by the display cutout and appear edge-to-edge.
The following example shows an app before and after targeting Android 15 (API level 35), and before and after applying insets.
What to check if your app is already edge-to-edge
If your app is already edge-to-edge and applies insets, you are mostly unimpacted, except in the following scenarios. However, even if you think you aren't impacted, we recommend you test your app.
- You have a non-floating window, such as an
Activity
that usesSHORT_EDGES
,NEVER
orDEFAULT
instead ofLAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
. If your app crashes on launch, this might be due to your splashscreen. You can either upgrade the core splashscreen dependency to 1.2.0-alpha01 or later or setwindow.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutInDisplayCutoutMode.always
. - There might be lower-traffic screens with occluded UI. Verify these
less-visited screens don't have occluded UI. Lower-traffic screens include:
- Onboarding or sign-in screens
- Settings pages
What to check if your app is not already edge-to-edge
If your app is not already edge-to-edge, you are most likely impacted. In addition to the scenarios for apps that are already edge-to-edge, you should consider the following:
- If your app uses Material 3 Components (
androidx.compose.material3
) in compose, such asTopAppBar
,BottomAppBar
, andNavigationBar
, these components are likely not impacted because they automatically handle insets. - If your app is using Material 2 Components (
androidx.compose.material
) in Compose, these components don't automatically handle insets. However, you can get access to the insets and apply them manually. In androidx.compose.material 1.6.0 and later, use thewindowInsets
parameter to apply the insets manually forBottomAppBar
,TopAppBar
,BottomNavigation
, andNavigationRail
. Likewise, use thecontentWindowInsets
parameter forScaffold
. - If your app uses views and Material Components
(
com.google.android.material
), most views-based Material Components such asBottomNavigationView
,BottomAppBar
,NavigationRailView
, orNavigationView
, handle insets and require no additional work. However, you need to addandroid:fitsSystemWindows="true"
if usingAppBarLayout
. - For custom composables, apply the insets manually as padding. If your
content is within a
Scaffold
, you can consume insets using theScaffold
padding values. Otherwise, apply padding using one of theWindowInsets
. - If your app is using views and
BottomSheet
,SideSheet
or custom containers, apply padding usingViewCompat.setOnApplyWindowInsetsListener
. ForRecyclerView
, apply padding using this listener and also addclipToPadding="false"
.
What to check if your app must offer custom background protection
If your app must offer custom background protection to 3-button navigation or
the status bar, your app should place a composable or view behind the system bar
using WindowInsets.Type#tappableElement()
to get the 3-button
navigation bar height or WindowInsets.Type#statusBars
.
Additional edge-to-edge resources
See the Edge to Edge Views and Edge to Edge Compose guides for additional considerations on applying insets.
Deprecated APIs
The following APIs are deprecated but not disabled:
R.attr#enforceStatusBarContrast
R.attr#navigationBarColor
(for 3 button navigation, with 80% alpha)Window#isStatusBarContrastEnforced
Window#setNavigationBarColor
(for 3 button navigation, with 80% alpha)Window#setStatusBarContrastEnforced
The following APIs are deprecated and disabled:
R.attr#navigationBarColor
(for gesture navigation)R.attr#navigationBarDividerColor
R.attr#statusBarColor
Window#setDecorFitsSystemWindows
Window#getNavigationBarColor
Window#getNavigationBarDividerColor
Window#getStatusBarColor
Window#setNavigationBarColor
(for gesture navigation)Window#setNavigationBarDividerColor
Window#setStatusBarColor
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 que se verán afectados por este cambio:
- Los tamaños
Configuration.screenWidthDp
yscreenHeightDp
ya no excluyen las barras del sistema. Configuration.smallestScreenWidthDp
se ve afectado indirectamente por los cambios enscreenWidthDp
yscreenHeightDp
.Configuration.orientation
se ve afectado indirectamente por los cambios enscreenWidthDp
yscreenHeightDp
en dispositivos cercanos al cuadrado.Display.getSize(Point)
se ve afectado indirectamente por los cambios enConfiguration
. Este elemento dejó de estar disponible a partir del nivel de API 30.Display.getMetrics()
ya funciona de esta manera desde el nivel de API 33.
El atributo eleganteTextHeight se establece en verdadero de forma predeterminada
En el caso de las apps orientadas a Android 15, el atributo TextView
elegantTextHeight
se convierte en true
de forma predeterminada, lo que reemplaza la fuente compacta de forma predeterminada con algunas secuencias de comandos que tienen métricas verticales grandes por una mucho más legible. La fuente compacta se introdujo para evitar diseños rotos. Android 13 (nivel de API 33) evita muchas de estas fallas permitiendo 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 poco probable que se admita en próximas versiones. Por lo tanto, si tu app admite las siguientes secuencias de comandos: árabe, laosiano, Birmania, tamil, guyaratí, canarés, malabar, oriya, telugu o tailandés, configura elegantTextHeight
en true
para probar la app.
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.
Altura de línea predeterminada de la configuración regional para EditText
En las versiones anteriores de Android, el diseño de texto estiraba la altura del texto para cumplir con la altura de la línea de la fuente que coincidía con la configuración regional actual. Por ejemplo, si el contenido estaba en japonés, debido a que la altura de la línea de la fuente japonesa es ligeramente mayor que la de una fuente latina, la altura del texto se agrandó un poco más grande. Sin embargo, a pesar de estas diferencias en las alturas de las líneas, el tamaño del elemento EditText
se dimensionó de manera uniforme, independientemente de la configuración regional que se usara, como se ilustra en la siguiente imagen:
En el caso de las apps orientadas a Android 15, ahora se reserva una altura de línea mínima para que EditText
coincida con la fuente de referencia de la configuración regional especificada, como se muestra en la siguiente imagen:
Si es necesario, tu app puede restablecer el comportamiento anterior especificando el atributo useLocalePreferredLineHeightForMinimum
en false
, y tu app 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 orientadas a Android 15 deben ser la app principal o que ejecuten un servicio en primer plano para solicitar foco de audio. Si una app intenta solicitar el foco cuando no cumple con uno de estos requisitos, la llamada mostrará AUDIOFOCUS_REQUEST_FAILED
.
Puedes obtener más información sobre el foco de audio en Cómo administrar el foco 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.