Services & Foreground Service
A Service is an Android component built to run in the background without any user interface of its own. It's a different tool from everything covered so far: WorkManager schedules and guarantees deferred work, coroutines run structured async code tied to a scope's lifetime, and a Service just keeps running, independent of any screen, for as long as it's needed.
Three Flavors of Service
"Service" actually covers three fairly different things, and picking the wrong one is a common source of confusion:
| Kind | What it's for | Current status |
|---|---|---|
| Plain (background) Service | Unbounded background work with no user-visible indication | Heavily restricted on modern Android — the OS aggressively limits or kills these; WorkManager replaces most of what this used to be used for |
| Foreground Service | Ongoing work the user should be actively aware of, shown via a persistent notification — like tracking a hike's GPS route live | The right choice for genuinely ongoing, user-visible background work |
| Bound Service | Work another component (an Activity, another app) needs to interact with directly through a client-server style interface | Used when you need two-way method calls into the running Service, not just a fire-and-forget task |
For TrailLog, the one that matters most is the Foreground Service — it's the only one of the three genuinely suited to something like live GPS tracking while the user has switched to another app.
TrailLog's HikeTrackingService
Picture the user tapping "Start Hike." From that moment, TrailLog needs to keep recording GPS points even if the user locks their phone or switches to a messaging app mid-hike. That's a textbook Foreground Service: ongoing, user-initiated, and something the user should always be able to see is happening.
HikeTrackingService outline
class HikeTrackingService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val notification = buildRecordingNotification()
startForeground(NOTIFICATION_ID, notification)
beginRecordingGpsPoints()
return START_STICKY
}
override fun onBind(intent: Intent): IBinder? = null // not a bound service
override fun onDestroy() {
stopRecordingGpsPoints()
super.onDestroy()
}
}
startForeground(NOTIFICATION_ID, notification) is what promotes this Service to
foreground status, and it must be called within seconds of the Service starting — Android
requires it. That persistent "Recording hike…" notification isn't an inconvenience to route
around; it's a genuine transparency requirement, so the user always has a visible, tappable
reminder that GPS tracking is actively running in the background. Building the notification
content itself follows the same channel-and-builder pattern covered in
Notifications & Channels.
Starting the Service
From the Activity or Fragment where the user taps "Start Hike," you start the Service with
ContextCompat.startForegroundService() rather than the plain startService()
call — this variant is what correctly signals to the system that the Service intends to call
startForeground() right away:
fun startHikeTracking(context: Context) {
val intent = Intent(context, HikeTrackingService::class.java)
ContextCompat.startForegroundService(context, intent)
}
Stopping It Cleanly
When the user taps "End Hike," the Service needs to stop recording and remove itself — and its notification — right away:
fun stopHikeTracking(context: Context) {
val intent = Intent(context, HikeTrackingService::class.java)
context.stopService(intent)
}
Inside the Service, onDestroy() is the right place to release any GPS callbacks or
other resources — leaving them running after the Service is gone is a reliable way to leak battery
and memory.
The FOREGROUND_SERVICE_LOCATION Permission
Recent Android versions require an additional manifest permission specifically for foreground services that track location, on top of the regular location permission itself:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
This exists on top of everything else this lesson covered because the OS treats "running continuously in the foreground" and "accessing the user's location" as two separate, both sensitive, capabilities that each need explicit declaration. The next lesson, Runtime Permissions, covers how TrailLog actually asks the user for location access at runtime — declaring it in the manifest is only step one.