Tiempo de inicio de la app

Los usuarios esperan que las apps se carguen rápido y sean responsivas. Una app con inicio lento no cumple con esta expectativa y puede decepcionar a los usuarios. Este tipo de experiencia negativa puede hacer que un usuario califique tu app con una puntuación baja en Play Store o incluso que deje de usarla por completo.

En esta página, se proporciona información para ayudarte a optimizar el tiempo de inicio de tu app, lo que incluye una descripción general de las partes internas del proceso de inicio, la forma de generar perfiles del rendimiento del inicio y algunos problemas habituales relacionados con la hora de inicio, además de sugerencias para saber cómo abordarlos.

Información sobre los diferentes estados de inicio de la app

El inicio de una app se puede llevar a cabo en uno de estos tres estados: inicio en frío, inicio semicaliente o inicio en caliente. Cada estado determina el tiempo que tarda tu app en volverse visible para el usuario. En inicio en frío, tu app se inicia desde cero. En los otros estados, el sistema necesita llevar la ejecución de la app del segundo plano al primero.

Te recomendamos que siempre optimices tu app en función de la perspectiva de un inicio en frío, ya que esto también puede mejorar el rendimiento de los inicios semicalientes y en caliente.

Para optimizar tu app para un inicio rápido, es útil conocer qué sucede en los niveles del sistema y de la app, y cómo estos interactúan, en cada uno de los estados.

Dos métricas importantes para determinar el inicio de la app son el tiempo para la visualización inicial (TTID) y el tiempo para la visualización completa (TTFD). El TTID es el tiempo que lleva mostrar el primer fotograma, y el TTFD es el tiempo que tarda la app en volverse completamente interactiva. Ambos son igualmente importantes, porque el TTID informa al usuario que la app se está cargando, y el TTFD muestra cuándo la app ya se puede usar. Si alguno de estos es demasiado largo, es posible que el usuario salga de la app incluso antes de que se cargue por completo.

Inicio en frío

El inicio en frío hace referencia a una app que se inicia desde cero. Esto significa que, hasta este inicio, el proceso del sistema crea el proceso de la app. Los inicios en frío se producen, por ejemplo, cuando se inicia tu app por primera vez desde que se inició el dispositivo o desde que el sistema finalizó la app.

Este tipo de inicio presenta el mayor desafío a la hora de minimizar el tiempo de inicio, ya que el sistema y la app deben trabajar más que en los otros estados de inicio.

Cuando comienza el inicio en frío, el sistema tiene las siguientes tres tareas:

  1. Cargar e iniciar la app
  2. Mostrar una ventana de inicio en blanco para la app inmediatamente después del inicio
  3. Crear el proceso de la app

No bien el sistema crea el proceso de la app, este es responsable de las siguientes etapas:

  1. Crear el objeto de la app
  2. Iniciar el subproceso principal
  3. Crear la actividad principal
  4. Aumentar las vistas
  5. Diseñar la pantalla
  6. Realizar el dibujo inicial

Cuando el proceso de la app completa la apertura inicial, el proceso del sistema intercambia la ventana en segundo plano que se muestra y la reemplaza con la actividad principal. En este punto, el usuario puede comenzar a usar la app.

En la Figura 1, se muestra cómo los procesos del sistema y la app transfieren el trabajo entre sí.

Figura 1: Representación visual de las partes importantes del inicio en frío de una app.

Pueden surgir problemas de rendimiento durante la creación de la app y de la actividad.

Creación de la app

Cuando se inicia tu app, la ventana en blanco de inicio permanece en la pantalla hasta que el sistema termina de abrir la app por primera vez. En este punto, el proceso del sistema intercambia la ventana de inicio por tu app, lo que le permite al usuario interactuar con ella.

Si anulas Application.onCreate() en tu propia app, el sistema invoca el método onCreate() en el objeto de la app. Luego, la app genera el subproceso principal, también conocido como subproceso de IU, y le asigna la tarea de crear la actividad principal.

Desde este momento, los procesos del nivel del sistema y de la app continúan de acuerdo con las etapas del ciclo de vida de la app.

Creación de la actividad

