PREGUNTAS Y RESPUESTAS
Android
¿Por qué Kotlin es una opción central para el desarrollo Android nativo?
Android permite Kotlin, Java y C++, pero Kotlin tiene soporte de primera clase en Android Studio y es el lenguaje recomendado para desarrollo Android moderno; Jetpack Compose se construye sobre Kotlin. Su sistema de tipos expresa nulabilidad y ayuda a detectar algunos errores antes de ejecutar, aunque no elimina la necesidad de validar datos externos. Ver Kotlin y Android.
¿Qué componentes forman una app Android?
El sistema reconoce cuatro tipos principales de componentes: Activity para interacción con UI, Service para trabajo sin UI visible, BroadcastReceiver para responder a broadcasts y ContentProvider para compartir datos mediante una interfaz controlada. Se declaran en AndroidManifest.xml cuando corresponde y cada uno tiene ciclo de vida propio; ninguno debe actuar como almacén de datos duraderos. Muchas apps modernas usan una sola Activity como contenedor de pantallas Compose, aunque es un patrón y no un requisito. Consulta fundamentos de aplicaciones Android y la guía de arquitectura.
¿Qué diferencia hay entre un Intent explícito e implícito?
Un Intent explícito nombra el componente destinatario; uno implícito declara una acción y datos para que Android encuentre una app capaz de resolverla, por ejemplo abrir un enlace o compartir contenido. Usa Intents explícitos para servicios internos sensibles, valida los datos recibidos y configura con cuidado qué componentes quedan exportados. Ver Intents y filtros y seguridad de componentes.
¿Qué significa el ciclo de vida de una Activity?
El sistema invoca callbacks como onCreate, onStart, onResume, onPause, onStop y onDestroy según la creación, visibilidad y foco de la actividad. Usa cada transición para inicializar o pausar recursos que dependan de ella, pero no supongas que siempre se llamará onDestroy antes de que el proceso termine. La guía del ciclo de vida muestra las transiciones y sus implicaciones.
¿Cómo conservar estado ante rotaciones y muerte del proceso?
Una rotación recrea normalmente la Activity; la muerte del proceso puede eliminar todo lo que solo estaba en memoria y no garantiza una última llamada de limpieza. Mantén estado de pantalla en un ViewModel para sobrevivir a cambios de configuración, guarda estado pequeño y restaurable con SavedStateHandle o el mecanismo de estado guardado, y persiste datos del usuario en la capa de datos. Ver guardar estado de UI.
¿Qué capas recomienda la arquitectura Android moderna?
Como base, separa UI y datos: la UI representa estado y envía acciones; la capa de datos contiene reglas de negocio y expone datos por repositorios. Una capa de dominio es opcional. Usa flujo unidireccional y modelos de estado inmutables para que el origen de los cambios sea claro y comprobable. Consulta la guía de arquitectura y sus recomendaciones.
¿Qué responsabilidad tiene un Repository?
Un Repository media entre la UI y fuentes como red, base local o preferencias; expone datos del dominio, centraliza cambios y resuelve caché o sincronización. Los ViewModels y composables no deberían consultar directamente cada fuente, porque el repositorio permite cambiar la implementación y probar la lógica sin acoplarla a la plataforma. Ver la guía oficial de la capa de datos.
¿Cuándo vale la pena una capa de dominio con casos de uso?
Añádela cuando una regla compleja o reutilizada por varios ViewModels se beneficie de una unidad con responsabilidad clara. No es requisito para toda app: si cada operación sencilla tiene una clase adicional, aumenta el acoplamiento y el mantenimiento sin aportar valor. La guía Android define la capa de dominio como opcional.
¿Cómo representa la UI Jetpack Compose?
Compose usa funciones @Composable para describir la interfaz a partir del estado actual. Cuando cambia un valor observable que una composición lee, las partes afectadas pueden recomponerse; por eso las funciones deben ser declarativas y no deben mutar el estado como efecto de la composición. Consulta la arquitectura de UI con Compose.
¿Qué son state hoisting, remember y rememberSaveable?
State hoisting significa mover el estado al ancestro común que necesita leerlo y modificarlo, dejando al composable recibir estado y emitir eventos; esto facilita reutilización y pruebas. remember conserva un objeto mientras la llamada permanece en la composición, mientras rememberSaveable puede restaurar valores compatibles tras recreación de la Activity. Ninguno sustituye al almacenamiento persistente de datos de negocio; ver estado en Compose.
¿Qué aporta un ViewModel y qué no debe contener?
ViewModel es un state holder de pantalla o lógica de negocio que expone el estado que la UI necesita y conserva ese estado durante cambios de configuración. No sobrevive por sí solo a muerte del proceso y no debería guardar referencias a Activity, View o Context ligado a pantalla; usa SavedStateHandle para pequeños datos restaurables y repositorios para datos persistentes. Consulta ViewModel y estado guardado.
¿Qué hace suspend y cómo mantener el hilo principal libre?
suspend permite que una coroutine se pause y continúe sin bloquear el hilo mientras espera una operación suspendible; no significa por sí mismo que el trabajo corra en segundo plano. Las funciones de datos deben ser main-safe: mueve I/O a Dispatchers.IO y cálculo intensivo a Dispatchers.Default, devolviendo el resultado al contexto adecuado para actualizar UI. Ver coroutines en Android.
¿Cómo ayuda la concurrencia estructurada a manejar solicitudes?
Vincula cada coroutine a un scope cuyo dueño y vida útil sean claros, como viewModelScope para trabajo de pantalla. Así, cuando el scope termina, las coroutines hijas se cancelan; propaga cancelación y errores, evita GlobalScope para trabajo normal y usa un scope externo explícito solo si la operación debe continuar más allá de la pantalla. Consulta prácticas recomendadas de coroutines.
¿Cuándo usar Flow o StateFlow y cómo observarlos en UI?
Una función suspend suele representar una consulta puntual; Flow representa valores que llegan en el tiempo y StateFlow mantiene un valor actual observable, útil para estado de pantalla. En Compose recolecta con collectAsStateWithLifecycle para detener o reanudar la observación según el ciclo de vida visible; en Views usa una recolección consciente del ciclo de vida. Ver flujo unidireccional en Compose y recomendaciones de UI.
¿Cómo evitar trabajo innecesario durante una recomposición?
Mide primero; divide UI por responsabilidades, mantén estable la identidad de elementos de listas con claves y evita cálculos costosos dentro de composables que pueden volver a ejecutarse. Compose puede omitir recomposiciones cuando conoce estabilidad y no cambian los parámetros relevantes, pero optimizar a ciegas o añadir remember por todas partes puede empeorar claridad sin resolver el cuello de botella. Consulta rendimiento en Compose.
¿Cómo diseñar para distintos tamaños de ventana y dispositivos?
Adapta la UI al espacio de ventana disponible, que puede cambiar por rotación, modo multiventana, plegado o cambio de tamaño, y no al modelo del dispositivo por sí solo. Usa clases de tamaño de ventana para decidir entre una o varias columnas y patrones de navegación; prueba tamaños compactos, medianos y expandidos. Las APIs y breakpoints pueden variar por versión; consulta apps adaptables.
¿Cómo hacer accesible una pantalla Compose?
Usa componentes estándar cuando sea posible porque ya exponen semántica; en UI personalizada define rol, etiqueta, estado y acciones mediante semantics. Prueba que lectores de pantalla puedan describir los controles y que haya un orden de navegación lógico, soporte de texto ampliado y objetivos táctiles utilizables. Consulta accesibilidad en Compose y semantics.
¿Cuándo usar Room y cómo evolucionar su esquema?
Room agrega una capa tipada sobre SQLite con entidades, DAO y consultas verificadas durante la compilación; es útil para datos estructurados locales, caché y uso sin conexión. Cada cambio de esquema publicado necesita una migración que conserve los datos del usuario, y las consultas no deben bloquear el hilo principal. Revisa la guía actual de Room.
¿Cómo diseñar una función offline-first?
Mantén una fuente local canónica para lecturas de la UI; el Repository sincroniza los cambios con la red cuando corresponde y expone estado de frescura o error. Define qué ocurre con escrituras sin conexión y cómo resolver conflictos, porque sincronización optimista no implica que el servidor siempre acepte el cambio. Consulta la guía oficial para apps offline-first.
¿Cómo realizar solicitudes de red sin bloquear la UI ni filtrar información?
Realiza operaciones de red fuera del hilo principal, controla timeout, errores HTTP, cancelación y estados de carga, y usa HTTPS con validación normal de certificados. Modela respuestas y errores en la capa de datos; no confíes en la disponibilidad de red como garantía de que el endpoint sea accesible. Ver coroutines para I/O y seguridad de red.
¿Cuándo usar WorkManager en vez de una coroutine de pantalla?
Usa una coroutine ligada al ViewModel para trabajo que solo importa mientras la pantalla esté activa. Para trabajo diferible que debe persistir si la app se cierra o el dispositivo se reinicia —por ejemplo, sincronizar una carga cuando haya red— evalúa WorkManager y expresa sus restricciones; el sistema decide cuándo ejecutarlo dentro de esas condiciones. Ver programación de trabajo persistente.
¿Cómo manejar permisos y minimizar acceso a datos del usuario?
Solicita el permiso en contexto, cuando el usuario inicia una función que lo necesita, explica su propósito y ofrece una alternativa si lo rechaza. Pide solo el acceso mínimo; para seleccionar fotos o videos, el Photo Picker puede evitar pedir acceso general a la biblioteca. Los permisos y comportamientos concretos dependen de la versión de Android; consulta la guía de permisos y Photo Picker.
¿Qué prácticas protegen datos y componentes Android?
Guarda datos privados en almacenamiento interno salvo que exista una necesidad de compartirlos; utiliza HTTPS, minimiza permisos y no exportes componentes innecesariamente. Los Intents que inician servicios internos deben ser explícitos y las superficies expuestas deben validar entradas y aplicar permisos adecuados. Revisa la lista oficial de seguridad y las prácticas de seguridad.
¿Qué diferencia hay entre pruebas locales e instrumentadas?
Las pruebas locales corren en la JVM y son rápidas para lógica pura o clases con dependencias sustituidas; las instrumentadas corren en dispositivo o emulador y validan APIs del framework o integración real. Mantén la mayor parte de lógica fuera de Activity y componentes Android para poder probarla sin arrancar un dispositivo. Ver qué probar en Android y pruebas instrumentadas.
¿Cómo probar una pantalla Compose y sus estados accesibles?
Las pruebas de Compose encuentran nodos por texto, etiquetas o propiedades de semantics, realizan acciones y comprueban el estado mostrado. Prueba estados de carga, vacío, éxito y error, además de interacciones críticas; la misma semántica que hace la UI comprobable también ayuda a herramientas de accesibilidad. Consulta pruebas de Compose y semantics para pruebas.
¿Cómo investigar un problema de rendimiento Android?
Formula una métrica y un recorrido reproducible, mide inicio, scroll y memoria en una compilación parecida a producción, y usa trazas para encontrar el trabajo que excede el presupuesto de cuadros. Macrobenchmark mide recorridos grandes; Baseline Profiles pueden acelerar rutas críticas desde el primer uso y R8 optimiza la compilación de release. Comprueba antes y después en dispositivos representativos; los requisitos de herramientas dependen de las versiones de AGP y Jetpack. Ver medición, Macrobenchmark y Baseline Profiles.
¿Qué herramientas ayudan a depurar un fallo?
Logcat muestra mensajes de la app y del sistema, excepciones y stack traces; Android Studio Debugger permite detenerse en breakpoints e inspeccionar variables. Para UI Compose, inspecciona el árbol de composición y semantics; registra errores con contexto suficiente, evitando imprimir tokens o datos sensibles. Consulta Logcat y la guía de depuración en Android Studio.
¿Qué representan build type, product flavor y build variant?
Un build type define ajustes como debug o release; un product flavor define una variante de producto o entorno; Gradle combina ambos para producir un build variant con código, recursos y configuración propios. Úsalos para separar staging y producción, identificadores y endpoints sin editar manualmente el artefacto después de compilar. Consulta build variants; la sintaxis puede variar con versiones de Android Gradle Plugin.
¿Qué comprobar antes de distribuir un release?
Prueba el artefacto optimizado en dispositivos y API levels soportados, configura la firma sin incluir claves privadas en el repositorio y revisa permisos, manifest, tamaño y restauración de estado. Para Google Play suele publicarse un Android App Bundle, y Play App Signing separa la clave de carga de la clave que firma APKs para usuarios; verifica el flujo actual del canal de distribución. Ver firma de apps y subida de paquetes.
En una entrevista, ¿cómo elegirías Compose o Views en una app existente?
Considera experiencia del equipo, estabilidad del código existente, integración con bibliotecas y coste de migración; Compose es el toolkit moderno recomendado para UI, pero una migración puede ser gradual e interoperar con Views. Decide por pantalla o módulo con criterios medibles, cobertura de pruebas y accesibilidad, en vez de reescribir toda la app solo por adoptar una tecnología. Consulta arquitectura UI Android e interoperabilidad Compose.