Android services let your app keep running long‑lasting tasks without freezing the UI. In this post we’ll break down what a service is, explore its three main variants, and walk through concrete code examples.
What Is an Android Service?
A service is a component that performs work on a background thread. By off‑loading heavy operations—such as data sync or media uploads—to a service, your app’s main UI thread remains free to respond to user input. The core purpose of a service is simply to keep performance optimal while tasks run separately from the screen.
Types of Services
| Service | Core Idea |
|---|---|
| Background Service | Executes silently; no user notification. |
| Foreground Service | Runs in the background and shows a persistent notification so the user is aware that work is ongoing. |
| Bound Service | Provides an interface for other components (activities, services) to talk to it—essentially IPC (inter‑process communication). |
1️⃣ Background Service – Step by Step
1.1 Create the Service Class
class MyService : Service() {
override fun onCreate() { /* called when service is first created */ }
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
/* starts background work */
}
override fun onDestroy() { /* called when system stops the service */ }
override fun onBind(intent: Intent?): IBinder? = null // not needed for a pure background service
}
Why onBind returns null?
Because this service isn’t meant to be bound; it just runs in the background.
1.2 Add a Periodic Task with Kotlin Coroutines
private val serviceScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
serviceScope.launch {
while (isActive) { // coroutine keeps running until cancelled
delay(3000) // wait 3 seconds
Log.d("MyService", "service status is running smoothly")
}
}
return START_STICKY // restarts if killed by the OS
}
Key points
- The coroutine runs on
Dispatchers.IO, an appropriate background thread. - A 3‑second delay simulates a long‑running task (e.g., image upload).
1.3 Clean Up in onDestroy
override fun onDestroy() {
serviceScope.cancel() // stops the coroutine to avoid leaks
super.onDestroy()
}
If you omit this cancellation, the coroutine would keep running even after the service is stopped, potentially causing instability.
1.4 Declare the Service in AndroidManifest.xml
<service
android:name=".MyService"
android:enabled="true"
android:exported="false" />
Why these attributes?
android:enabled="true"ensures the service is available at runtime.android:exported="false"keeps it private to your app.
1.5 UI to Start/Stop (Jetpack Compose)
Button(onClick = { startService(Intent(this, MyService::class.java)) }) {
Text("Start Service")
}
Button(onClick = { stopService(Intent(this, MyService::class.java)) }) {
Text("Stop Service")
}
Only two lines are needed—just like starting an activity.
1.6 Verify in Action
- Launch the app → tap Start Service.
- A toast confirms launch and Logcat shows “service status” every 3 s.
- Tap Stop Service;
onDestroyappears in Logcat, confirming cleanup.
2️⃣ Foreground Service – Making Background Work Visible
2.1 Create the Service Class with a Notification
class MyForegroundService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val notification = buildNotification()
startForeground(1, notification) // shows persistent notification
serviceScope.launch { // same coroutine task as background service
while (isActive) {
delay(3000)
Log.d("MyForegroundService", "service status is running smoothly")
}
}
return START_STICKY
}
private fun buildNotification(): Notification {
val channelName = "Sync Channel"
val channelId = "sync_channel_id"
return Notification.Builder(this, channelId)
.setContentTitle("Foreground Service")
.setContentText("Service is running smoothly")
.setSmallIcon(R.drawable.ic_sync) // replace with your icon
.build()
}
}
- The notification tells the user that a background task is active.
startForegroundmust be called before any heavy work begins.
2.2 Manifest Permissions & Service Declaration
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<service
android:name=".MyForegroundService"
android:foregroundServiceType="dataSync" />
Why these permissions?
FOREGROUND_SERVICEallows the system to run the service in the foreground.POST_NOTIFICATIONSis required because we’re displaying a notification.
2.3 UI Buttons to Control the Foreground Service
Button(onClick = {
val intent = Intent(this, MyForegroundService::class.java).apply {
action = MyForegroundService.Action.START.name
}
startService(intent)
}) { Text("Start Foreground Service") }
Button(onClick = {
val intent = Intent(this, MyForegroundService::class.java).apply {
action = MyForegroundService.Action.STOP.name
}
startService(intent)
}) { Text("Stop Foreground Service") }
2.4 Running the Service
- The first launch triggers a notification‑permission dialog—accept it once.
- Tap Start → a persistent notification appears and Logcat logs status every 3 s.
- Tap Stop → notification disappears,
onDestroyfires in Logcat.
3️⃣ Bound Service – IPC Between Components
3.1 Service Implementation with LocalBinder
class MyBoundService : Service() {
private val binder = LocalBinder()
private val randomGenerator = Random()
inner class LocalBinder : Binder() {
fun getService(): MyBoundService = this@MyBoundService
}
override fun onBind(intent: Intent?): IBinder? = binder
// Public method for clients to call
fun generateRandomNumber(max: Int = 100): Int =
randomGenerator.nextInt(max + 1)
}
LocalBinderacts as a glue that lets activities or other services obtain the current instance ofMyBoundService.- The service exposes
generateRandomNumber()so any bound component can request data directly.
3.2 Declare in Manifest
<service android:name=".MyBoundService"
android:enabled="true"
android:exported="false" />
No extra permissions are required for a simple bound service.
3.3 Bind the Service from an Activity
class MainActivity : ComponentActivity() {
private var boundService: MyBoundService? = null
private var isBound = false
private val connection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
boundService = (binder as MyBoundService.LocalBinder).getService()
isBound = true
}
override fun onServiceDisconnected(name: ComponentName?) {
boundService = null
isBound = false
}
}
override fun onStart() {
super.onStart()
Intent(this, MyBoundService::class.java).also { intent ->
bindService(intent, connection, Context.BIND_AUTO_CREATE)
}
}
override fun onStop() {
super.onStop()
if (isBound) unbindService(connection)
}
}
3.4 UI to Fetch Data from the Bound Service
var randomNumber by remember { mutableStateOf(0) }
Column {
Text(text = if (isBound) "Service is bound" else "Service not bound")
Text(text = "Random number: $randomNumber")
Button(onClick = {
boundService?.let { service ->
randomNumber = service.generateRandomNumber()
}
}) {
Text("Get Number from Service")
}
}
What happens behind the scenes?
- The activity binds to
MyBoundServiceon start. - When the user taps Get Number from Service, the activity calls
generateRandomNumber()through the binder, receives a new random integer, and displays it.
Recap of what we learned
| Concept | Why It Matters |
|---|---|
| Background Service | Silent background work; ideal for tasks that don’t need to inform the user. |
| Foreground Service | Persistent notification keeps the system and user aware of ongoing work—necessary for long‑running processes like sync or location tracking. |
| Bound Service | Enables tight coupling between activities/services, allowing them to call each other’s methods directly via a binder—great for shared data or complex IPC. |
With these code patterns you can now:
- Spin up silent background jobs that restart automatically.
- Run foreground services that respect user notifications and system policies.
- Create bound services for component‑to‑component communication.
Happy coding, and may your apps run smoothly in the background!
📌 Full Course Playlist https://www.youtube.com/playlist?list=PLO1OrQEU0vHNmD9Xqzs-qXwzzwrDvdhVu
~ ~ THANK YOU FOR READING ~ ~