Saltar al contenido principal

Componentes de una aplicación Android y el Manifest

En el tema 1 dejamos pendiente una pregunta: ¿de qué está hecha realmente una aplicación Android por dentro? No es un programa con un único punto de entrada como los que se escriben en el módulo de Programación. Una app Android es un conjunto de piezas independientes que el sistema operativo puede crear, destruir y combinar según lo necesite, no un main() que se ejecuta de arriba a abajo.

Entender esto desde el principio evita mucha confusión más adelante: en Android no eres tú quien decide cuándo se ejecuta tu código. Es el sistema quien llama a tus componentes cuando hace falta.


Los cuatro componentes​

Android define cuatro tipos de componentes. Cualquier aplicación está construida combinando algunos (no necesariamente todos) de estos cuatro bloques.

Activity​

Representa una pantalla con la que el usuario interactúa. Es, con diferencia, el componente que más se usa y con el que trabajaremos primero.

  • Una aplicación puede tener varias Activities, pero el enfoque que se sigue en este curso —y el habitual hoy en día— usa una única Activity por aplicación: todas las pantallas (login, listado, detalle, ajustes...) conviven dentro de ella como destinos de navegación. Se retoma más abajo, al hablar de Intents.
  • Tiene un ciclo de vida propio, gestionado por el sistema: se crea, se pausa, se reanuda, se destruye. Lo veremos en detalle en la siguiente página.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}

Service​

Ejecuta trabajo en segundo plano, sin interfaz de usuario propia. Sirve para operaciones que deben continuar aunque el usuario cambie de pantalla o incluso salga de la aplicación: reproducir música, sincronizar datos, descargar un fichero grande.

  • Un Service no crea automáticamente un hilo nuevo: por defecto se ejecuta en el hilo principal, igual que una Activity. Si hace trabajo pesado, hay que moverlo explícitamente a otro hilo.
  • Profundizaremos en tareas en segundo plano y servicios en una unidad posterior; de momento basta con saber que existen y para qué sirven.

Broadcast Receiver​

Escucha anuncios del sistema o de otras aplicaciones y reacciona a ellos: batería baja, cambio de conectividad, llegada de un SMS, que el dispositivo termine de arrancar.

class BateriaBajaReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// Reaccionar al evento del sistema
}
}

No tiene interfaz y normalmente hace un trabajo muy breve: recibe el aviso y delega (por ejemplo, arrancando un Service o mostrando una notificación).

Content Provider​

Gestiona el acceso a datos compartidos entre aplicaciones, a través de una interfaz uniforme parecida a una base de datos. Es el mecanismo que usa, por ejemplo, la aplicación de Contactos para dejar que otras apps consulten (con permiso) los contactos del usuario.

Es el componente menos usado de los cuatro en aplicaciones típicas; se retoma cuando se trabaja con almacenamiento compartido entre aplicaciones.

nota

Por qué importa distinguirlos Cada componente tiene un ciclo de vida y unas reglas distintas. Confundir un Service con una Activity lleva a errores típicos de principiante, como intentar actualizar una vista desde un componente que no tiene pantalla.


El AndroidManifest.xml: la tarjeta de identidad​

Todo componente que una aplicación quiera usar debe declararse en el AndroidManifest.xml, en la raíz de src/main/. Si un componente no está en el Manifest, el sistema no sabe que existe y la aplicación falla al intentar usarlo.

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<uses-permission android:name="android.permission.INTERNET" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.MiApp">

<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<service android:name=".SincronizacionService" />

<receiver android:name=".BateriaBajaReceiver" />

</application>

</manifest>

El Manifest responde a varias preguntas que el sistema necesita antes de instalar o ejecutar la aplicación:

PreguntaDónde se responde
¿Qué componentes tiene la app?<activity>, <service>, <receiver>, <provider>
¿Por dónde se arranca?El <intent-filter> con MAIN y LAUNCHER en la actividad de entrada
¿Qué permisos necesita?<uses-permission>
¿Qué versiones de Android soporta?minSdk, targetSdk (normalmente en build.gradle.kts, referenciados aquí)
¿Qué icono y nombre muestra?android:icon, android:label

El intent-filter de arranque​

<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

Esta combinación exacta es la que le dice al sistema: "esta es la Activity que se abre cuando el usuario toca el icono de la app". Sin ella, la aplicación se instala pero no aparece en el launcher del dispositivo.

android:exported​

Indica si el componente puede ser invocado por otras aplicaciones. Desde Android 12, declararlo explícitamente es obligatorio para cualquier componente con un intent-filter. Por defecto conviene dejarlo en false salvo que exista una razón concreta para exponer el componente.


Intents: cómo se comunican los componentes​

Si los componentes son piezas independientes, hace falta un mecanismo para pasar de uno a otro o para pedirle algo al sistema. Ese mecanismo es el Intent: un objeto que describe una intención de acción.

Intent explícito​

Indica exactamente qué componente se quiere lanzar: la clase concreta, sin ambigüedad. Es la forma de arrancar, por ejemplo, un Service propio o una Activity de otra aplicación instalada de la que se conoce el nombre.

val intent = Intent(this, MiServicioDeSincronizacion::class.java)
startService(intent)

Intent implícito​

Describe una acción a realizar, sin decir qué componente debe hacerla. Es el sistema quien busca, entre todas las aplicaciones instaladas, cuál puede resolverla.

val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://www.google.com"))
startActivity(intent)

Con un intent implícito como este, el sistema puede ofrecer varios navegadores instalados y dejar que el usuario elija, o abrir directamente el único disponible.

consejo

Extras Los datos que viajan con un Intent (putExtra) se llaman extras. Solo pueden ser tipos serializables: primitivos, String, Parcelable... No se puede pasar directamente, por ejemplo, un objeto arbitrario sin prepararlo antes.

nota

¿Y para moverse entre pantallas de mi propia app? Es tentador pensar que la navegación entre pantallas se resuelve lanzando un Intent explícito hacia otra Activity, y durante años fue la forma habitual de hacerlo. En este curso no se trabaja así: cada aplicación tendrá una única Activity y las pantallas serán destinos de navegación dentro de ella, gestionados por el Navigation Component, que se presenta a continuación.


Una Activity, muchas pantallas: el Navigation Component​

El enfoque moderno recomendado por Google —y el que se sigue en este curso— reduce cada aplicación a una sola Activity. Lo que antes eran varias Activities (login, listado, detalle...) pasa a ser varios destinos de navegación (Fragment o pantallas Compose) dentro de esa única Activity.

Esto lo gestiona el Navigation Component, una librería de Jetpack pensada exactamente para este problema:

  • Un grafo de navegación describe, de forma declarativa, qué destinos existen y cómo se conectan entre ellos.
  • Un NavController es el objeto que ejecuta ese grafo: pedirle que navegue a un destino sustituye a lanzar un Intent explícito y llamar a startActivity.
  • Los argumentos entre destinos (el equivalente a los extras de un Intent) viajan de forma tipada, sin depender de claves de texto propensas a errores.
// Navegar al destino "detalle", pasándole un argumento
navController.navigate(R.id.detalleFragment, bundleOf("idElemento" to 42))
consejo

Esto es solo una primera toma de contacto El Navigation Component se trabajará en profundidad en la unidad 3, con su propio grafo visual, NavHost y paso de argumentos tipado. De momento basta con entender la idea de fondo: una Activity, varios destinos, y que el Intent explícito entre Activities propias deja de ser la herramienta de navegación habitual.