Después de que el proceso de la app crea tu actividad, esta lleva a cabo las siguientes operaciones:

  1. Inicializa valores.
  2. Llama a constructores.
  3. Llama al método de devolución de llamada, como Activity.onCreate(), apropiado para el estado del ciclo de vida de ese momento de la actividad.

Por lo general, el método onCreate() produce el mayor impacto en el tiempo de carga, ya que realiza el mayor trabajo de sobrecarga: carga y aumenta vistas, e inicializa los objetos necesarios para que se ejecute la actividad.

Inicio semicaliente

El inicio semicaliente comprende un subconjunto de las operaciones que se llevan a cabo durante el inicio en frío. Al mismo tiempo, representa una sobrecarga mayor que en el caso del inicio en caliente. Muchos estados potenciales pueden ser considerados como inicios semicalientes, por ejemplo:

  • El usuario cancela la operación de tu app, pero luego vuelve a iniciarla. La ejecución del proceso podría continuar, pero la app debe recrear la actividad desde cero llamando a onCreate().

  • El sistema quita la app de la memoria y, luego, el usuario vuelve a iniciarla. Es necesario reiniciar el proceso y la actividad, pero, de alguna manera, la tarea puede aprovechar el paquete de estado de la instancia guardada, que se pasó a onCreate().

Inicio en caliente

Un inicio en caliente de la app tiene una sobrecarga menor que un inicio en frío. En un inicio en caliente, el sistema lleva tu actividad al primer plano. Si todas las actividades de la app están todavía en la memoria, la app puede evitar tener que repetir la inicialización del objeto, el aumento del diseño y la renderización.

Sin embargo, si se borró definitivamente una parte de la memoria en respuesta a eventos de recorte de memoria, como onTrimMemory(), será necesario recrear esos objetos como respuesta al evento de inicio en caliente.

El inicio en caliente muestra el mismo comportamiento en pantalla que el inicio en frío. El proceso del sistema muestra una pantalla en blanco hasta que la app termina de renderizar la actividad.

Figura 2: Un diagrama con los diferentes estados de inicio y sus respectivos procesos, cada uno de los cuales comienza desde el primer fotograma.

Cómo identificar el inicio de una app en Perfetto

Para depurar los problemas de inicio de la app, es útil determinar qué se incluye exactamente en la fase de inicio de la app. Para identificar toda la fase de inicio de la app en Perfetto, sigue estos pasos:

  1. En Perfetto, busca la fila con la métrica derivada de inicio de la app para Android. Si no la ves, intenta capturar un registro con la app de registro del sistema integrado en el dispositivo.

    Figura 3: Porción de métrica derivada de inicio de la app para Android en Perfetto.
  2. Haz clic en la porción asociada y presiona m para seleccionarla. Aparecen corchetes alrededor de la porción para indicar cuánto tiempo tardó. La duración también se muestra en la pestaña Selección actual.

  3. Para fijar la fila de inicio de la app para Android, haz clic en el ícono de fijar, que se muestra cuando pones el puntero sobre la fila.

  4. Desplázate hasta la fila en la que aparece la app en cuestión y haz clic en la primera celda para expandir la fila.

  5. Para acercar el subproceso principal, por lo general en la parte superior, presiona w (presiona s, a, d para alejar, mover hacia la izquierda y hacia la derecha, respectivamente).

    Figura 4: Porción de métrica derivada de inicio de la app para Android junto al subproceso principal de la app
  6. La porción de métricas derivadas facilita ver qué se incluye exactamente en el inicio de la app para que puedas continuar depurando con más detalle.

Usa métricas para inspeccionar y mejorar las startups

Si quieres diagnosticar de manera adecuada el rendimiento del tiempo de inicio, puedes registrar métricas que muestren cuánto tarda tu app en iniciarse. Android ofrece varios medios para mostrarte que tu app tiene un problema y te ayuda a diagnosticarlo. Android vitals puede alertarte cuando se produce un problema, y las herramientas de diagnóstico pueden ayudarte a identificarlo.

Beneficios de usar métricas de inicio

Android usa las métricas de tiempo para la visualización inicial (TTID) y tiempo para la visualización completa (TTFD) para optimizar los inicios de apps semicalientes y en frío. Android Runtime (ART) usa los datos de estas métricas para precompilar código de forma eficiente y optimizar los inicios en el futuro.

