Android apps are built from four component types the system knows how to launch: Activities, Services, Broadcast Receivers, and Content Providers. Learn what each is for and when to reach for it.
Every Android app is assembled from four kinds of component, each an entry point the system can start independently. Activities are screens; Services do long-running work without UI; Broadcast Receivers respond to system-wide events; Content Providers share data between apps. Knowing which component fits a need — and that each must be declared in the manifest — is foundational Android design.
Component What it is / when to use
Activity one screen / UI entry point (lessons 2-3)
Service long-running work with NO UI (music, sync, upload)
Broadcast Receiver reacts to system/app-wide events (boot, connectivity)
Content Provider shares structured data ACROSS apps (contacts, media)
All four are declared in the manifest — the system launches them by that
declaration. Most modern apps are mostly Activities + a bit of the rest,
with background work handled by WorkManager (next lesson) rather than raw Services.A Service performs operations that should continue independent of any screen — playing audio, syncing data. Because unrestricted background execution drains battery, modern Android tightly limits Services; a foreground service (with a persistent notification) is required for ongoing user-visible work like music, while deferrable background work should use WorkManager instead. The lesson is to prefer the modern tools and use raw Services only when truly needed.
class MusicService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// foreground service: ongoing, user-aware work (needs a notification)
startForeground(1, buildNotification())
playMusic()
return START_STICKY
}
override fun onBind(intent: Intent?): IBinder? = null
}
// modern Android RESTRICTS background services (battery). Use:
// foreground service -> ongoing user-visible work (music, navigation)
// WorkManager -> deferrable/guaranteed background work (next lesson)A Broadcast Receiver listens for broadcast messages — system events like connectivity changes or device boot, or custom app events. It runs briefly to react, not to do long work. Modern Android restricts what can be declared in the manifest (to save battery), so most receivers are registered at runtime while your app is active, for events you care about only then.
class ConnectivityReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// runs briefly to REACT — kick off real work elsewhere, don't block here
val connected = /* check network state */ true
println("connectivity changed: connected=$connected")
}
}
// register at runtime while the app is active (preferred over manifest-declared)
val receiver = ConnectivityReceiver()
registerReceiver(receiver, IntentFilter("android.net.conn.CONNECTIVITY_CHANGE"))
// ... unregisterReceiver(receiver) when done, tied to the lifecycle.A Content Provider exposes structured data through a uniform interface so other apps can query it — this is how you read the device’s contacts, calendar, or media store. Within a single app you rarely need one (you use Room directly), but it is the standard mechanism for cross-app data sharing and the API you go through to access shared system data.
// query the system Contacts provider via a content:// URI
val cursor = contentResolver.query(
ContactsContract.Contacts.CONTENT_URI,
arrayOf(ContactsContract.Contacts.DISPLAY_NAME),
null, null, null
)
cursor?.use {
while (it.moveToNext()) {
println(it.getString(0)) // each contact's display name
}
}
// you mostly CONSUME providers (contacts, media). You'd EXPOSE one only to
// share your app's data with OTHER apps. Inside your app, use Room directly.