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