Android WorkManager 2026年完全ガイド:バックグラウンドタスク、制約条件、面接対策

Android WorkManagerを使ったバックグラウンド処理の実装方法を詳しく解説。制約条件の設定、定期実行、チェーン処理、2026年の技術面接でよく問われる質問まで網羅的にカバー。

Android WorkManagerによるバックグラウンドタスク処理の図解

2026年現在、Android WorkManagerはバックグラウンドタスクを処理するための標準ソリューションとして確立されています。Android 15のバッテリー最適化強化により、アプリがバックグラウンドで確実にタスクを実行することがこれまで以上に重要になっています。WorkManagerは、アプリが終了した後やデバイスが再起動した後でも、遅延可能で信頼性の高い非同期タスクを実行するためのAndroid Jetpackライブラリです。本記事では、WorkManagerの基本概念から高度な使用方法、そして2026年の技術面接でよく問われる質問について詳しく解説します。

WorkManagerを選ぶべき理由

WorkManagerは、JobScheduler、AlarmManager、Firebase JobDispatcherの機能を統合し、APIレベル21以上で一貫した動作を保証します。タスクの制約条件(ネットワーク接続、充電状態など)を宣言的に指定でき、システムがタスクの最適な実行タイミングを自動的に決定します。Foreground Serviceが即時実行を必要とするタスク向けであるのに対し、WorkManagerは遅延可能だが確実に実行されるべきタスクに最適です。

WorkManagerの基本:OneTimeWorkRequestとWorkerクラス

WorkManagerの基本的な構成要素は、WorkerクラスとWorkRequestです。Workerクラスはバックグラウンドで実行するタスクのロジックを定義し、WorkRequestはそのタスクをどのように実行するかを指定します。

まず、WorkManagerの依存関係をプロジェクトに追加します。

build.gradle.kts (Module)kotlin
dependencies {
    implementation("androidx.work:work-runtime-ktx:2.10.0")
    // For testing
    androidTestImplementation("androidx.work:work-testing:2.10.0")
}

次に、データを同期するシンプルなWorkerを実装します。

kotlin
class SyncDataWorker(
    context: Context,
    params: WorkerParameters
) : Worker(context, params) {

    override fun doWork(): Result {
        return try {
            // Get input data
            val userId = inputData.getString(KEY_USER_ID)
                ?: return Result.failure()

            // Perform sync operation
            val syncResult = performDataSync(userId)

            // Return output data on success
            val outputData = workDataOf(
                KEY_SYNC_COUNT to syncResult.recordCount,
                KEY_LAST_SYNC to System.currentTimeMillis()
            )
            Result.success(outputData)

        } catch (e: NetworkException) {
            // Retry on network errors
            Result.retry()
        } catch (e: Exception) {
            // Permanent failure
            Result.failure()
        }
    }

    private fun performDataSync(userId: String): SyncResult {
        // Actual sync logic here
        return SyncResult(recordCount = 42)
    }

    companion object {
        const val KEY_USER_ID = "user_id"
        const val KEY_SYNC_COUNT = "sync_count"
        const val KEY_LAST_SYNC = "last_sync"
    }
}

このWorkerをスケジュールするには、OneTimeWorkRequestを作成してWorkManagerにエンキューします。

kotlin
class DataSyncManager(private val context: Context) {

    fun scheduleSyncWork(userId: String) {
        val inputData = workDataOf(
            SyncDataWorker.KEY_USER_ID to userId
        )

        val syncWorkRequest = OneTimeWorkRequestBuilder<SyncDataWorker>()
            .setInputData(inputData)
            .addTag("sync_work")
            .build()

        WorkManager.getInstance(context)
            .enqueueUniqueWork(
                "user_sync_$userId",
                ExistingWorkPolicy.REPLACE,
                syncWorkRequest
            )
    }
}

