Administra los cambios en la configuración

Algunas configuraciones del dispositivo pueden cambiar mientras se ejecuta la app. Estos cambios incluyen, entre otros:

  • El tamaño de visualización de la app
  • Orientación de la pantalla
  • Tamaño y grosor de la fuente
  • Configuración regional
  • Diferencias entre el modo oscuro y el modo claro
  • Disponibilidad del teclado

La mayoría de estos cambios en la configuración se deben a algunas interacciones del usuario. Por ejemplo, cuando se rota o pliega el dispositivo, cambia la cantidad de espacio de pantalla disponible para tu app. Del mismo modo, modificar los parámetros de configuración del dispositivo, como el tamaño de la fuente, el idioma o el tema preferido, cambia sus valores respectivos en el Configuration objeto.

Estos parámetros suelen requerir cambios lo suficientemente grandes en la IU de tu app como para que la plataforma de Android tenga un mecanismo específico destinado a ellos. Este mecanismo es la recreación de Activity.

Recreación de actividades

El sistema recrea una Activity cuando se produce un cambio de configuración. Para ello, el sistema llama a onDestroy y destruye la instancia Activityexistente. Luego, crea una instancia nueva con onCreate, y esta nueva Activity instancia se inicializa con la configuración nueva y actualizada. Esto también significa que el sistema recrea la IU con la nueva configuración.

Por lo general, la Activity actúa como host para los elementos componibles. Cuando se vuelve a crear la Activity, Compose también vuelve a crear tu IU con los nuevos valores de configuración.

El comportamiento de recreación ayuda a tu app a adaptarse a las nuevas configuraciones, ya que vuelve a cargarse de forma automática con recursos alternativos que coinciden con la nueva configuración del dispositivo.

Ejemplo de recreación

Considera un elemento componible que muestra un título estático con un recurso de cadenas:

// In the res/values/strings.xml file
// <string name="compose">Jetpack Compose</string>

// In your Compose code
Text(
    text = stringResource(R.string.compose)
)

Cuando se crea la Activity, el elemento componible Text lee la configuración actual (como el idioma) y resuelve el recurso de cadenas adecuado.

Si el idioma cambia, el sistema vuelve a crear la actividad. Cuando esto sucede, Compose vuelve a crear la IU. Como stringResource lee desde la configuración actual, el título se actualiza automáticamente al valor localizado correcto.

Además, la recreación borra todo estado que se haya conservado como campo en la Activity.

Para preservar el estado de tu IU durante los cambios de configuración, usa los patrones recomendados de administración de estados. Usa ViewModel para los datos y la lógica empresarial, y usa rememberSaveable para el estado a nivel de la IU. Con estos mecanismos, tu estado sobrevive a la recreación de Activity mientras la IU se actualiza para reflejar la nueva configuración.

Para obtener más información sobre cómo guardar el estado en Compose, consulta Cómo guardar el estado de la IU en Compose.

Expectativas de los usuarios

El usuario de una app espera que se preserve el estado. Cuando un usuario completa un formulario y abre otra app en el modo multiventana para consultar información, su experiencia del usuario no será buena si regresa y los datos del formulario se borraron o si regresa a otra parte de la app. Como desarrollador, debes proporcionar una experiencia del usuario coherente que no se vea afectada por los cambios de configuración y la recreación de actividades.

Para verificar si el estado se preserva en tu aplicación, puedes realizar acciones que provocan cambios de configuración mientras la app está en primer plano y cuando está en segundo plano. Estas acciones incluyen lo siguiente:

  • Rotar el dispositivo
  • Ingresar al modo multiventana
  • Cambiar el tamaño de la app en el modo multiventana o en una ventana de formato libre
  • Plegar un dispositivo plegable con varias pantallas
  • Cambiar el tema del sistema, como del modo oscuro al modo claro
  • Cambiar el tamaño de la fuente
  • Cambiar el idioma del sistema o de la app
  • Conectar o desconectar un teclado de hardware
  • Conectar o desconectar un conector

