Scenario-Based Use Cases

Scenario 1: Music Player

Requirement

User starts a song, goes to another app, and music should continue.

Best Choice

Foreground Service

Why?

| Reason |

| ----------------------------------------- |

| Music is user-visible work |

| Music should continue in background |

| User should see notification |

| Notification can have play/pause controls |

Flow

User clicks Play
        ↓
Start Foreground MusicService
        ↓
Show music notification
        ↓
Start MediaPlayer
      ↓
User controls music from notification


Scenario 2: Large Video Upload

Requirement

User uploads a large video. Upload should continue after leaving the screen.

Best Choice

CaseBest Option
Upload is immediate and user-visibleForeground Service
Upload can be delayed/retriedWorkManager

Flow

User clicks Upload
        ↓
Create upload notification
        ↓
Start Foreground Service
        ↓
Upload file
        ↓
Update progress notification
        ↓
Upload complete
        ↓
Stop service


Scenario 3: Daily Data Sync

Requirement

App needs to sync data once every day in background.

Best Choice

WorkManager

Why?

| Reason |

| --------------------------------------------- |

| Daily sync is deferrable |

| WorkManager handles retry |

| WorkManager handles network constraints |

| WorkManager works better with OS restrictions |

Flow

Schedule PeriodicWorkRequest
        ↓
Set network constraint
        ↓
Run once every 24 hours
        ↓
Sync data
        ↓
Retry if failed


Scenario 4: Location Tracking

Requirement

Fitness app tracks user running for 1 hour.

Best Choice

Foreground Service with location type

Why?

| Reason |

| ------------------------------------- |

| Location tracking is ongoing |

| User must know location is being used |

| Work should continue in background |

| Notification is required |

Manifest Example

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<service
    android:name=".LocationTrackingService"
    android:exported="false"
    android:foregroundServiceType="location" />


Scenario 5: Bluetooth Device Connection

Requirement

Activity needs to communicate with a Bluetooth device continuously.

Best Choice

Bound Service

Use Foreground Service also if Bluetooth communication should continue after app goes to background.

Flow

Activity binds to BluetoothService
        ↓
Activity calls connect()
        ↓
Service maintains Bluetooth socket
        ↓
Activity calls sendCommand()
        ↓
Service returns response
        ↓
Activity unbinds when screen closes


Scenario 6: Timer App

Requirement

User starts stopwatch and closes app. Timer should continue.

Best Choice

Foreground Service

Why?

| Reason |

| ---------------------------------- |

| Timer is ongoing |

| User-visible task |

| Notification can show elapsed time |


Scenario 7: Chat Message Notification

Requirement

App receives chat message from FCM.

Best Choice

TaskBest Option
Simple notificationFirebaseMessagingService
Long processingWorkManager
Download image/message syncWorkManager

Flow

FCM message received
        |
        v
Is it simple notification?
        |
   +----+----+
   |         |
  Yes        No
   |         |
   v         v
Show       Schedule
Notification WorkManager


Mini Example — Location Foreground Service

class LocationTrackingService : Service() {

    override fun onCreate() {
        super.onCreate()
        createNotificationChannel()
    }

    override fun onStartCommand(
        intent: Intent?,
        flags: Int,
        startId: Int
    ): Int {

        val notification = NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("Tracking your run")
            .setContentText("Location tracking is active")
            .setSmallIcon(R.drawable.ic_location)
            .setOngoing(true)
            .build()

        startForeground(101, notification)

        startLocationUpdates()

        return START_STICKY
    }

    private fun startLocationUpdates() {
        // Request location updates from FusedLocationProviderClient
    }

    override fun onDestroy() {
        super.onDestroy()

        // Stop location updates here
    }

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

    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

            val channel = NotificationChannel(
                CHANNEL_ID,
                "Location Tracking",
                NotificationManager.IMPORTANCE_LOW
            )

            getSystemService(NotificationManager::class.java)
                .createNotificationChannel(channel)
        }
    }

    companion object {
        const val CHANNEL_ID = "location_channel"
    }
}


Mini Example — Started Service for Log Upload

class LogUploadService : Service() {

    override fun onStartCommand(
        intent: Intent?,
        flags: Int,
        startId: Int
    ): Int {

        val logFile = intent?.getStringExtra("log_file")

        Thread {
            try {
                uploadLogFile(logFile)
            } finally {
                stopSelf(startId)
            }
        }.start()

        return START_REDELIVER_INTENT
    }

    private fun uploadLogFile(file: String?) {
        Log.d("LogUploadService", "Uploading $file")
    }

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

Why use stopSelf(startId)?

If multiple start requests come,
stopSelf(startId) stops the correct request safely.

---

Mini Example — Bound Counter Service

CounterService.kt

class CounterService : Service() {

    private val binder = CounterBinder()
    private var counter = 0

    inner class CounterBinder : Binder() {
        fun getService(): CounterService {
            return this@CounterService
        }
    }

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

    fun increment() {
        counter++
    }

    fun getCounter(): Int {
        return counter
    }
}

MainActivity.kt

class MainActivity : AppCompatActivity() {

    private var service: CounterService? = null
    private var bound = false

    private val connection = object : ServiceConnection {

        override fun onServiceConnected(
            name: ComponentName?,
            binder: IBinder?
        ) {
            val counterBinder = binder as CounterService.CounterBinder
            service = counterBinder.getService()
            bound = true
        }

        override fun onServiceDisconnected(name: ComponentName?) {
            bound = false
            service = null
        }
    }

    override fun onStart() {
        super.onStart()

        bindService(
            Intent(this, CounterService::class.java),
            connection,
            Context.BIND_AUTO_CREATE
        )
    }

    fun onButtonClick() {
        service?.increment()

        val value = service?.getCounter()

        Log.d("Counter", "Value = $value")
    }

    override fun onStop() {
        super.onStop()

        if (bound) {
            unbindService(connection)
            bound = false
        }
    }
}