Monitoreo y Optimización de Tasa de Fotogramas en Android

Introducción

La optimización del rendimiento en aplicaciones Android es un aspecto crucial en el proceso de desarrollo, y la tasa de fotogramas (Frame Rate) es un indicador clave para evaluar el rendimiento de una aplicación. En este artículo, exploraremos en profundidad cómo monitorear la tasa de fotogramas de aplicaciones Android y cómo optimizar el rendimiento a través de ejemplos de código.

¿Qué es la Tasa de Fotogramas

La tasa de fotogramas se refiere al número de imágenes que una aplicación puede renderizar por segundo. Normalmente se expresa en FPS (Frames Per Second). Por ejemplo, si una aplicación renderiza 60 imágenes por segundo, su tasa de fotogramas es de 60 FPS. Una tasa de fotogramas más alta se traduce en una experiencia de usuario más fluida, pero la estabilidad de la tasa de fotogramas es igualmente importante.

Importancia de la Tasa de Fotogramas

En la experiencia de usuario, el nivel de la tasa de fotogramas afecta directamente la velocidad de respuesta y los efectos visuales de la aplicación. Sin embargo, no solo se debe buscar una tasa de fotogramas alta, sino también prestar atención a su estabilidad. A continuación, exploraremos en detalle la importancia de estos dos aspectos.

Valor Absoluto de la Tasa de Fotogramas

El valor absoluto de la tasa de fotogramas representa el número de imágenes que una aplicación puede renderizar por segundo. Generalmente, una tasa de fotogramas más alta se asocia con una experiencia de usuario más fluida. ¿Por qué 60 FPS se ha convertido en un estándar? Esto se debe a las características visuales del o humano y la frecuencia de actualización de las pantallas electrónicas. La mayoría de los teléfonos y computadoras tienen una frecuencia de actualización de 60 Hz, lo que significa que actualizan el contenido en la pantalla 60 veces por segundo. Por lo tanto, cuando una aplicación puede renderizar imágenes a 60 FPS, coincide perfectamente con la frecuencia de actualización de la pantalla, proporcionando una experiencia muy fluida al usuario.

Si la tasa de fotogramas es inferior a 60 FPS, el usuario puede empezar a sentir tartamudeo o falta de fluidez, porque la aplicación no puede seguir el ritmo de actualización de la pantalla, lo que hace que las animaciones e interacciones no sean lo suficientemente suaves. Por lo tanto, el objetivo de 60 FPS se busca para lograr la mejor experiencia de usuario.

Estabilidad de la Tasa de Fotogramas

La estabilidad de la tasa de fotogramas se refiere a la variación de la tasa en un período de tiempo. Incluso si el valor absoluto de la tasa de fotogramas es bajo, si es muy estable, la experiencia de usuario puede seguir siendo buena. Por el contrario, incluso si el valor absoluto es alto, si es inestable, el usuario puede sentirse incómodo. Una tasa de fotogramas inestable puede manifestarse como temblores en la imagen o caídas repentinas en la tasa de fotogramas, lo que puede hacer que el usuario sienta que la aplicación se traba.

En general, la situación ideal es tener una tasa de fotogramas alta y estable. Sin embargo, en ciertas situaciones, si se debe elegir, la estabilidad puede ser más importante. Por ejemplo, en aplicaciones de realidad virtual (VR), una tasa de fotogramas estable es crucial para prevenir mareos y molestias. En aplicaciones normales, incluso si la tasa de fotogramas no es muy alta, si se puede matnener estable, el usuario puede percibirla como fluida.

Cómo Monitorear la Tasa de Fotogramas a Través del Código

El monitoreo de la tasa de fotogramas generalmente requiere insertar código en partes específicas de la aplicación para capturar información sobre la tasa de fotogramas. A continuación se presenta un ejemplo que utiliza la clase Choreographer de Android para monitorear la tasa de fotogramas:

public class MonitorRendimiento {
    private static final String ETIQUETA = "MonitorRendimiento";
    private static final long INTERVALO_MONITOREO = 1000;
    private static long ultimoTiempoFotogramaNanos = 0;
    private static long contadorFotogramas = 0;
    private static long inicioMonitoreo = 0;
    private static Choreographer.FrameCallback callbackFotograma;