Existen varios enfoques que puedes adoptar para preservar el estado relevante ante una recreación de Activity. Para determinar cuál usar, debes considerar el tipo de estado que quieres preservar:

  • La persistencia local para controlar el cierre del proceso en el caso de datos complejos o grandes (el almacenamiento local persistente incluye bases de datos o DataStore)
  • Objetos retenidos, como instancias de ViewModel, para controlar el estado relacionado con la IU en la memoria mientras el usuario usa la app de forma activa
  • rememberSaveable para preservar el estado transitorio de la IU durante los cambios de configuración y el cierre del proceso iniciado por el sistema (esto es adecuado para el estado que depende de la entrada del usuario, la posición de desplazamiento o la navegación, pero no pertenece a un ViewModel)

Para obtener información detallada sobre las APIs de cada enfoque, y cuándo es apropiado usarlas, consulta Cómo guardar estados de la IU.

Cómo restringir la recreación de actividades

Puedes evitar la recreación de actividades automática para ciertos cambios de configuración. En las apps modernas solo de Compose, la IU se vuelve a componer de cualquier manera, pero se recomienda controlar el cambio de configuración directamente.

De forma predeterminada, un cambio de configuración obliga al sistema a destruir y volver a crear la actividad, incluida la IU y cualquier objeto derivado de la actividad. Si declaras que tu actividad maneja el cambio de configuración por sí misma, el sistema evita esto. En su lugar, solo se actualiza el objeto Configuration, y Compose vuelve a componer tu IU con los valores nuevos.

Controlar los cambios de configuración directamente en Compose tiene varios beneficios:

  • Mejora el rendimiento: Volver a componer la IU es menos costoso que un ciclo completo de recreación de actividades, en especial para cambios menores.
  • Animaciones fluidas: Evitar el reinicio de una actividad te permite ejecutar animaciones continuas en los cambios de configuración, como transiciones de diseño fluidas durante la rotación del dispositivo.
  • Preservación del estado: Retener la instancia de actividad reduce el riesgo de pérdida de estado transitorio de la IU durante un evento como la rotación de la pantalla. Ten en cuenta que debes controlar la preservación del estado para el cierre del proceso iniciado por el sistema.

Para inhabilitar la recreación de actividades por ciertos cambios de configuración, agrega el tipo de configuración a android:configChanges en la <activity> entrada de tu AndroidManifest.xml archivo. Los valores posibles aparecen en la documentación del atributo android:configChanges.

El siguiente código de manifiesto inhabilita la recreación de Activity de MyActivity cuando cambia la orientación de la pantalla y la disponibilidad del teclado:

<activity
    android:name=".MyActivity"
    android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
    android:label="@string/app_name">

Cómo reaccionar a los cambios de configuración

Jetpack Compose permite que tu app reaccione con mayor facilidad a los cambios de configuración. Sin embargo, si inhabilitas la recreación de Activity para todos los cambios de configuración en los que es posible hacerlo, la app debe controlar correctamente los cambios de configuración.

El objeto Configuration está disponible en la jerarquía de la IU de Compose con el elemento local de composición de LocalConfiguration. Cada vez que cambia, se vuelven a componer las funciones de componibilidad que leen de LocalConfiguration.current. Para obtener información sobre cómo funcionan los elementos locales de composición, consulta Datos de alcance local con CompositionLocal.

Ejemplo

En el siguiente ejemplo, un elemento componible muestra una fecha con un formato específico. Este objeto componible reacciona a los cambios de configuración regional del sistema llamando a ConfigurationCompat.getLocales con LocalConfiguration.current.

@Composable
fun DateText(year: Int, dayOfYear: Int) {
    val dateTimeFormatter = DateTimeFormatter.ofPattern(
        "MMM dd",
        ConfigurationCompat.getLocales(LocalConfiguration.current)[0]
    )
    Text(
        dateTimeFormatter.format(LocalDate.ofYearDay(year, dayOfYear))
    )
}

Para evitar la recreación de Activity cuando cambia la configuración regional, la Activity que aloja el código de Compose debe inhabilitar los cambios de configuración regional. Para hacerlo, configura android:configChanges como locale|layoutDirection.

Cambios de configuración: conceptos clave y prácticas recomendadas