Los inicios más rápidos generan una interacción más prolongada de los usuarios con tu app, lo cual reduce las instancias en las que se sale de la app con antelación, se reinicia la instancia o se cambia a otra app.

Android vitals

Android vitals puede ayudarte a mejorar el rendimiento de tu app. Para ello, te envía alertas a través de Play Console cuando los tiempos de inicio de tu app son excesivos.

Android vitals considera excesivos los siguientes tiempos de inicio para tu app:

Android vitals usa la métrica de tiempo para la visualización inicial (TTID). Si deseas obtener información sobre cómo Google Play recopila datos de Android vitals, consulta la documentación de Play Console.

Tiempo para la visualización inicial

El tiempo para la visualización inicial (TTID) es el tiempo que lleva mostrar el primer fotograma de la IU de la app. Esta métrica mide el tiempo que tarda una app en producir su primer fotograma, lo que incluye la inicialización del proceso durante un inicio en frío, la creación de actividad durante un inicio en frío o semicaliente y la visualización del primer fotograma. Si el TTID de tu app es bajo, ayudas a mejorar la experiencia del usuario, ya que permite que esta se inicie rápidamente. El framework de Android informa automáticamente el TTID para cada app. Cuando realices optimizaciones para el inicio de la app, te recomendamos implementar reportFullyDrawn para obtener información de hasta TTFD.

El TTID se mide como un valor de tiempo que representa el tiempo total transcurrido que incluye la siguiente secuencia de eventos:

  • Iniciar el proceso
  • Inicializa los objetos.
  • Crear e inicializar la actividad
  • Aumentar el diseño
  • Dibujar la app por primera vez

Recuperar TTID

Para encontrar el TTID, busca en la herramienta de línea de comandos Logcat una línea de resultado que contenga un valor llamado Displayed. Este valor es el TTID y es similar al siguiente ejemplo, en el que el TTID es de 3s534 ms:

ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms

Para encontrar el TTID en Android Studio, inhabilita los filtros en la vista de Logcat desde el menú desplegable de filtros y, luego, busca el tiempo de Displayed, como se muestra en la Figura 5. Esto es necesario porque el servidor del sistema, no la app misma, entrega este registro.

Figura 5: Se inhabilitaron los filtros y el valor Displayed en logcat.

La métrica Displayed de los resultados de Logcat no necesariamente captura el tiempo transcurrido hasta que se cargan y muestran todos los recursos. Se omiten los recursos a los que no se hace referencia en el archivo de diseño o que la app crea como parte de la inicialización del objeto. Excluye estos recursos porque cargarlos es un proceso intercalado, y no se bloquea la pantalla inicial de la app.

En ocasiones, la línea Displayed de los resultados de Logcat contiene un campo adicional para el tiempo total. Por ejemplo:

ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms (total +1m22s643ms)

En este caso, la primera medición de tiempo se realiza solo para la actividad que se inició primero. La medición de tiempo total comienza cuando se inicia el proceso de la app y puede incluir otra actividad que se haya iniciado primero, pero que no mostró nada en la pantalla. Solo se muestra la medición de tiempo total cuando hay una diferencia entre los tiempos de inicio de la actividad y el tiempo total de inicio.

Te recomendamos que uses Logcat en Android Studio, pero si no lo usas, también puedes medir el TTID ejecutando tu app con el comando adb del administrador de actividades de shell. Por ejemplo:

adb [-d|-e|-s <serialNumber>] shell am start -S -W
com.example.app/.MainActivity
-c android.intent.category.LAUNCHER
-a android.intent.action.MAIN

La métrica Displayed aparece en el resultado de Logcat como antes. En la ventana de terminal, se muestra lo siguiente:

Starting: Intent
Activity: com.example.app/.MainActivity
ThisTime: 2044
TotalTime: 2044
WaitTime: 2054
Complete

Los argumentos -c y -a son opcionales y te permiten especificar <category> y <action>.

Tiempo para la visualización completa

El tiempo para la visualización completa (TTFD) es el tiempo que tarda una app en ser interactiva para el usuario. Se informa como el tiempo que lleva mostrar el primer fotograma de la IU de la app, así como el contenido que se carga de forma asíncrona después de que se muestra el fotograma inicial. Por lo general, este es el contenido principal que se carga desde la red o el disco, según lo informa la app. En otras palabras, el TTFD incluye el TTID, así como el tiempo que tarda en usarse la app. Mantener el TTFD de tu app bajo ayuda a mejorar la experiencia del usuario, ya que permite que interactúen con ella rápidamente.