    public static void iniciarMonitoreo() {
        inicioMonitoreo = SystemClock.elapsedRealtime();
        callbackFotograma = new Choreographer.FrameCallback() {
            @Override
            public void doFrame(long tiempoFotogramaNanos) {
                long tiempoActualNanos = tiempoFotogramaNanos;
                if (ultimoTiempoFotogramaNanos != 0) {
                    long tiempoFotogramaMilisegundos = (tiempoActualNanos - ultimoTiempoFotogramaNanos) / 1000000;
                    float tasaFotogramas = 1000f / tiempoFotogramaMilisegundos;
                    contadorFotogramas++;

                    long tiempoTranscurrido = SystemClock.elapsedRealtime() - inicioMonitoreo;

                    if (tiempoTranscurrido >= INTERVALO_MONITOREO) {
                        float tasaPromedioFotogramas = (contadorFotogramas / (tiempoTranscurrido / 1000f));
                        Log.d(ETIQUETA, "Tasa promedio de fotogramas en el último minuto: " + tasaPromedioFotogramas + " FPS");
                        contadorFotogramas = 0;
                        inicioMonitoreo = SystemClock.elapsedRealtime();
                    }
                }
                ultimoTiempoFotogramaNanos = tiempoActualNanos;
                Choreographer.getInstance().postFrameCallback(callbackFotograma);
            }
        };
        Choreographer.getInstance().postFrameCallback(callbackFotograma);
    }

    public static void detenerMonitoreo() {
        if (callbackFotograma != null) {
            Choreographer.getInstance().removeFrameCallback(callbackFotograma);
        }
        ultimoTiempoFotogramaNanos = 0;
        contadorFotogramas = 0;
        inicioMonitoreo = 0;
    }
}

En el ejemplo anterior, hemos creado una clase MonitorRendimiento que utiliza Choreographer para calcular periódicamente la tasa de fotogramas. Puedes llamar al método iniciarMonitoreo en las partes adecuadas de tu aplicación para iniciar el monitoreo de la tasa de fotogramas, y luego llamar a detenerMonitoreo cuando ya no sea necesario.

Técnicas de Optimización de Tasa de Fotogramas

Una vez que has identificado problemas de tasa de fotogramas en tu aplicación, el siguiente paso es optimizar. A continuación, presentamos algunas técnicas comunes de optimización de tasa de fotogramas, con ejemplos más detallados y análisis:

Reducción de Jerarquía de Vistas

Reducir la jerarquía de vistas es un método clave para mejorar la tasa de fotogramas al disminuir el anidamiento de vistas. El anidamiento de vistas hace que las operaciones de dibujo sean más complejas, lo que reduce la tasa de fotogramas. A continuación se presenta un ejemplo:

Estructura de vistas no óptima:

<RelativeLayout>
    <LinearLayout>
        <TextView />
        <ImageView />
    </LinearLayout>
</RelativeLayout>

En la estructura anterior, hay múltiples capas de anidamiento, lo que provoca dibujos innecesarios. La forma optimizada es reducir el anidamiento, como se muestra a continuación:

Estructura de vistas optimizada:

<androidx.constraintlayout.widget.ConstraintLayout>
    <TextView />
    <ImageView />
</androidx.constraintlayout.widget.ConstraintLayout>

Al reducir el anidamiento, se aligera la carga de dibujo, mejorando la tasa de fotogramas.

Uso de Aceleración por Hardware

Android proporciona aceleración por hardware para acelerar el renderizado gráfico. Para asegurarte de que tu aplicación aproveche al máximo la aceleración por hardware, puedes agregar android:hardwareAccelerated="true" en el archivo de layout XML o habilitar la aceleración por hardware en el código. A continuación se presenta un ejemplo:

<application android:hardwareAccelerated="true">
    <!-- Otras configuraciones de la aplicación -->
</application>

Habilitar la aceleración por hardware puede acelerar el dibujo de vistas, mejorando la tasa de fotogramas.

Tareas Asíncronas

Colocar las tareas que consumen mucho tiempo en un hilo de fondo evita que el hilo principal se bloquee, lo que podría reducir la tasa de fotogramas. Esto incluye solicitudes de red, operaciones de lectura/escritura de archivos, operaciones de base de datos, etc. A continuación se presenta un ejemplo que utiliza tareas asíncronas para manejar solicitudes de red:

import androidx.lifecycle.ViewModel;
import androidx.lifecycle.viewModelScope;
import kotlinx.coroutines.launch

class MiViewModel : ViewModel() {

    fun realizarSolicitudRed() {
        viewModelScope.launch {
            try {
                val resultado = obtenerDatosDesdeRed()
                // Manejar el resultado de la solicitud de red
            } catch (e: Exception) {
                // Manejar excepciones
            }
        }
    }

    private suspend fun obtenerDatosDesdeRed(): String {
        // Simular solicitud de red
        kotlinx.coroutines.delay(1000) // Retraso de 1 segundo, simulando el tiempo de la solicitud de red
        return "Datos de Red"
    }
}

Al ejecutar las solicitudes de red en un hilo de fondo, se evita que el hilo principal se bloquee, manteniendo la tasa de fotogramas estable.

Optimización de Imágenes y Animaciones

