Cómo evitar descargas redundantes

Muchos de los usuarios de tu app tienen acceso intermitente a Internet o tienen límites en la cantidad de información que pueden descargar en sus dispositivos. Puedes alentar a los usuarios a interactuar con tu app más a menudo reduciendo la cantidad de datos que tu app necesita descargar.

La forma más simple de reducir las descargas es descargar solo lo que necesitas. En términos de datos, eso significa que implementar API de REST que permiten especificar criterios de consulta que limitan los datos devueltos mediante el uso de parámetros, como el momento de la última actualización.

Del mismo modo, cuando se descarguen imágenes, es aconsejable reducir el tamaño de los elementos del lado del servidor, en lugar de descargar imágenes de tamaño completo que se reducen en el cliente.

Cómo almacenar localmente los archivos en caché

Otra técnica importante es evitar la descarga de datos duplicados. Puedes reducir la probabilidad de descargar los mismos datos de manera repetida mediante el almacenamiento en caché. Si almacenas en caché los datos y recursos de tu app, crea una copia local de la información a la que la app necesita hacer referencia. Si la app necesita acceder a la misma información varias veces durante un corto período de tiempo, solo deberás descargarla en la memoria caché una vez.

Es importante almacenar en caché de la forma más agresiva posible con el fin de reducir la cantidad total de datos de descarga. Siempre almacena en caché los recursos estáticos, incluidas las descargas a pedido, como imágenes de tamaño completo, durante el mayor tiempo posible. Los recursos a pedido se deben almacenar por separado para permitirte vaciar regularmente la caché a pedido, a fin de administrar el tamaño.

Para garantizar que el almacenamiento en caché no haga que tu app muestre datos obsoletos, asegúrate de extraer la hora a la que se actualizó por última vez el contenido solicitado, y cuando caduca, desde los encabezados de respuesta HTTP. Esto te permitirá determinar cuándo se debe actualizar el contenido asociado.

Kotlin

    val conn: HttpsURLConnection = url.openConnection() as HttpsURLConnection
    val currentTime: Long = System.currentTimeMillis()
    val expires: Long = conn.getHeaderFieldDate("Expires", currentTime)
    val lastModified: Long = conn.getHeaderFieldDate("Last-Modified", currentTime)
    if (lastModified < lastUpdateTime) {
        // Skip update
    } else {
        // Parse update
        lastUpdateTime = lastModified
    }
    

Java

    // url represents the website containing the content to place into the cache.
    HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();

    long currentTime = System.currentTimeMillis();
    long expires = conn.getHeaderFieldDate("Expires", currentTime);
    long lastModified = conn.getHeaderFieldDate("Last-Modified", currentTime);

    // lastUpdateTime represents when the cache was last updated.
    if (lastModified < lastUpdateTime) {
        // Skip update
    } else {
        // Parse update
        lastUpdateTime = lastModified;
    }
    

Con este enfoque, también puedes almacenar en caché contenido dinámico de forma eficaz y, a la vez, asegurarte de que la aplicación no muestre información obsoleta.

Puedes almacenar en caché datos no sensibles en el directorio de caché externa no administrada, obtenidos a través de Context#getExternalCacheDir().

Como opción, puedes usar la caché de aplicación administrada y segura, que se obtiene a través de Contexto#getCacheDir(). Ten en cuenta que esta caché interna se puede vaciar cuando el sistema se ejecuta con poco almacenamiento disponible.

Nota: Los archivos almacenados en la ubicación de la caché se borran cuando se desinstala la app.

Cómo usar la caché de respuesta de HttpsURLConnection

Android 4.0 agregó una memoria caché de respuesta a HttpsURLConnection. Puedes habilitar el almacenamiento en caché de respuesta HTTP en dispositivos compatibles utilizando la reflexión de la siguiente manera:

Kotlin

    private fun enableHttpResponseCache() {
        try {
            val httpCacheSize: Long = 10L * 1024L * 1024L // 10 MiB
            val httpCacheDir = File(cacheDir, "http")
            Class.forName("android.net.http.HttpResponseCache")
                    .getMethod("install", File::class.java, Long::class.javaPrimitiveType)
                    .invoke(null, httpCacheDir, httpCacheSize)
        } catch (httpResponseCacheNotAvailable: Exception) {
            Log.d(TAG, "HTTP response cache is unavailable.")
        }
    }
    

Java

    private void enableHttpResponseCache() {
      try {
        long httpCacheSize = 10 * 1024 * 1024; // 10 MiB
        File httpCacheDir = new File(getCacheDir(), "http");
        Class.forName("android.net.http.HttpResponseCache")
             .getMethod("install", File.class, long.class)
             .invoke(null, httpCacheDir, httpCacheSize);
      } catch (Exception httpResponseCacheNotAvailable) {
        Log.d(TAG, "HTTP response cache is unavailable.");
      }
    }
    

En este código de ejemplo, se activará la caché de respuesta en dispositivos que ejecuten Android 4.0 y versiones posteriores sin afectar las versiones anteriores.

Con la caché instalada, las solicitudes HTTP completamente almacenadas en caché se pueden atender desde el almacenamiento local, lo que elimina la necesidad de abrir una conexión de red. Las respuestas en caché condicional pueden validar su actualización desde el servidor, lo que elimina el costo de ancho de banda asociado con la descarga.

Las respuestas no almacenadas en caché se almacenan en la memoria caché de respuesta para futuras solicitudes.