El sistema determina el TTID cuando Choreographer llama al método onDraw() de la actividad y cuando sabe que lo llama por primera vez. Sin embargo, el sistema no sabe cuándo determinar el TTFD porque cada app se comporta de manera diferente. Para determinar el TTFD, la app debe indicarle al sistema cuando alcanza el estado de obtenida por completo.

Recuperar TTFD

Para encontrar el TTFD, indica el estado dibujado por completo llamando al método reportFullyDrawn() de ComponentActivity. El método reportFullyDrawn informa cuándo la app está completamente cargada y en estado utilizable. El TTFD es el tiempo que transcurre desde que el sistema recibe el intent de inicio de la app hasta que se llama a reportFullyDrawn(). Si no llamas a reportFullyDrawn(), no se informa ningún valor de TTFD.

Para medir el TTFD, llama a reportFullyDrawn() después de dibujar por completo la IU y todos los datos. No llames a reportFullyDrawn() antes de que se dibuje y muestre por primera vez la ventana de la primera actividad según la medición del sistema, ya que este informará el tiempo medido. En otras palabras, si llamas a reportFullyDrawn() antes de que el sistema detecte el TTID, el sistema informa tanto el TTID como el TTFD como el mismo valor, y este valor es el de TTID.

Cuando usas reportFullyDrawn(), Logcat muestra un resultado como el siguiente, en el que el TTFD es de 1s54 ms:

system_process I/ActivityManager: Fully drawn {package}/.MainActivity: +1s54ms

En ocasiones, el resultado de Logcat incluye un tiempo total, como se describe en Tiempo para la visualización inicial.

Si los tiempos de visualización son más lentos de lo que deseas, puedes intentar identificar los cuellos de botella en el proceso de inicio.

Puedes usar reportFullyDrawn() para indicar el estado dibujado por completo en casos básicos en los que sepas que se logró el estado dibujado por completo. Sin embargo, en los casos en que los subprocesos en segundo plano deben completar el trabajo en segundo plano antes de que se alcance el estado dibujado por completo, deberás retrasar reportFullyDrawn() para obtener una medición del TTFD más precisa. Para obtener información sobre cómo retrasar reportFullyDrawn(), consulta la siguiente sección.

Mejora la precisión de los tiempos de inicio

Si tu app realiza una carga diferida y la visualización inicial no incluye todos los recursos, como cuando la app recupera imágenes de la red, te recomendamos que retrases la llamada a reportFullyDrawn hasta que pueda usarse la app para incluir la propagación de la lista como parte del tiempo de tus comparativas.

Por ejemplo, si la IU contiene una lista dinámica, como RecyclerView, o lista diferida, esto podría propagarse con una tarea en segundo plano que se completa después de que se dibuja la lista por primera vez y, por lo tanto, después de que la IU se marca como obtenida por completo. En tales casos, la propagación de la lista no se incluye en las comparativas.

Para incluir la propagación de la lista como parte del tiempo de tus comparativas, obtén FullyDrawnReporter con getFullyDrawnReporter() y agrégale un generador de informes al código de tu app. Lanza el generador de informes después de que la tarea en segundo plano termine de propagar la lista.

FullyDrawnReporter no llama al método reportFullyDrawn() hasta que se lanzan todos los generadores de informes agregados. Cuando se agrega un generador de informes hasta que se completa el proceso en segundo plano, los tiempos también incluyen la cantidad de tiempo que se tarda en propagar la lista en los datos de tiempo de inicio. Esto no cambia el comportamiento de la app para el usuario, pero permite que los datos de inicio de tiempo incluyan el tiempo que tarda en propagarse a la lista. No se llama a reportFullyDrawn() hasta que se completan todas las tareas, independientemente del orden.

En el siguiente ejemplo, se muestra cómo puedes ejecutar varias tareas en segundo plano de forma simultánea, cada una de las cuales registra su propio generador de informes:

Kotlin

class MainActivity : ComponentActivity() {