Estos son los conceptos clave que debes conocer cuando trabajas en cambios de configuración:

  • Configuraciones: Las configuraciones del dispositivo definen cómo debe mostrarse la IU al usuario; por ejemplo, el tamaño de visualización de la app, la configuración regional o el tema del sistema. En Compose, puedes acceder a los valores de configuración con LocalConfiguration.
  • Cambios de configuración: Las configuraciones cambian debido a la interacción del usuario. Por ejemplo, el usuario puede cambiar la configuración del dispositivo o cómo interactúa físicamente con él. No hay forma de evitar los cambios de configuración.
  • Recreación de Activity: Como resultado de los cambios en la configuración, se recrean las actividades de forma predeterminada.Activity Este es un mecanismo integrado para reinicializar el estado de la app para la configuración nueva.
  • Destrucción de Activity: La recreación de Activity hace que el sistema destruya la instancia de Activity anterior y cree una nueva en su lugar. La instancia anterior es obsoleta. Evita retener referencias a objetos con alcance de ciclo de vida más allá de su alcance previsto.
  • Estado: El estado de la instancia de Activity anterior no está presente en la instancia de Activity nueva, ya que son dos instancias de objeto diferentes. En lugar de vincular el estado a la actividad, usa las APIs recomendadas para preservar el estado de la app y del usuario, como se describe en Cómo guardar estados de la IU.
  • Inhabilitación: Inhabilitar la recreación de actividades para un tipo de cambio de configuración requiere que tu app se actualice de forma adecuada en respuesta a la nueva configuración. Para la mayoría de las apps de Compose, no se recomienda.

Para proporcionar una buena experiencia del usuario, ten en cuenta las siguientes prácticas recomendadas:

  • Prepárate para cambios de configuración frecuentes: No supongas que los cambios de configuración son poco frecuentes o nunca ocurren, independientemente del nivel de API, el factor de forma o el kit de herramientas de la IU. Cuando un usuario genera un cambio de configuración, espera que las apps se actualicen y continúen funcionando de forma correcta con la nueva configuración.
  • Conserva el estado: No pierdas el estado del usuario cuando se produzca una recreación de Activity. Conserva el estado como se describe en Cómo guardar estados de la IU con APIs como ViewModel y rememberSaveable.
  • Evita recurrir a inhabilitar como una solución rápida: No inhabilites la recreación de Activity como un atajo para evitar la pérdida de estado. Inhabilitar la recreación de actividades requiere que cumplas con la promesa de controlar el cambio, y aún puedes perder el estado debido a la recreación de Activity a partir de otros cambios de configuración, el cierre del proceso o el cierre de la app. Es imposible inhabilitar por completo la recreación de Activity. Conserva el estado como se describe en Cómo guardar estados de la IU.
  • No evites los cambios de configuración: No establezcas restricciones con respecto a la orientación, la relación de aspecto ni el cambio de tamaño para evitar los cambios de configuración y Activity recreación. Esto afectará de forma negativa a los usuarios que prefieran usar tu app de una manera determinada.

Cómo controlar los cambios de configuración basados en el tamaño

Los cambios de configuración basados en el tamaño pueden ocurrir en cualquier momento y son más probables cuando tu app se ejecuta en un dispositivo de pantalla grande en el que los usuarios pueden ingresar en el modo multiventana. Los usuarios esperan que tu app funcione bien en ese entorno.

Existen dos tipos generales de cambios de tamaño: significativos y mínimos. Un cambio de tamaño significativo es aquel en el que un conjunto diferente de recursos alternativos se aplica a la nueva configuración debido a una diferencia en el tamaño de la pantalla, como el ancho, la altura o el ancho mínimo. Entre estos recursos, se incluyen aquellos que la app define por sí misma y los de cualquiera de sus bibliotecas.

Cómo restringir la recreación de actividades para los cambios de configuración basados en el tamaño

Cuando inhabilitas la recreación de Activity para cambios de configuración basados en el tamaño, el sistema no recrea la Activity. En su lugar, recibe una llamada a Activity.onConfigurationChanged. Cualquier elemento componible que lea LocalConfiguration.current se vuelve a componer automáticamente para reflejar el nuevo tamaño.

La recreación de Activity está inhabilitada para los cambios de configuración basados en el tamaño cuando tienes android:configChanges="screenSize|smallestScreenSize|orientation|screenLayout" en tu archivo de manifiesto.

Recursos adicionales

Para obtener más información sobre cómo controlar los cambios de configuración, consulta los siguientes recursos adicionales:

Documentación

Contenido de Views