enqueueUniqueWorkを使用することで、同じ識別子を持つタスクが重複してスケジュールされることを防止できます。ExistingWorkPolicy.REPLACEは既存のタスクをキャンセルして新しいタスクに置き換え、KEEPは既存のタスクを保持します。

制約条件の設定:実行条件の制御

WorkManagerの最も強力な機能の一つは、タスクの実行条件を制約として指定できることです。これにより、ネットワーク接続がある場合のみ、または充電中のみタスクを実行するといった制御が可能になります。

kotlin
fun scheduleImageUploadWork(imageUri: String) {
    val constraints = Constraints.Builder()
        .setRequiredNetworkType(NetworkType.UNMETERED)  // WiFi only
        .setRequiresBatteryNotLow(true)                 // Battery > 20%
        .setRequiresCharging(false)                     // No charging required
        .setRequiresStorageNotLow(true)                 // Sufficient storage
        .build()

    val uploadWorkRequest = OneTimeWorkRequestBuilder<ImageUploadWorker>()
        .setConstraints(constraints)
        .setInputData(workDataOf("image_uri" to imageUri))
        .setBackoffCriteria(
            BackoffPolicy.EXPONENTIAL,
            Duration.ofMinutes(1)
        )
        .build()

    WorkManager.getInstance(context)
        .enqueue(uploadWorkRequest)
}

NetworkTypeには以下のオプションがあります:

  • NOT_REQUIRED: ネットワーク不要
  • CONNECTED: 任意のネットワーク接続
  • UNMETERED: WiFiなどの従量制でないネットワーク
  • METERED: モバイルデータなどの従量制ネットワーク
  • NOT_ROAMING: ローミング中でないネットワーク

setBackoffCriteriaは、タスクがResult.retry()を返した場合のリトライ間隔を設定します。EXPONENTIALポリシーでは、リトライごとに待機時間が2倍になります(1分、2分、4分...)。

定期実行タスク:PeriodicWorkRequest

バックグラウンドでの定期的なデータ同期やキャッシュクリーンアップなど、繰り返し実行するタスクにはPeriodicWorkRequestを使用します。

kotlin
class PeriodicSyncScheduler(private val context: Context) {

    fun schedulePeriodicSync() {
        val constraints = Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)
            .setRequiresBatteryNotLow(true)
            .build()

        val periodicSyncRequest = PeriodicWorkRequestBuilder<SyncDataWorker>(
            repeatInterval = 6, TimeUnit.HOURS,
            flexInterval = 30, TimeUnit.MINUTES
        )
            .setConstraints(constraints)
            .setInputData(workDataOf("sync_type" to "periodic"))
            .addTag("periodic_sync")
            .build()

        WorkManager.getInstance(context)
            .enqueueUniquePeriodicWork(
                "periodic_data_sync",
                ExistingPeriodicWorkPolicy.UPDATE,
                periodicSyncRequest
            )
    }

    fun cancelPeriodicSync() {
        WorkManager.getInstance(context)
            .cancelUniqueWork("periodic_data_sync")
    }
}

flexIntervalパラメータは、タスクが実行される時間枠を指定します。上記の例では、6時間ごとに実行され、実行タイミングは各間隔の最後の30分以内となります。これにより、システムは複数のアプリのタスクをバッチ処理してバッテリーを節約できます。

注意点として、定期タスクの最小間隔は15分です。これより短い間隔を指定しても、システムは15分に強制します。

タスクチェーン:順次実行と並列実行

WorkManagerでは、複数のタスクを順次または並列に実行するチェーンを構築できます。これは、画像のダウンロード、処理、アップロードといった一連の操作に最適です。

kotlin
class ImageProcessingPipeline(private val context: Context) {