    sealed interface ActivityState {
        data object LOADING : ActivityState
        data object LOADED : ActivityState
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            var activityState by remember {
                mutableStateOf(ActivityState.LOADING as ActivityState)
            }
            fullyDrawnReporter.addOnReportDrawnListener {
                activityState = ActivityState.LOADED
            }
            ReportFullyDrawnTheme {
                when(activityState) {
                    is ActivityState.LOADING -> {
                        // Display the loading UI.
                    }
                    is ActivityState.LOADED -> {
                        // Display the full UI.
                    }
                }
            }
            SideEffect {
                lifecycleScope.launch(Dispatchers.IO) {
                    fullyDrawnReporter.addReporter()

                    // Perform the background operation.

                    fullyDrawnReporter.removeReporter()
                }
                lifecycleScope.launch(Dispatchers.IO) {
                    fullyDrawnReporter.addReporter()

                    // Perform the background operation.

                    fullyDrawnReporter.removeReporter()
                }
            }
        }
    }
}

Java

public class MainActivity extends ComponentActivity {
    private FullyDrawnReporter fullyDrawnReporter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        fullyDrawnReporter = getFullyDrawnReporter();
        fullyDrawnReporter.addOnReportDrawnListener(() -> {
            // Trigger the UI update.
            return Unit.INSTANCE;
        });

        new Thread(new Runnable() {
            @Override
            public void run() {
                fullyDrawnReporter.addReporter();

                // Do the background work.

               fullyDrawnReporter.removeReporter();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                fullyDrawnReporter.addReporter();

                // Do the background work.

                fullyDrawnReporter.removeReporter();
            }
        }).start();
    }
}

Si tu app usa Jetpack Compose, puedes usar las siguientes APIs para indicar el estado dibujado por completo:

  • ReportDrawn: Indica que el elemento componible está listo de inmediato para interactuar.
  • ReportDrawnWhen: Toma un predicado, como list.count > 0, para indicar cuándo el elemento componible está listo para interactuar.
  • ReportDrawnAfter: Toma un método de suspensión que, una vez que se completa, indica que el elemento componible está listo para interactuar.
Cómo identificar cuellos de botella

Para detectar cuellos de botella, puedes usar el Generador de perfiles de CPU de Android Studio. Para obtener más información, consulta Cómo inspeccionar la actividad de la CPU con el Generador de perfiles de CPU.

Además, puedes obtener información sobre los posibles cuellos de botella mediante el seguimiento intercalado de los métodos onCreate() de tus apps y actividades. Para obtener más información sobre el seguimiento intercalado, consulta la documentación sobre las funciones de Trace y la descripción general del registro del sistema.

Cómo resolver problemas habituales

En esta sección, se analizan varios problemas que, a menudo, afectan el rendimiento de inicio de las apps. Por lo general, estos problemas están relacionados con la inicialización de apps y objetos de actividades, además de con la carga de pantallas.

Inicialización de app intensa

El rendimiento del inicio puede verse afectado cuando tu código anula el objeto Application y ejecuta trabajo pesado o lógica compleja cuando se inicializa ese objeto. Es posible que tu app pierda tiempo durante el inicio si las subclases de Application realizan inicializaciones que aún no son necesarias.

Algunas de estas pueden ser completamente innecesarias, como cuando se inicializa la información de estado para la actividad principal cuando la app se inicia en respuesta a un intent. Con un intent, la app usa solo un subconjunto de los datos de estado inicializados con anterioridad.

Otros desafíos que se presentan durante la inicialización de una app incluyen eventos de recolección de elementos no utilizados, que tienen un gran impacto o son numerosos, o E/S de disco que se producen junto con la inicialización y bloquean el proceso aún más. La recolección de elementos no utilizados se debe tener en cuenta específicamente para el tiempo de ejecución de Dalvik; el tiempo de ejecución de Android Runtime (ART) realiza la recolección de elementos no utilizados en simultáneo, lo que minimiza el impacto de esa operación.

Cómo diagnosticar el problema

Puedes usar el seguimiento de métodos o el registro en línea para diagnosticar el problema.

Seguimiento de métodos

Cuando se ejecuta el Generador de perfiles de CPU, se descubre que el método callApplicationOnCreate() llama finalmente a tu método com.example.customApplication.onCreate. Si la herramienta muestra que la ejecución de estos métodos tarda demasiado en completarse, sigue explorando para descubrir qué trabajo se está desarrollando.

