Android SDK
03 / 03

Services, Permissions & Background Work

Android SDK: Services, Permissions & Background Work

WorkManager

WorkManager is the recommended API for deferrable, guaranteed background work. It survives app restarts and works across Android versions.

// Add dependency: implementation("androidx.work:work-runtime-ktx:2.9.0")

// Define work
class UploadWorker(context: Context, params: WorkerParameters) :
    CoroutineWorker(context, params) {

    override suspend fun doWork(): Result {
        val fileUri = inputData.getString("file_uri") ?: return Result.failure()
        return try {
            uploadFile(fileUri)
            Result.success()
        } catch (e: Exception) {
            if (runAttemptCount < 3) Result.retry() else Result.failure()
        }
    }
}

// Schedule work
val uploadRequest = OneTimeWorkRequestBuilder<UploadWorker>()
    .setInputData(workDataOf("file_uri" to "content://..."))
    .setConstraints(
        Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)
            .setRequiresBatteryNotLow(true)
            .build()
    )
    .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, TimeUnit.SECONDS)
    .build()

WorkManager.getInstance(context).enqueue(uploadRequest)

// Periodic work
val syncRequest = PeriodicWorkRequestBuilder<SyncWorker>(15, TimeUnit.MINUTES)
    .build()
WorkManager.getInstance(context)
    .enqueueUniquePeriodicWork("sync", ExistingPeriodicWorkPolicy.KEEP, syncRequest)

// Observe status
WorkManager.getInstance(context)
    .getWorkInfoByIdLiveData(uploadRequest.id)
    .observe(this) { info ->
        if (info?.state == WorkInfo.State.SUCCEEDED) showSuccess()
    }

Foreground Services

// For long-running tasks visible to user (music playback, navigation, upload)
class MusicService : Service() {

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val notification = buildNotification()
        startForeground(NOTIFICATION_ID, notification)  // must call within 5s
        // do work...
        return START_STICKY
    }

    override fun onBind(intent: Intent?) = null

    private fun buildNotification(): Notification {
        val channel = NotificationChannel(
            "music_channel", "Music Playback", NotificationManager.IMPORTANCE_LOW
        )
        getSystemService(NotificationManager::class.java).createNotificationChannel(channel)

        return NotificationCompat.Builder(this, "music_channel")
            .setContentTitle("Now Playing")
            .setSmallIcon(R.drawable.ic_music)
            .build()
    }
}

// Start from Activity
val serviceIntent = Intent(this, MusicService::class.java)
ContextCompat.startForegroundService(this, serviceIntent)

BroadcastReceiver

// Dynamic receiver (registered in code, active while app runs)
class NetworkReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        val isConnected = cm.activeNetwork != null
        // handle connectivity change
    }
}

// Register/unregister with lifecycle
private lateinit var receiver: NetworkReceiver
override fun onResume() {
    super.onResume()
    receiver = NetworkReceiver()
    registerReceiver(receiver, IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION))
}
override fun onPause() {
    super.onPause()
    unregisterReceiver(receiver)
}

// Send local broadcast (within app only)
LocalBroadcastManager.getInstance(this)
    .sendBroadcast(Intent("com.example.CUSTOM_ACTION"))

Key Jetpack Libraries

  • ViewModel + LiveData/StateFlow: survive configuration changes, hold UI state.

  • Room: SQLite ORM with compile-time query verification, Flow support, migrations.

  • Navigation Component: type-safe navigation graph, back stack management, deep links.

  • DataStore: replaces SharedPreferences — coroutine-based, type-safe (Preferences or Proto).

  • Hilt: dependency injection built on Dagger, first-class Android support.

  • Retrofit + OkHttp: HTTP client with Kotlin coroutine adapters.

  • Coil: coroutine-based image loading, Compose support.

  • Paging 3: paginated data loading from network/database with RecyclerView or Compose integration.

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free