¿Qué palancas tienes para reducir el tamaño de un APK/IPA?
Los binarios más pequeños se instalan de forma más confiable en redes limitadas y equipos de gama baja, y los mismos tres movimientos — reducir código, dividir por dispositivo, diferir assets — aparecen en Android y en iOS con nombres distintos.
APRENDE HACIENDOPon a prueba tu intuición3 pasos
¿Qué palancas tienes para reducir el tamaño de un APK/IPA?
Usa los ejemplos y la explicación de la lección para comprobar tu idea.
Reflexión personal. No se envía a ningún servicio de IA.#Por qué el tamaño es una métrica de producto, no solo un número de build
El tamaño de instalación aparece en los embudos de conversión mucho antes de aparecer en un reporte de crashes: en una conexión lenta o medida, una descarga grande es donde un usuario abandona la instalación, y tanto Google Play como la App Store muestran el tamaño de forma prominente antes de que un usuario decida tocar instalar. Por debajo de cierto umbral de tamaño, Play solía (y en espíritu sigue haciéndolo) tratar a una app como elegible para más mercados y mejor posicionamiento en regiones de bajo ancho de banda. Así que "reducir el tamaño del APK/IPA" no es una tarea de limpieza única — es un presupuesto continuo que defiendes de la misma forma en que defenderías un presupuesto de performance, porque cada nueva dependencia, cada PNG sin convertir y cada regla de keep no usada le va restando en silencio.
#Reducir el código que envías
En Android, R8 (el shrinker por defecto desde Android Gradle Plugin 3.4, un sucesor directo de ProGuard que sigue leyendo archivos proguard-rules.pro) hace tres cosas en un solo paso cuando isMinifyEnabled = true: el tree-shaking elimina clases y métodos a los que nada alcanzable llama, la minificación renombra lo que queda a nombres cortos, y un paso de optimización separado inlinea métodos triviales y fusiona clases cuando es seguro hacerlo. El problema con el que todo equipo se topa alguna vez: el código basado en reflexión — clases modelo de Gson/Moshi, generadores de Parcelable, cualquier cosa encontrada por nombre de clase en tiempo de ejecución — le parece inalcanzable al análisis estático de R8 y se elimina o renombra a menos que agregues una regla -keep explícita, por lo que un build que se reduce sin problemas en debug puede fallar en release con un ClassNotFoundException. isShrinkResources = true (que requiere que la minificación esté activada) hace lo análogo para los recursos: descarta drawables, layouts y strings que nada en el código reducido o en el manifest sigue referenciando, además de los conjuntos de recursos de densidad o idioma alternativos que hayas excluido explícitamente vía resourceConfigurations.
En iOS, la palanca equivalente es el dead-code stripping combinado con la optimización de módulo completo (whole-module optimization): el linker elimina símbolos no referenciados, y compilar con -Osize (en lugar de -O, que optimiza para velocidad) le indica al compilador que prefiera código más pequeño incluso a un pequeño costo en tiempo de ejecución. A diferencia del problema de las reglas de keep en Android, el despacho dinámico de Objective-C y la reflexión de Swift (Mirror) crean trampas similares de "esto parece no usarse pero se llama por nombre", por lo que el dead-code stripping agresivo se valida contra el uso real, no se asume seguro sin más.
#Dividir la descarga por dispositivo, no por lista de funcionalidades
La palanca individual más grande en ambas plataformas es no enviar cada variante de dispositivo a cada dispositivo. El formato App Bundle (.aab) de Android reemplazó el modelo de "un APK universal con cada ABI, densidad e idioma incluidos": subes un solo bundle, y la entrega dinámica de Google Play genera y sirve un APK optimizado por cada dispositivo que lo solicita — solo su ABI, solo su densidad de pantalla, solo sus idiomas — así que los bytes que un usuario realmente descarga son una fracción de lo que habría enviado un APK universal. El App Thinning de iOS logra el equivalente conceptual mediante slicing: un archive de la App Store se corta por arquitectura de dispositivo y clase de pantalla en el momento de la distribución, así que un iPhone descarga solo el slice que necesita en lugar de un binario gordo compilado para cada dispositivo que Apple haya lanzado alguna vez. Ninguna de las dos reduce el tamaño de tu artefacto fuente — el .aab o el archive todavía pueden ser grandes — pero ambas reducen drásticamente lo que el teléfono de un usuario real realmente descarga, que es el número que importa para la conversión.
#Diferir lo que no se necesita el primer día
Más allá de dividir por dispositivo, ambas plataformas te permiten dividir por cuándo se necesita el contenido. Play Feature Delivery de Android permite marcar un módulo como on-demand o condicional, descargado solo si un usuario llega a la funcionalidad que lo necesita (o solo en dispositivos que cumplen una condición, como un nivel mínimo de API o soporte para cierta característica de hardware) en lugar de incluirlo en la instalación inicial. Los On-Demand Resources (ODR) de iOS hacen lo mismo pero para assets en vez de código: etiquetas un conjunto de imágenes, sonidos o datos de nivel como on-demand, y el sistema operativo los descarga desde el CDN de Apple solo cuando la app solicita esa etiqueta en tiempo de ejecución, purgándolos de nuevo cuando ya no se necesitan — ideal para contenido de niveles avanzados o assets de funcionalidades poco usadas que de otro modo estarían en cada instalación, la use o no el usuario.
#Los assets suelen ser el peso real, no el código
Para la mayoría de las apps, las imágenes eclipsan al código compilado en bytes crudos, así que las decisiones de formato y entrega de assets suelen mover la aguja más que cualquier paso de reducción de código. En Android: preferir WebP (tanto en modo con pérdida como sin pérdida, típicamente le gana a PNG por un 25–35% con calidad comparable) o un formato moderno como AVIF para contenido fotográfico, y usar VectorDrawable para íconos e ilustraciones simples en lugar de enviar PNGs separados desde mdpi hasta xxxhdpi — un solo archivo XML pequeño escala sin pérdida a cada densidad. En iOS: preferir HEIC sobre PNG/JPEG para assets fotográficos incluidos en el bundle, y usar PDFs vectoriales con "Preserve Vector Data" en un asset catalog en lugar de enviar copias raster @1x/@2x/@3x de cada ícono. En ambas plataformas vale la pena correr periódicamente una auditoría de recursos no usados — el check de lint UnusedResources de Android (con tools:keep/tools:discard para todo lo que solo se encuentre vía Resources.getIdentifier(), que el análisis estático no puede ver) y un barrido manual del asset catalog de Xcode en busca de imágenes que ya nada referencia — porque los assets sin usar se acumulan en silencio a medida que se eliminan funcionalidades y nadie recuerda borrar sus drawables.
| Palanca | Android | iOS | Qué ahorra principalmente |
|---|---|---|---|
| Reducción de código | R8 (tree-shaking, minificación, optimización) vía isMinifyEnabled | Dead-code stripping + optimización de módulo completo con -Osize | Tamaño del código compilado |
| Entrega por dispositivo | Android App Bundle → Play genera APKs divididos por dispositivo | App Thinning → App Store hace slicing por arquitectura/pantalla | Bytes realmente descargados |
| Contenido diferido/opcional | Play Feature Delivery (módulos on-demand/condicionales) | On-Demand Resources (ODR) | Tamaño de instalación inicial |
| Reducción de recursos | isShrinkResources + lint UnusedResources | Auditorías manuales/de asset catalog de imágenes sin usar | Tamaño de recursos incluidos |
| Formato de imagen | WebP/AVIF, VectorDrawable para íconos | HEIC, PDFs vectoriales en asset catalogs | Tamaño de los assets |
| Splits nativos/de arquitectura | abiFilters / splits de ABI del App Bundle | Slicing por arquitectura (p. ej., solo arm64 donde se soporte) | Tamaño de binarios nativos |
| Higiene de dependencias | Eliminar librerías duplicadas/sin usar (./gradlew :app:dependencies) | Recortar frameworks/pods sin usar, evitar frameworks dinámicos pesados | Código + overhead fijo por framework |
| Medir el resultado | APK Analyzer, bundletool get-size total | Xcode Organizer App Size Report / App Thinning Size Report | Verifica que lo anterior funcionó de verdad |