Seguimiento intercalado

Usa el seguimiento intercalado para investigar posibles culpables, incluidos los siguientes:

  • La función onCreate() inicial de tu app
  • Cualquier objeto singleton que tu app inicializa
  • Cualquier E/S de disco, deserialización o bucle cerrado que pueda producirse durante el cuello de botella

Soluciones para el problema

Si el problema se relaciona con inicializaciones innecesarias o E/S de disco, la solución es la inicialización diferida. En otras palabras, solo inicializa los objetos que son inmediatamente necesarios. En lugar de crear objetos globales estáticos, cambia a un patrón singleton, en el que la app inicializa objetos solo la primera vez que los necesita.

Además, considera usar un framework de inyección de dependencias, como Hilt, que crea objetos y dependencias cuando se los inyecta por primera vez.

Si la app usa proveedores de contenido para inicializar los componentes de la app al inicio, considera usar la biblioteca de App Startup.

Inicialización de actividad intensa

A menudo, la creación de actividades implica mucho trabajo de sobrecarga alta. En general, hay oportunidades de optimizar este trabajo para obtener mejoras de rendimiento. Estos son algunos problemas habituales:

  • Ampliación de diseños grandes o complejos
  • Bloqueo de la pantalla cuando se escribe en el disco, o E/S de red.
  • Carga y decodificación de mapas de bits
  • Rasterización de objetos VectorDrawable
  • Inicialización de otros subsistemas de la actividad

Cómo diagnosticar el problema

También en este caso, tanto el registro de métodos como el registro en línea pueden ser útiles.

Seguimiento de métodos

Cuando uses el Generador de perfiles de CPU, presta atención a los constructores de la subclase Application y los métodos com.example.customApplication.onCreate() de tu app.

Si la herramienta muestra que la ejecución de estos métodos tarda demasiado en completarse, sigue explorando para descubrir qué trabajo se está desarrollando.

Seguimiento intercalado

Usa el seguimiento intercalado para investigar posibles culpables, incluidos los siguientes:

  • La función onCreate() inicial de tu app
  • Cualquier objeto singleton que tu app inicializa
  • Cualquier E/S de disco, deserialización o bucle cerrado que pueda producirse durante el cuello de botella

Soluciones para el problema

Si bien hay varios cuellos de botella posibles, los siguientes son dos problemas y soluciones habituales:

  • Cuando más grande es tu jerarquía de vistas, más tarda la app en ampliarla. Puedes seguir dos pasos para abordar este problema:
    • Acopla la jerarquía de vistas reduciendo los diseños redundantes o anidados.
    • No amplíes partes de la IU que no necesiten ser visibles durante el inicio. En su lugar, usa un objeto ViewStub como marcador de posición para subjerarquías que la app pueda ampliar en un tiempo más apropiado.
  • Tener toda la inicialización de recursos en el subproceso principal también puede hacer que el inicio sea más lento. Puedes tratar este problema de la siguiente manera:
    • Transfiere toda la inicialización de recursos para que la app pueda llevarla a cabo de manera diferida en otro subproceso.
    • Permite que la app cargue y muestre las vistas y, luego, actualiza las propiedades visuales que dependen de mapas de bits y otros recursos.

Pantallas de presentación personalizadas

Es posible que observes tiempo adicional durante el inicio si ya usaste uno de los siguientes métodos para implementar una pantalla de presentación personalizada en Android 11 (nivel de API 30) o versiones anteriores:

  • Usar el atributo del tema windowDisablePreview para desactivar la pantalla en blanco inicial que dibuja el sistema durante el inicio
  • Usar una Activity dedicada

A partir de Android 12, se requiere migrar a la API de SplashScreen. Esta API permite un tiempo de inicio más rápido y te permite ajustar la pantalla de presentación de las siguientes maneras:

Además, la biblioteca de compatibilidad proporciona portabilidad a versiones anteriores de la API de SplashScreen para permitir la retrocompatibilidad y crear una apariencia coherente para la visualización de la pantalla de presentación en todas las versiones de Android.

Consulta la guía de migración de la pantalla de presentación para obtener más detalles.