WorkManager, Notifikacie
WorkManager a Notifikácie na pozadí
1. Úvod do WorkManagera
WorkManager je knižnica určená na plánovanie odložiteľných a garantovaných úloh na pozadí. Zabezpečuje vykonanie práce aj vtedy, keď je aplikácia zatvorená alebo zariadenie reštartované. Je ideálny na pravidelnú kontrolu dát na serveri, synchronizáciu databázy a následné zobrazenie lokálnej notifikácie používateľovi priamo z behu úlohy na pozadí.
2. Potrebné závislosti (Version Catalog & build.gradle.kts)
1. Do súboru gradle/libs.versions.toml pridajte definíciu WorkManagera:
[versions]
work = "2.9.0"
[libraries]
androidx-work-runtime-ktx = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "work" }
2. Následne do súboru app/build.gradle.kts pridajte závislosť:
dependencies {
implementation(libs.androidx.work.runtime.ktx)
}
3. Povolenia a manifest (AndroidManifest.xml)
Od verzie Android 13 (API 33) je na zobrazenie notifikácií potrebné povolenie. Pridajte ho do AndroidManifest.xml:
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
V Jetpack Compose vyžiadate toto povolenie od používateľa priamo v composable obrazovke pred plánovaním prác:
val permissionLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestPermission(),
onResult = { isGranted ->
if (isGranted) {
// Povolenie udelené
}
}
)
LaunchedEffect(Unit) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
}
}
4. Vytvorenie Notification Channel (Kanála oznámení)
Pre Android 8.0+ (API 26+) musí každá notifikácia patriť do kanála. Kanál je najlepšie zaregistrovať pri štarte aplikácie (v triede Application alebo v MainActivity.onCreate()):
fun createNotificationChannel(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channelId = "sync_channel_id"
val name = "Kontrola úloh"
val descriptionText = "Oznámenia o dokončení úloh na pozadí"
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel = NotificationChannel(channelId, name, importance).apply {
description = descriptionText
}
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
}
5. Trieda Worker a zobrazenie notifikácie
Úloha na pozadí dedí z triedy CoroutineWorker. V metóde doWork() prebehne požadovaná logika a v prípade úspechu sa zostaví a odošle notifikácia cez NotificationCompat.Builder:
import android.Manifest
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
class NotificationWorker(
private val context: Context,
workerParams: WorkerParameters
) : CoroutineWorker(context, workerParams) {
override suspend fun doWork(): Result {
// 1. Vykonanie potrebnej práce na pozadí (napr. načítanie z databázy)
val title = inputData.getString("title") ?: "Nová správa"
val message = inputData.getString("message") ?: "Nezabudnite dnes na Vašu výzvu."
// 2. Zobrazenie notifikácie
sendNotification(title, message)
return Result.success()
}
private fun sendNotification(title: String, message: String) {
val channelId = "sync_channel_id"
// Kliknutie otvorí hlavnú Compose aktivitu
val intent = Intent(context, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent = PendingIntent.getActivity(
context,
0,
intent,
PendingIntent.FLAG_IMMUTABLE
)
val builder = NotificationCompat.Builder(context, channelId)
.setSmallIcon(R.drawable.ic_notification) // Nahraďte vlastnou ikonou zo zložky res/drawable
.setContentTitle(title)
.setContentText(message)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
// Kontrola povolenia pred zobrazením
if (ActivityCompat.checkSelfPermission(
context,
Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED
) {
NotificationManagerCompat.from(context).notify(1001, builder.build())
}
}
}
6. Spustenie a naplánovanie práce z aplikácie
V Compose UI alebo vo ViewModeli môžete naplánovať jednorazovú alebo opakovanú prácu a voliteľne jej odovzdať vstupné parametre:
// Nastavenie obmedzení (napr. iba pri pripojení na internet)
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
// Vstupné dáta pre Worker
val inputData = workDataOf(
"title" to "Aktualizácia dát",
"message" to "Dáta boli úspešne stiahnuté na pozadí."
)
// Zostavenie jednorazovej požiadavky
val workRequest = OneTimeWorkRequestBuilder<NotificationWorker>()
.setConstraints(constraints)
.setInputData(inputData)
.build()
// Zaradenie do frontu WorkManagera
WorkManager.getInstance(context).enqueue(workRequest)
Pre periodické spúšťanie (napr. každé 2 hodiny):
val repeatingRequest = PeriodicWorkRequestBuilder<NotificationWorker>(
2, TimeUnit.HOURS
).setInputData(inputData).build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
"PeriodicSyncWork",
ExistingPeriodicWorkPolicy.KEEP,
repeatingRequest
)
7. Spustenie a inicializácia priamo z MainActivity (Jetpack Compose)
Ukážka kompletnej MainActivity, ktorá pri štarte inicializuje kanál, v Composable požiada o povolenie a po kliknutí na tlačidlo naplánuje vykonanie Workera[cite: 2, 20]:
import android.Manifest
import android.os.Build
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.work.*
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 1. Vytvorenie kanála pri štarte aplikácie
createNotificationChannel(this)
setContent {
NotificationScreen()
}
}
}
@Composable
fun NotificationScreen() {
val context = LocalContext.current
// 2. Žiadosť o povolenie na zobrazenie notifikácií (Android 13+)
val permissionLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestPermission(),
onResult = { isGranted ->
// Spracovanie výsledku povolenia
}
)
LaunchedEffect(Unit) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
}
}
// 3. UI s tlačidlom na spustenie WorkManagera
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Button(
onClick = {
val inputData = workDataOf(
"title" to "Zadanie dokončené",
"message" to "Práca z WorkManagera bola úspešne vykonaná."
)
val workRequest = OneTimeWorkRequestBuilder<NotificationWorker>()
.setInputData(inputData)
.build()
// Spustenie cez WorkManager pomocou kontextu z LocalContext
WorkManager.getInstance(context).enqueue(workRequest)
}
) {
Text("Spustiť úlohu a zobraziť notifikáciu")
}
}
}