    fun processAndUploadImages(imageIds: List<String>) {
        val workManager = WorkManager.getInstance(context)

        // Step 1: Download images in parallel
        val downloadRequests = imageIds.map { imageId ->
            OneTimeWorkRequestBuilder<DownloadImageWorker>()
                .setInputData(workDataOf("image_id" to imageId))
                .addTag("download")
                .build()
        }

        // Step 2: Apply filters (runs after ALL downloads complete)
        val filterRequest = OneTimeWorkRequestBuilder<ApplyFilterWorker>()
            .addTag("filter")
            .build()

        // Step 3: Compress images
        val compressRequest = OneTimeWorkRequestBuilder<CompressImageWorker>()
            .addTag("compress")
            .build()

        // Step 4: Upload to server
        val uploadRequest = OneTimeWorkRequestBuilder<UploadImageWorker>()
            .setConstraints(
                Constraints.Builder()
                    .setRequiredNetworkType(NetworkType.CONNECTED)
                    .build()
            )
            .addTag("upload")
            .build()

        // Build the chain
        workManager
            .beginWith(downloadRequests)  // Parallel execution
            .then(filterRequest)          // Sequential after downloads
            .then(compressRequest)         // Sequential
            .then(uploadRequest)           // Sequential
            .enqueue()
    }
}

beginWithに複数のWorkRequestを渡すと、それらは並列に実行されます。thenでチェーンされたタスクは、前のすべてのタスクが成功した後に実行されます。

タスクの監視とキャンセル

実行中のタスクの状態を監視し、必要に応じてキャンセルすることは、優れたユーザーエクスペリエンスのために不可欠です。

kotlin
class WorkStatusObserver(private val context: Context) {

    fun observeWorkStatus(workId: UUID): Flow<WorkInfo.State> {
        return WorkManager.getInstance(context)
            .getWorkInfoByIdFlow(workId)
            .map { it?.state ?: WorkInfo.State.CANCELLED }
    }

    fun observeWorkByTag(tag: String): Flow<List<WorkInfo>> {
        return WorkManager.getInstance(context)
            .getWorkInfosByTagFlow(tag)
    }

    // Usage in ViewModel
    fun monitorSyncProgress() {
        viewModelScope.launch {
            observeWorkByTag("sync_work").collect { workInfoList ->
                workInfoList.forEach { workInfo ->
                    when (workInfo.state) {
                        WorkInfo.State.RUNNING -> {
                            val progress = workInfo.progress.getInt("progress", 0)
                            _uiState.update { it.copy(syncProgress = progress) }
                        }
                        WorkInfo.State.SUCCEEDED -> {
                            val syncCount = workInfo.outputData.getInt("sync_count", 0)
                            _uiState.update { it.copy(syncComplete = true, recordCount = syncCount) }
                        }
                        WorkInfo.State.FAILED -> {
                            _uiState.update { it.copy(syncError = "Sync failed") }
                        }
                        else -> { /* Handle other states */ }
                    }
                }
            }
        }
    }
}

タスクをキャンセルするには、タグ、ユニーク名、またはIDを使用します:

kotlin
val workManager = WorkManager.getInstance(context)

// Cancel by unique name
workManager.cancelUniqueWork("user_sync_123")

// Cancel by tag
workManager.cancelAllWorkByTag("sync_work")

// Cancel by ID
workManager.cancelWorkById(workId)

// Cancel all work (use with caution)
workManager.cancelAllWork()

CoroutineWorkerによる非同期処理

Kotlinコルーチンを使用する場合、CoroutineWorkerがより自然な選択です。doWorkがサスペンド関数となり、非同期操作を簡潔に記述できます。

