はじめに
Androidアプリ開発において、バックグラウンド処理は避けて通れない重要な機能です。データの同期、ファイルのアップロード、定期的な通知など、ユーザーがアプリを直接操作していない時でも確実に実行する必要があるタスクは数多く存在します。
従来、Androidのバックグラウンド処理には、JobScheduler、AlarmManager、Service、SyncAdapterなど、多様なAPIが存在していました。しかし、それぞれに異なるAPIや制約があり、デバイスのバージョンごとの互換性問題も抱えていました。
WorkManagerは、これらの課題を解決するためにGoogleが提供するJetpackライブラリです。統一されたAPIで、デバイスの再起動やアプリの終了後でも確実にタスクを実行できる、信頼性の高いバックグラウンド処理を実現します。
この記事では、WorkManagerの基本から実践的な使い方まで、2025年時点の最新情報を元に解説します。
WorkManager とは
WorkManagerは、遅延可能で信頼性の高いバックグラウンドタスクを実行するためのJetpackライブラリです。
主な特徴
- 保証された実行: アプリが終了しても、デバイスが再起動しても、タスクは確実に実行される
- 制約ベース: ネットワーク接続、充電状態、バッテリー残量などの条件を指定可能
- バッテリー効率: Dozeモードなどの省電力機能に準拠
- 柔軟なスケジューリング: 即時実行、遅延実行、定期実行をサポート
- 後方互換性: API 14以降をサポート、内部で最適なAPIを自動選択
2025年の現状
項目 | 状態 |
---|---|
最新バージョン | WorkManager 2.10.5(SDK 35対応) |
Hilt対応 | androidx.hilt:hilt-work で完全サポート |
Compose統合 | StateFlowでの状態観測が容易 |
WorkManager が適しているケース
✅ 定期的なデータ同期: サーバーとのデータ同期(1日1回など)
✅ ログやアナリティクスの送信: バックエンドへのログ送信
✅ 画像の圧縮やアップロード: 大きなファイルの処理
✅ データベースのクリーンアップ: 古いデータの削除
✅ 通知の定期配信: リマインダーや更新通知
WorkManager が適していないケース
❌ 即時実行が必要: UIの更新など(→ Coroutines使用)
❌ 正確なタイミング: 目覚まし時計など(→ AlarmManager使用)
❌ フォアグラウンド処理: 音楽再生など(→ Foreground Service使用)
❌ サーバーからのPush通知: (→ Firebase Cloud Messaging使用)
基本的な使い方
1. Worker の作成
Workerクラスは、バックグラウンドで実行する処理を定義します。
標準的な Worker
class UploadWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
override fun doWork(): Result {
return try {
// バックグラウンド処理を実行
uploadData()
// 成功
Result.success()
} catch (e: Exception) {
Log.e("UploadWorker", "Upload failed", e)
// 失敗(リトライ)
Result.retry()
}
}
private fun uploadData() {
// アップロード処理
Log.d("UploadWorker", "Uploading data...")
Thread.sleep(2000) // シミュレーション
Log.d("UploadWorker", "Upload complete")
}
}
CoroutineWorker
Coroutinesを使用する場合は、CoroutineWorker
を継承します。
class UploadWorker(
context: Context,
params: WorkerParameters
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
return withContext(Dispatchers.IO) {
try {
// suspendな処理を実行
uploadData()
Result.success()
} catch (e: Exception) {
Log.e("UploadWorker", "Upload failed", e)
Result.retry()
}
}
}
private suspend fun uploadData() {
// 実際のAPI呼び出しなど
Log.d("UploadWorker", "Uploading data...")
kotlinx.coroutines.delay(2000)
Log.d("UploadWorker", "Upload complete")
}
}
2. WorkRequest の作成
WorkRequestは、Workerをいつ、どのように実行するかを定義します。
OneTimeWorkRequest(一度だけ実行)
// 即座に実行
val uploadWorkRequest = OneTimeWorkRequestBuilder<UploadWorker>()
.build()
WorkManager.getInstance(context).enqueue(uploadWorkRequest)
PeriodicWorkRequest(定期実行)
// 6時間ごとに実行(最小間隔は15分)
val periodicWorkRequest = PeriodicWorkRequestBuilder<UploadWorker>(
6, TimeUnit.HOURS
).build()
WorkManager.getInstance(context).enqueue(periodicWorkRequest)
💡ポイント: PeriodicWorkRequest
の最小間隔は15分です。それより短い間隔で実行したい場合は、OneTimeWorkRequest
を使用し、Worker内で再スケジュールしてください。
3. Constraints(制約)の設定
Constraintsを使用すると、特定の条件が満たされた時のみタスクを実行できます。
val constraints = Constraints.Builder()
// ネットワーク接続が必要
.setRequiredNetworkType(NetworkType.CONNECTED)
// Wi-Fi接続が必要
.setRequiredNetworkType(NetworkType.UNMETERED)
// 充電中のみ
.setRequiresCharging(true)
// バッテリー残量が少なくない時
.setRequiresBatteryNotLow(true)
// デバイスがアイドル状態(Android 6.0以降)
.setRequiresDeviceIdle(true)
// ストレージ容量が少なくない時
.setRequiresStorageNotLow(true)
.build()
val workRequest = OneTimeWorkRequestBuilder<UploadWorker>()
.setConstraints(constraints)
.build()
WorkManager.getInstance(context).enqueue(workRequest)
Work の管理
一意な Work の実行
同じタスクを重複して実行しないようにするには、enqueueUniqueWork
を使用します。
WorkManager.getInstance(context).enqueueUniqueWork(
"upload_work", // 一意な名前
ExistingWorkPolicy.KEEP, // 既存のWorkを保持
workRequest
)
ExistingWorkPolicy の種類
ポリシー | 説明 |
---|---|
REPLACE |
既存のWorkをキャンセルして新しいWorkを実行 |
KEEP |
既存のWorkが存在する場合は新しいWorkを無視 |
APPEND |
既存のWorkの後に新しいWorkをチェーン |
APPEND_OR_REPLACE |
既存Workが失敗していたら置き換え、成功していたら追加 |
Work のチェーン
複数のWorkを順番に実行したり、並列実行したりできます。
順次実行
val compressWork = OneTimeWorkRequestBuilder<CompressWorker>().build()
val uploadWork = OneTimeWorkRequestBuilder<UploadWorker>().build()
val notifyWork = OneTimeWorkRequestBuilder<NotifyWorker>().build()
// 圧縮 → アップロード → 通知
WorkManager.getInstance(context)
.beginWith(compressWork)
.then(uploadWork)
.then(notifyWork)
.enqueue()
並列実行
val compressImageWork = OneTimeWorkRequestBuilder<CompressImageWorker>().build()
val compressVideoWork = OneTimeWorkRequestBuilder<CompressVideoWorker>().build()
val uploadWork = OneTimeWorkRequestBuilder<UploadWorker>().build()
// 画像圧縮と動画圧縮を並列実行 → 両方完了したらアップロード
WorkManager.getInstance(context)
.beginWith(listOf(compressImageWork, compressVideoWork))
.then(uploadWork)
.enqueue()
複雑なチェーン
val workA = OneTimeWorkRequestBuilder<WorkerA>().build()
val workB = OneTimeWorkRequestBuilder<WorkerB>().build()
val workC = OneTimeWorkRequestBuilder<WorkerC>().build()
val workD = OneTimeWorkRequestBuilder<WorkerD>().build()
val workE = OneTimeWorkRequestBuilder<WorkerE>().build()
// A, B を並列実行 → C → D, E を並列実行
val continuation = WorkManager.getInstance(context)
.beginWith(listOf(workA, workB))
.then(workC)
.then(listOf(workD, workE))
continuation.enqueue()
Hilt との統合
Application クラスの設定
HiltとWorkManagerを統合するには、ApplicationクラスでWorkManagerの初期化をカスタマイズする必要があります。Configuration.Provider
インターフェースを実装することで、WorkManagerのデフォルト初期化を無効化し、HiltによるWorkerの依存性注入を有効にします。
@HiltAndroidApp
class MyApplication : Application(), Configuration.Provider {
@Inject
lateinit var workerFactory: HiltWorkerFactory
override val workManagerConfiguration: Configuration
get() = Configuration.Builder()
.setWorkerFactory(workerFactory)
.setMinimumLoggingLevel(android.util.Log.DEBUG)
.build()
}
ポイント:
-
Configuration.Provider
を実装することで、WorkManagerの自動初期化が無効化されます -
HiltWorkerFactory
がHiltから注入され、Worker内での依存性注入を可能にします -
setMinimumLoggingLevel
でログレベルを設定できます(デバッグ時はDEBUG
、本番ではERROR
推奨)
HiltWorker の作成
@HiltWorker
アノテーションを使用することで、Worker内でRepositoryやUseCaseなどの依存性を注入できます。これにより、ビジネスロジックを再利用しながら、テスタブルなWorkerを作成できます。
@HiltWorker
class UploadWorker @AssistedInject constructor(
@Assisted context: Context,
@Assisted params: WorkerParameters,
private val uploadRepository: UploadRepository // Hiltで注入
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
return try {
val filePath = inputData.getString("file_path") ?: return Result.failure()
// Repositoryを使用してアップロード
uploadRepository.uploadFile(filePath)
Result.success()
} catch (e: Exception) {
Result.retry()
}
}
}
ポイント:
-
@HiltWorker
アノテーションでWorkerをHiltの管理対象にします - コンストラクタに
@AssistedInject
を付与します -
Context
とWorkerParameters
には@Assisted
を付与(WorkManagerが提供) - その他の依存性(Repository、UseCaseなど)は通常通りHiltで注入されます
- 依存性注入により、Workerのユニットテストが容易になります
Compose StateFlowでの状態観測
WorkManagerとJetpack Composeを組み合わせることで、バックグラウンド処理の進捗をリアルタイムでUIに反映できます。WorkInfo
をStateFlowとして観測し、Compose のcollectAsState()
で状態変化を監視します。これにより、アップロード進捗、処理状態、エラー情報などをリアクティブにUIに表示できます。
ViewModelでの状態管理
@HiltViewModel
class UploadViewModel @Inject constructor(
private val workManager: WorkManager
) : ViewModel() {
// WorkInfoをStateFlowとして公開
fun observeUploadWork(workId: UUID): StateFlow<WorkInfo?> {
return workManager.getWorkInfoByIdFlow(workId)
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = null
)
}
// ユニークな名前でWorkを観測
fun observeUniqueWork(uniqueWorkName: String): StateFlow<List<WorkInfo>> {
return workManager.getWorkInfosForUniqueWorkFlow(uniqueWorkName)
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = emptyList()
)
}
// アップロードを開始
fun startUpload(filePath: String): UUID {
val inputData = workDataOf("file_path" to filePath)
val uploadRequest = OneTimeWorkRequestBuilder<UploadWorker>()
.setInputData(inputData)
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
)
.build()
workManager.enqueueUniqueWork(
"upload_work",
ExistingWorkPolicy.KEEP,
uploadRequest
)
return uploadRequest.id
}
}
Composeでの状態観測とUI表示
@Composable
fun UploadScreen(
viewModel: UploadViewModel = hiltViewModel()
) {
var workId by remember { mutableStateOf<UUID?>(null) }
// WorkInfoの状態を観測
val workInfo by viewModel.observeUploadWork(workId ?: UUID.randomUUID())
.collectAsState()
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
// アップロードボタン
Button(
onClick = {
workId = viewModel.startUpload("/path/to/file.jpg")
},
enabled = workInfo?.state?.isFinished != false
) {
Text("アップロード開始")
}
Spacer(modifier = Modifier.height(16.dp))
// 状態表示
when (workInfo?.state) {
WorkInfo.State.ENQUEUED -> {
Text("待機中...")
CircularProgressIndicator()
}
WorkInfo.State.RUNNING -> {
Text("アップロード中...")
LinearProgressIndicator(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp)
)
// プログレス情報を表示
workInfo?.progress?.let { progress ->
val current = progress.getInt("current", 0)
val total = progress.getInt("total", 100)
Text("進捗: $current / $total")
}
}
WorkInfo.State.SUCCEEDED -> {
Icon(
imageVector = Icons.Default.CheckCircle,
contentDescription = "成功",
tint = Color.Green,
modifier = Modifier.size(48.dp)
)
Text("アップロード完了!")
}
WorkInfo.State.FAILED -> {
Icon(
imageVector = Icons.Default.Error,
contentDescription = "失敗",
tint = Color.Red,
modifier = Modifier.size(48.dp)
)
Text("アップロード失敗")
}
WorkInfo.State.CANCELLED -> {
Text("キャンセルされました")
}
else -> {
// 初期状態または未定義
}
}
}
}
Workerでのプログレス更新
Worker側から進捗情報を送信することで、UIでリアルタイムに進捗を表示できます。
@HiltWorker
class UploadWorker @AssistedInject constructor(
@Assisted context: Context,
@Assisted params: WorkerParameters,
private val uploadRepository: UploadRepository
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
return withContext(Dispatchers.IO) {
try {
val filePath = inputData.getString("file_path")
?: return@withContext Result.failure()
val fileSize = getFileSize(filePath)
var uploadedBytes = 0L
// 進捗をUI側に送信
uploadRepository.uploadFileWithProgress(filePath) { bytes ->
uploadedBytes += bytes
val progress = ((uploadedBytes.toFloat() / fileSize) * 100).toInt()
// プログレス更新
setProgress(
workDataOf(
"current" to uploadedBytes.toInt(),
"total" to fileSize.toInt(),
"progress" to progress
)
)
}
Result.success()
} catch (e: Exception) {
Log.e("UploadWorker", "Upload failed", e)
Result.retry()
}
}
}
private fun getFileSize(filePath: String): Long {
return File(filePath).length()
}
}
ポイント:
-
WorkManager.getWorkInfoByIdFlow()
またはgetWorkInfosForUniqueWorkFlow()
でFlowを取得 -
stateIn()
でFlowをStateFlowに変換し、ViewModelのスコープで管理 - Compose側で
collectAsState()
を使って状態を監視 -
WorkInfo.State
で実行状態を判定(ENQUEUED、RUNNING、SUCCEEDED、FAILEDなど) -
setProgress()
でWorker側からUIへ進捗情報を送信可能 -
WorkInfo.progress
から進捗データを取得してプログレスバーに反映
まとめ
WorkManagerは、2025年現在、Androidのバックグラウンド処理における最も信頼性が高く、推奨される選択肢です。遅延可能で確実に実行する必要があるタスクには、WorkManagerを使用することで、ユーザー体験を損なうことなく、効率的なバックグラウンド処理を実現できます。