Anil Sai
Anil Sai
Android Developer

πŸ“‘ Android Fragment Lifecycle Guide

πŸ“‘ Android Fragment Lifecycle Guide

βš™οΈ Android Service Lifecycle: Deep Dive with Kotlin & Best Practices

Android Service is a powerful component for performing long-running operations in the background β€” like playing music, handling downloads, or syncing data β€” without a user interface.

Understanding the lifecycle of a Service is crucial to building robust and battery-efficient apps. In this post, we’ll cover:

  • Types of Services
  • Lifecycle methods
  • Code examples in Kotlin with explanations
  • Tips for modern development

πŸ“ Types of Android Services

Type Description
Started Service Launched via startService() or ContextCompat.startForegroundService(). Runs until stopSelf() or stopService() is called.
Bound Service Bound to one or more components using bindService(). Stops when all clients unbind.
Foreground Service Similar to started service, but shows a notification to run in foreground (avoids background restrictions).

πŸ” Service Lifecycle Diagram (Started Service)

1
2
3
4
5
6
7
8
9
onCreate()
   ↓
onStartCommand()
   ↓
Service Running
   ↓
stopSelf() / stopService()
   ↓
onDestroy()

πŸ” Service Lifecycle Diagram (Bound Service)

1
2
3
4
5
6
7
onCreate()
   ↓
onBind()
   ↕ (multiple clients)
onUnbind()
   ↓
onDestroy()

πŸ§ͺ Kotlin Code Examples with Explanation

βœ… Basic Started Service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class MyStartedService : Service() {

    override fun onCreate() {
        super.onCreate()
        Log.d("Service", "onCreate")
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.d("Service", "onStartCommand")
        // Do background task here
        return START_STICKY
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d("Service", "onDestroy")
    }

    override fun onBind(intent: Intent?): IBinder? {
        return null // Not a bound service
    }
}

Explanation:

  • onCreate(): Called when the service is created.
  • onStartCommand(): Called each time startService() is called. Your background work starts here.
  • START_STICKY: Tells the system to recreate the service after it has been killed.
  • onDestroy(): Cleanup resources here.
  • onBind() returns null since it’s not a bound service.

To start this service:

1
2
val intent = Intent(this, MyStartedService::class.java)
startService(intent)

βœ… Bound Service Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MyBoundService : Service() {

    private val binder = LocalBinder()

    inner class LocalBinder : Binder() {
        fun getService(): MyBoundService = this@MyBoundService
    }

    override fun onBind(intent: Intent?): IBinder {
        return binder
    }

    fun getTimestamp(): Long = System.currentTimeMillis()
}

Explanation:

  • The service exposes a binder interface to allow activities to call methods like getTimestamp().
  • When an activity binds, onBind() is called and the LocalBinder is returned.

Binding from activity:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
private var bound = false
private var service: MyBoundService? = null

private val connection = object : ServiceConnection {
    override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
        val localBinder = binder as MyBoundService.LocalBinder
        service = localBinder.getService()
        bound = true
    }

    override fun onServiceDisconnected(name: ComponentName?) {
        bound = 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 (bound) {
        unbindService(connection)
        bound = false
    }
}

βœ… Foreground Service Example (Modern Approach)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class MyForegroundService : Service() {

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val notification = NotificationCompat.Builder(this, "service_channel")
            .setContentTitle("Foreground Service")
            .setContentText("Running...")
            .setSmallIcon(R.drawable.ic_notification)
            .build()

        startForeground(1, notification)
        return START_NOT_STICKY
    }

    override fun onBind(intent: Intent?): IBinder? = null
}

Explanation:

  • Required for Android 8.0+ if you’re running a long task in the background.
  • startForeground() keeps your service alive with a persistent notification.

Create a notification channel:

1
2
3
4
5
6
7
8
9
10
11
fun createNotificationChannel(context: Context) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val channel = NotificationChannel(
            "service_channel",
            "Service Channel",
            NotificationManager.IMPORTANCE_DEFAULT
        )
        val manager = context.getSystemService(NotificationManager::class.java)
        manager.createNotificationChannel(channel)
    }
}

πŸ§™β€β™‚οΈ Tips & Best Practices

πŸ”‹ 1. Prefer WorkManager for Background Work

Use WorkManager for jobs that need guaranteed execution and system-friendly constraints:

1
2
val workRequest = OneTimeWorkRequestBuilder<MyWorker>().build()
WorkManager.getInstance(context).enqueue(workRequest)

⚠️ 2. Always use Foreground Services for Long Tasks

Long tasks like audio recording, location updates should use foreground services with notifications.


🧼 3. Stop Services Explicitly

1
stopService(Intent(this, MyStartedService::class.java))

Or from within:

1
stopSelf()

πŸ›  4. Use JobIntentService for Pre-Oreo Compatibility

If supporting API < 26, use JobIntentService to queue tasks safely.


🧠 Conclusion

Android Services give your app powerful background capabilities β€” but with great power comes responsibility. Choose the right type of service, respect the lifecycle, and always use modern alternatives (like WorkManager) when appropriate.

comments powered by Disqus