kotlin
class CoroutineSyncWorker(
    context: Context,
    params: WorkerParameters
) : CoroutineWorker(context, params) {

    @Inject
    lateinit var repository: DataRepository

    override suspend fun doWork(): Result {
        return try {
            setForeground(createForegroundInfo())

            val totalRecords = 100
            var processedRecords = 0

            repository.getRecordsToSync().collect { record ->
                repository.syncRecord(record)
                processedRecords++

                // Report progress
                setProgress(workDataOf(
                    "progress" to (processedRecords * 100 / totalRecords)
                ))
            }

            Result.success(workDataOf(
                "synced_count" to processedRecords
            ))

        } catch (e: CancellationException) {
            throw e  // Don't catch cancellation
        } catch (e: Exception) {
            if (runAttemptCount < 3) {
                Result.retry()
            } else {
                Result.failure(workDataOf("error" to e.message))
            }
        }
    }

    private fun createForegroundInfo(): ForegroundInfo {
        val notification = NotificationCompat.Builder(applicationContext, "sync_channel")
            .setContentTitle("Syncing Data")
            .setSmallIcon(R.drawable.ic_sync)
            .setOngoing(true)
            .build()

        return ForegroundInfo(
            NOTIFICATION_ID,
            notification,
            ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
        )
    }

    companion object {
        private const val NOTIFICATION_ID = 1001
    }
}

setForegroundを呼び出すことで、長時間実行されるタスクをForeground Serviceとして実行し、システムによるキャンセルを防止できます。Android 14以降では、foregroundServiceTypeを明示的に指定する必要があります。

Androidの面接対策はできていますか?

インタラクティブなシミュレーター、flashcards、技術テストで練習しましょう。

2026年技術面接でよく問われる質問

Androidの技術面接では、WorkManagerに関する深い理解が求められることが多くあります。以下は、よく問われる質問とその回答のポイントです。

Q1: WorkManagerとForeground Serviceの使い分けは?

WorkManagerは遅延可能だが確実に実行されるべきタスクに使用します(例:ログのアップロード、定期的な同期)。Foreground Serviceは即時かつ継続的な実行が必要なタスクに使用します(例:音楽再生、位置追跡)。WorkManagerのタスクはシステムの判断で遅延される可能性がありますが、Foreground Serviceは即座に開始されます。

Q2: Dozeモードとの関係は?

Dozeモード中、WorkManagerのタスクはメンテナンスウィンドウまで遅延されます。ただし、setExpedited()を使用すると、より早いタイミングでの実行を要求できます。Android 12以降では、setExpeditedはForeground Serviceを自動的に使用してタスクを実行します。

Q3: Worker内での依存性注入は?

Hilt 2.50以降では、@HiltWorkerアノテーションを使用して簡単に依存性を注入できます:

kotlin
@HiltWorker
class HiltSyncWorker @AssistedInject constructor(
    @Assisted context: Context,
    @Assisted params: WorkerParameters,
    private val repository: DataRepository  // Injected
) : CoroutineWorker(context, params) {
    // ...
}

Q4: テストはどのように行うか?

work-testingライブラリを使用して、同期的にWorkerをテストできます:

kotlin
@Test
fun testSyncWorker() {
    val worker = TestListenableWorkerBuilder<SyncDataWorker>(context)
        .setInputData(workDataOf("user_id" to "123"))
        .build()

    val result = worker.doWork()

    assertThat(result).isEqualTo(ListenableWorker.Result.success())
}

まとめ

Android WorkManagerは、2026年のAndroid開発においてバックグラウンドタスク処理の標準ツールとして確立されています。制約条件の宣言的な指定、タスクチェーンの構築、そしてCoroutineWorkerによる非同期処理のサポートにより、信頼性の高いバックグラウンド処理を簡潔に実装できます。

技術面接では、WorkManagerの基本概念だけでなく、Dozeモードとの関係、適切な使い分け、テスト手法についても説明できることが求められます。本記事で紹介したパターンを実際のプロジェクトで活用し、深い理解を身につけることが重要です。

今日のチャレンジ

Android のバグを見つけられますか

実際のコード、隠れたバグ、1日1回。アカウントなしで試せます。

Anthony Fillion-Maillet

執筆

Anthony Fillion-Maillet

SharpSkill 創業者

10 年以上フルスタック開発に携わっています。SharpSkill を運営し、ここで公開される内容に責任を負っています。

2026年8月22日 更新

タグ

#android
#workmanager
#kotlin
#background-tasks
#jetpack

共有

関連記事