La optimización de los recursos de imágenes y animaciones en la aplicación es muy importante. Debes asegurarte de que las imágenes estén comprimidas y escaladas adecuadamente para reducir su tamaño. Además, el uso de gráficos vectoriales (Vector Drawables) garantiza que los iconos tengan buena calidad en diferentes densidades de pantalla. A continuación se presenta un ejemplo que utiliza gráficos vectoriales como iconos:

<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/ic_icono_vectorial" />

El uso de gráficos vectoriales puede reducir el tamaño de los recursos de imagen y mejorar la eficiencia del dibujo.

Gestión de Memoria

Una gestión adecuada de la memoria es crucial para mantener una tasa de fotogramas estable. Las fugas de memoria y la recolección frecuente de basura pueden provocar una disminución del rendimiento. Asegúrate de liberar referencias a objetos cuando no se utilicen y utiliza herramientas de análisis de memoria para detectar posibles fugas de memoria. A continuación se presenta un ejemplo que libera manualmente referencias a objetos que ya no son necesarias:

public class MiActividad extends Activity {
    private Bitmap mapaBitsGrande; // Objeto que necesita ser liberado

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Inicializar mapaBitsGrande
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // Liberar referencias de objetos al destruir la actividad
        if (mapaBitsGrande != null) {
            mapaBitsGrande.recycle();
            mapaBitsGrande = null;
        }
    }
}

Al liberar oportunamente las referencias a objetos, se reduce el uso de memoria, mejorando la tasa de fotogramas.

Uso de GPU para Dibujar

Intenta utilizar la GPU para las operaciones de dibujo, ya que es más eficiente que la CPU. Puedes usar OpenGL ES o SurfaceView de Android para el dibujo acelerado por GPU. A continuación se presenta un ejemplo que utiliza OpenGL ES para renderizar gráficos:

public class MiRenderizadorGL implements GLSurfaceView.Renderer {
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        // Inicializar entorno OpenGL
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        // Renderizar fotograma
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        // Manejar cambios de tamaño de vista
    }
}

Al utilizar la GPU para el dibujo, se acelera el renderizado gráfico, mejorando la tasa de fotogramas.

Casos de Estudio

A continuación se presentan algunos casos de estudio con análisis para una mejor comprensión de las soluciones a los problemas.

Alta Pérdida de Fotogramas

  • Los datos de monitoreo de tasa de fotogramas muestran una pérdida de fotogramas de un promedio de 60 FPS a 20 FPS, lo que causa que el usuario sienta tartamudeo en la aplicación.
  • Los datos de uso de CPU muestran que en momentos específicos, el uso de CPU del hilo principal alcanza el 90%, indicando una alta carga de CPU relacionada con la tartamudeo.
  • Los datos de uso de memoria muestran un aumento continuo en el uso de memoria, lo que sugiere una posible fuga de memoria.

Tartamudeo durante Solicitudes de Red

  • Los datos de monitoreo de tasa de fotogramas muestran claramente que el problema de tartamudeo ocurre cuando el usuario realiza solicitudes de red, con la tasa de fotogramas cayendo de 60 FPS a 10 FPS.
  • Los datos de uso de CPU indican que durante las solicitudes de red, el uso de CPU del hilo principal aumenta rápidamente al 100%.
  • Los datos de tiempo de respuesta muestran que las solicitudes de red tardan más de 5 segundos en responder, lo que confirma el problema con las solicitudes de red.

Fuga de Memoria que Causa Disminución del Rendimiento

  • El informe de herramientas de aálisis de memoria muestra claramente problemas de fuga de memoria en la aplicación, identificando objetos específicos y cadenas de referencia.
  • Los datos de monitoreo de tasa de fotogramas muestran que a medida que aumenta el uso de memoria, la tasa de fotogramas disminuye gradualmente, lo que finalmente resulta en una mala experiencia de usuario.

Alto Uso de GPU

  • Los datos de monitoreo de uso de GPU muestran que durante el renderizado gráfico, el uso de GPU se mantiene constantemente alto en un 90%, causando fluctuaciones significativas en la tasa de fotogramas.
  • Los datos de distribución del tiempo de renderizado muestran claramente que el tiempo de renderizado de algunos fotogramas es significativamente más largo, relacionado con el alto uso de GPU.

Alto Consumo de Batería

  • Los datos de monitoreo de consumo de batería muestran que la aplicación consume una gran cantidad de batería cuando se ejecuta en segundo plano, lo que reduce drásticamente el tiempo de autonomía del dispositivo.
  • Los datos de frecuencia de ejecución de tareas en segundo plano muestran claramente que algunas tareas en segundo plano se ejecutan con demasiada frecuencia, consumiendo gran cantidad de batería.

Etiquetas: Android rendimiento Tasa de Fotogramas optimización Monitoreo

Publicado el 7-8 16:20