¿Quieres dominar el desarrollo de apps Android y no sabes cómo organizar tu código, gestionar pantallas o comunicar componentes? Comprender en profundidad los conceptos de actividades, intents y fragments es esencial. Estos pilares del sistema operativo de Google permiten crear aplicaciones robustas, escalables y con una excelente experiencia de usuario.
A continuación, encontrarás la guía definitiva y actualizada sobre qué son las actividades, los intents y los fragments en Android. Explorarás sus ciclos de vida, su comunicación, los errores frecuentes, sus diferencias, y descubrirás ejemplos prácticos y buenas prácticas extraídas de documentación oficial, desarrolladores experimentados y las mejores fuentes educativas del ecosistema.
¿Qué es una Actividad (Activity) en Android?
Una Activity es el componente base sobre el que se estructura la interfaz de usuario en Android. Cada pantalla con la que el usuario puede interactuar está representada por una actividad. La actividad es la unidad central de interacción, responsable de gestionar los eventos, mostrar información y orquestar los diferentes componentes que forman la aplicación.
- Representación de pantalla: Cada vez que abres una app y ves una pantalla (login, perfil, ajustes), en realidad estás navegando entre distintas actividades.
- Contenedor de fragments: Las actividades pueden gestionar y operar múltiples fragments, que permiten interfaces más dinámicas y modulares.
- Responsabilidad de interacción: Gestionan la lógica de respuesta ante eventos de usuario, como toques, botones de hardware, o cambios de orientación.
Las actividades son la puerta de entrada principal de la mayoría de las aplicaciones. Por eso, es imprescindible entender cómo se declaran, se gestionan y cómo se comunican entre ellas y con el resto del sistema.
En el archivo AndroidManifest.xml
de tu proyecto, todas las actividades deben declararse explícitamente:
<activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
El filtro de intent anterior indica que la actividad es el punto de inicio de la app. Es decir, cuando el usuario pulsa el icono, esta pantalla será la primera en abrirse.
Características clave de las actividades:
- Independencia: Cada actividad tiene su propio ciclo de vida y puede existir de forma independiente.
- Pilas de actividades: Android gestiona una «pila» de actividades. Cuando abres una nueva, la anterior queda en pausa (y viceversa).
- Comunicación: Las actividades pueden iniciar otras actividades (de la misma app o de otras apps) mediante intents.
Ejemplo básico de inicialización
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Inicialización de vistas, recepción de datos del Intent, arranque de fragments, etc. }
El método onCreate()
es el punto de entrada de cada actividad. Aquí se «infla» la interfaz (layout XML), se inicializan variables, receptores, vistas y se puede empezar la lógica de negocio.
Ciclo de Vida de una Actividad en Android
El ciclo de vida de una actividad es la secuencia de estados por los que pasa una pantalla a medida que el usuario interactúa con ella o con otras aplicaciones. Comprender este ciclo es fundamental para:
- Evitar errores como pérdidas de datos o cierres inesperados.
- Optimizar recursos (memoria, batería).
- Brindar una experiencia de usuario fluida.
Los principales métodos que el sistema Android llama según la transición de estados son:
- onCreate(): Inicialización, inflado del layout, recuperación de estado previo.
- onStart(): La actividad se vuelve visible, pero aún no se puede interactuar.
- onResume(): La actividad está en primer plano y el usuario puede interactuar.
- onPause(): Otra actividad toma el foco, pero la actual sigue visible (por ejemplo, diálogo flotante).
- onStop(): La actividad deja de ser visible (el usuario cambia de tarea).
- onRestart(): Cuando la actividad vuelve al foco tras un
onStop()
. - onDestroy(): La actividad está por ser eliminada de la memoria.
¿Por qué debes dominar esto? Porque cada método es una oportunidad para:
- Guardar estado del usuario (por ejemplo, texto en formularios o posición de listas).
- Pausing animaciones o audio (en
onPause()
yonResume()
). - Detener servicios y tareas que no necesitas en segundo plano.
- Prevenir pérdidas de datos en cambios de configuración (rotación de pantalla, cambio de idioma, etc.).
La gestión del ciclo de vida es especialmente importante en experiencias como juegos, reproducción multimedia, apps bancarias o cualquier aplicación con lógica compleja.
¿Qué es un Intent en Android?
Un Intent es un objeto de mensajería usado por Android para comunicar componentes entre sí (activities, services, broadcast receivers, etc.). Es la vía para solicitar una acción, llevar datos o iniciar procesos dentro de la app o con otras aplicaciones y servicios del sistema.
¿Cómo funcionan los Intents?
- Iniciar una actividad: Navegar entre pantallas dentro de una app o abrir una pantalla de otra aplicación.
- Lanzar servicios: Ejecutar tareas en segundo plano, como descargas o sincronización.
- Emitir transmisiones (broadcasts): Enviar avisos a todo el sistema o apps registradas (por ejemplo, cambio de red, carga de batería).
- Comunicar datos (Extras): Pasar parámetros clave-valor o paquetes de datos entre componentes.
Tipos de Intents:
- Intents explícitos: Conoces la clase exacta del componente destino. Se usan para navegar entre tus propios componentes.
- Intents implícitos: Solicitas una acción general y dejas que Android decida qué componente (de tu app u otra) la gestione. Ejemplo: abrir una URL en el navegador, compartir imagen, elegir un contacto, etc.
El uso de Intents implícitos es una de las grandes ventajas de Android: tu app puede integrarse con cientos de apps sin conocerlas de antemano, siguiendo estándares comunes (ver, editar, compartir, etc.).
Sintaxis básica
// Intent explícito val intent = Intent(this, SegundaActividad::class.java) intent.putExtra("nombre", "Juan") startActivity(intent) // Intent implícito val intent = Intent(Intent.ACTION_VIEW) intent.data = Uri.parse("https://www.google.com") startActivity(intent)
Partes principales de un Intent:
- Action: Indica la acción (ver, enviar, editar, compartir…)
- Data: Un URI con la información a procesar (enlace, contacto…)
- Component: El destino específico para intents explícitos
- Category: Categoría adicional, como si es la actividad del launcher
- Extras: Pares clave-valor de datos adicionales
- Flags: Marcan cómo debe comportarse la actividad destino respecto a la pila y el historial
La documentación oficial de Android recomienda el uso de intents explícitos para iniciar servicios, por motivos de seguridad, y usar intents implícitos para solicitudes generales entre apps.
Usos frecuentes de Intents
- Navegar entre pantallas de la app
- Abrir el navegador, mapas, correo, cámara, etc.
- Compartir contenido nativo con otras apps
- Escuchar eventos del sistema mediante BroadcastReceiver
- Ejecutar tareas diferidas con PendingIntent (notificaciones, alarmas, widgets)
¿Qué es un Fragment en Android?
Un Fragment es una sección modular y reutilizable de la interfaz de usuario, junto con su propia lógica, que vive dentro de una Activity. Los fragments nacen para fortalecer la reutilización de código y construir interfaces dinámicas y adaptables, especialmente valiosas en dispositivos con diferentes tamaños de pantalla (móviles, tablets, plegables).
- Fragmentos = piezas de LEGO UI: Pueden combinarse, intercambiarse y reorganizarse dinámicamente en una actividad.
- Pueden tener su ciclo de vida propio, gestionado por la actividad contenedora.
- Reutilizables: El mismo fragment puede usarse en distintos contextos de la app.
- Flexibilidad total: Cambia fragments al vuelo según la navegación, orientación o tipo de dispositivo.
Ventajas principales de los fragments
- Modularidad: Divide pantallas complejas en partes más simples y gestionables.
- Reutilización: Un mismo fragmento se puede insertar en diferentes actividades o combinaciones.
- Adaptabilidad: Permiten mostrar varios fragments a la vez en tablets, pero sólo uno en móviles.
- Transacciones y pila: Se pueden agregar, reemplazar o eliminar fragments de forma dinámica, con animaciones y gestión de histórico.
- Compatibilidad: Funcionan bien con navegadores de pestañas, ViewPager, BottomNavigationView, etc.
Ejemplo básico de fragmento
class MiFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { return inflater.inflate(R.layout.mi_fragment_layout, container, false) } }
El fragmento se «infla» y se retorna como una vista. Luego, se añade a la actividad mediante el FragmentManager
:
supportFragmentManager.beginTransaction() .replace(R.id.contenedor, MiFragment()) .commit()
Ciclo de vida de los fragments
- onAttach(): El fragment se asocia con la actividad
- onCreate(): Inicializa estado no relacionado con la interfaz de usuario
- onCreateView(): Infla la interfaz y la retorna
- onViewCreated(): Inicializa elementos de la vista
- onResume()/onPause()/onDestroy(): Fácil de entender si sabes el ciclo de vida de Activity
Este ciclo permite manejar la inicialización y liberación de recursos de forma granular, optimizando el rendimiento y la experiencia del usuario.
Cómo declarar y usar fragments
- En XML: Puedes incluirlos directamente usando la etiqueta
<fragment>
dentro del layout de la actividad. - Por código: Usando
FragmentManager
yFragmentTransaction
para añadir, reemplazar o quitar fragments dinámicamente.
Ejemplo en XML:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment android:name="com.ejemplo.MiFragment" android:id="@+id/fragment_uno" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
Transacciones de fragments
- add(): Añade un fragment al contenedor.
- replace(): Sustituye el fragment actual por otro en el mismo contenedor.
- remove(): Quita un fragment del contenedor.
- addToBackStack(): Permite que el usuario navegue hacia atrás en el historial de fragments.
Comunicación entre fragments y actividades
- Un fragment puede comunicarse con su actividad contenedora a través de interfaces, usando métodos como
getActivity()
para acceder a la instancia de la actividad. - Una actividad puede acceder a los fragments mediante
findFragmentById()
ofindFragmentByTag()
. - Frente a la necesidad de comunicar fragments entre sí: lo ideal es delegar la comunicación a la actividad, que actúa como mediadora.
Intents y Filtros de Intents en Profundidad
Para entender la arquitectura y seguridad en Android, es clave comprender cómo los intents se relacionan con filtros de intents declarados en el AndroidManifest.xml
.
¿Qué es un filtro de intent?
- Especifica qué tipos de intents puede recibir un componente.
- Permite que otras aplicaciones o el sistema invoquen tu componente, siempre que el intent coincida con el filtro.
Ejemplo de filtro de intent para una actividad que recibe textos compartidos:
<activity android:name="ShareActivity" android:exported="false"> <intent-filter> <action android:name="android.intent.action.SEND"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/plain"/> </intent-filter> </activity>
Estructura de los filtros de intent
- <action>: La acción del intent (por ejemplo,
ACTION_SEND
para compartir,ACTION_VIEW
para visualizar recursos). - <category>: Agrupa componentes que pueden recibir el intent.
CATEGORY_DEFAULT
es la más común. - <data>: Define los tipos MIME, URIs, esquemas que acepta el componente (por ejemplo, texto, imágenes, archivos).
Estos filtros permiten que tu app sea invocada por otras apps, pero también limitan qué acciones le están permitidas.
Resolución de intents: ¿cómo decide Android qué abrir?
- Cuando se envía un intent implícito, Android busca componentes registrados cuyo filtro coincida con la acción, categoría y tipo de datos del intent.
- Si varios filtros coinciden, muestra un selector para permitir al usuario elegir.
Buenas prácticas en seguridad y diseño
- Usa filtros restrictivos: No declares filtros de intent demasiado generales, para no exponer tu app a llamadas no deseadas desde otras apps.
- Controla el atributo
android:exported
: Especifica si el componente es accesible desde fuera de tu app. Si no vas a permitirlo, ponlo afalse
. - Usa intents explícitos para iniciar tus propios servicios y actividades internas.
Nota importante: Desde Android 12, es obligatorio establecer explícitamente android:exported
en todos los componentes que tengan filtros de intent, de lo contrario, la aplicación no se podrá instalar.
PendingIntent: Intents Diferidos y Seguridad
Un PendingIntent es un envoltorio para un Intent que permite que otra app o el propio sistema ejecute el intent en representación de tu aplicación, con sus mismos permisos. Es esencial para notificaciones, alarmas, widgets, acciones rápidas, etc.
- Ejemplo: Cuando el usuario pulsa una notificación, la acción real (abrir actividad, ejecutar servicio) está encapsulada en un PendingIntent.
- Mutabilidad e inmutabilidad: Desde versiones recientes, debes especificar si el PendingIntent es mutable (puede modificarse) o inmutable (no puede modificarse).
Cuándo usar PendingIntent
- Para que el sistema ejecute tu código aunque tu app no esté en primer plano.
- Cuando quieras delegar en un componente externo (ejemplo: widgets de escritorio, alarmas, respuestas rápidas en notificaciones).
Buenas prácticas con PendingIntent
- Preferiblemente usa
FLAG_IMMUTABLE
si el intent no necesita ser modificado por otras apps. - Si necesitas que el intent sea modificable (respuestas a notificaciones, burbujas de chat), usa
FLAG_MUTABLE
y asegúrate de que el intent de base tenga toda la información relevante segura. - Usa intents explícitos dentro de los PendingIntent para evitar que otras apps puedan manipular el destino de la acción.
Transacciones y Gestión Dinámica de Fragments
Una de las ventajas más potentes de los fragments es su capacidad de ser gestionados dinámicamente durante la ejecución de la app. Veamos cómo hacerlo de forma profesional.
Gestión mediante FragmentManager y FragmentTransaction
- FragmentManager: Es la clase que gestiona las operaciones con fragments en una actividad (agregar, quitar, reemplazar).
- FragmentTransaction: Una «transacción» es un conjunto de cambios que puedes aplicar de una vez (ejemplo: reemplazar el fragment actual, añadir otro a la pila, combinar animaciones).
Ejemplo:
val transaction = supportFragmentManager.beginTransaction() transaction.replace(R.id.fragment_container, NuevoFragment()) transaction.addToBackStack(null) // Permite regresar al fragment anterior al pulsar "Atrás" transaction.commit()
Pila de retroceso (Back Stack)
- Permite que el usuario navegue hacia atrás entre fragments como si cambiara de pantalla.
- Cada
addToBackStack()
agrega la transacción a la pila. Si no la usas, el fragment anterior se destruye y no se puede regresar a él con el botón «Atrás».
Tipos de fragmentos proporcionados por Android
- DialogFragment: Muestra un cuadro de diálogo flotante sobre la actividad.
- ListFragment: Facilita listas de elementos dentro de un fragment.
- PreferenceFragment: Gestiona listas de preferencias.
Comunicación entre Fragments y Activities: Patrones y Buenas Prácticas
Uno de los retos a la hora de trabajar con fragments es la comunicación eficiente entre ellos, y entre los fragments y la actividad principal.
Comunicación fragment → activity
- Un fragment puede llamar métodos públicos de la actividad contenedora usando
getActivity()
. - Puede lanzar eventos y notificar a la actividad de cambios.
Comunicación activity → fragment
- La actividad puede buscar fragments creados usando
findFragmentById()
ofindFragmentByTag()
y llamar sus métodos públicos.
Comunicación fragment ↔ fragment (vía activity)
- Los fragments nunca deben comunicarse directamente. En su lugar, usan la actividad como mediadora (usando interfaces o eventos).
- Esto promueve una arquitectura desacoplada y favorece el mantenimiento o ampliación de la app.
Ejemplo de interfaz de comunicación
// En fragmento origen interface OnArticuloSelectedListener { fun onArticuloSelected(texto: String) } // En la actividad que implementa la interfaz class MainActivity : AppCompatActivity(), OnArticuloSelectedListener { override fun onArticuloSelected(texto: String) { // Lógica para responder al evento del fragmento } }
Casos de Uso Reales de Actividades, Intents y Fragments
- Apps de mensajería: La lista de chats puede ser un fragment; al abrir uno, la conversación es otro fragment en la misma actividad (especialmente en tablets).
- Apps de noticias: Un fragment muestra la lista de titulares, otro la noticia seleccionada.
- Apps bancarias: Niveles de seguridad y comunicación entre actividades separadas (inicio de sesión, operaciones, notificaciones).
- Apps multimedia: Uso intensivo de intents para abrir galerías, compartir, reproducir audio/video en apps externas.
- Widgets y notificaciones: Uso de PendingIntent para acciones rápidas desde fuera de la app principal.
Errores Comunes y Cómo Evitarlos
- No registrar actividades/fragments en el manifiesto: Sin declaración no se pueden abrir.
- Confundir intents explícitos e implícitos: Usar implícitos donde debería ser explícito causa errores de seguridad y funcionalidad.
- Olvidar
addToBackStack()
en transacciones de fragments: El usuario no podrá regresar al fragment anterior. - Pérdida de datos en cambios de configuración: No guardar estados correctamente en
onSaveInstanceState()
. - Comunicación directa y acoplada entre fragments: Hacer que los fragments dependan uno del otro dificulta el mantenimiento y la extensibilidad.
- No gestionar correctamente permisos en intents que requieren acceso a datos sensibles (contactos, almacenamiento, ubicación).
Buenas Prácticas y Recomendaciones Avanzadas
- Siempre valida los datos recibidos por extras en los intents. Puedes recibir intents maliciosos si exportas componentes.
- Utiliza SafeArgs y Navigation Component para navegación más segura y estructurada entre fragments y actividades.
- Apóyate en patrones de arquitectura (MVVM, MVP, Clean Architecture) para desacoplar la lógica de negocio de la UI.
- Divide actividades complejas en múltiples fragments para mejorar la mantenibilidad.
- Controla cuidadosamente el ciclo de vida para evitar fugas de memoria y recursos bloqueados.
Referencias y Recursos Recomendados
- Guía oficial de actividades en Android Developers
- Guía completa sobre Intents y Filtros de Intents
- Documentación de Fragments de Android
- Análisis de actividades vs fragments por desarrolladores expertos
- Tutoriales prácticos de uso de fragments
Dominar los conceptos de actividades, intents y fragments es el primer paso para desarrollar aplicaciones Android eficaces, robustas y adaptadas a las exigencias de dispositivos modernos y a las expectativas del usuario. La correcta separación de responsabilidades, el conocimiento del ciclo de vida, la utilización segura de la comunicación entre componentes y el diseño modular te permitirán implementar apps escalables y de calidad profesional.