新型コロナの影響で自宅待機になってしまい、その間勉強するものとしてSunflower
リポジトリを
勧めてもらいました。
JetPackのライブラリのうち、今回はWorkManager
編です
尚、引用しているソースは明記しているところ以外は、基本的には全てSunflower
のリポジトリのものです。
環境
- 確認時は
Android Studio
のバージョンは3.6.2
を使用しました -
JetPack
はAndroidX
ライブラリを利用するのでCompile SDK
を28
以上にする必要があります
そもそもWorkManager
ってなに?
WorkManager API を使用すると、アプリが終了したりデバイスが再起動したりしても
実行することが要求される延期可能な非同期タスクのスケジュールを簡単に設定できます。
です!
WorkManager
の主な機能
機能
- API 14 までの下位互換性
- API 23 以上が搭載されたデバイスでは
JobScheduler
を使用 - API 14~22 が搭載されたデバイスでは
BroadcastReceiver
とAlarmManager
を組み合わせて使用
- API 23 以上が搭載されたデバイスでは
- ネットワークの可用性や充電ステータスなどの処理の制約を追加する
- 非同期の 1 回限りのタスクや定期的なタスクのスケジュールを設定する
- スケジュール設定されたタスクの監視と管理を行う
- タスクを連携させる
- アプリやデバイスが再起動してもタスクを確実に実行する
- Doze モードなどの省電力機能に準拠する
(公式より)
この中だと、APIレベルが低いものでも同じように使えるというところと、Dozeモード
に対応しているのがいいですね
(Dozeモード中
にバックグラウンド処理が色々動かない、というのはAndroidあるあるだと思いますので。。。)
注意点
公式によると
WorkManager は、アプリが終了したりデバイスが再起動したりしても確実に実行する必要がある遅延可能なタスク(つまり、直ちに実行する必要がないタスク)を対象としています。次に例を示します。
- ログやアナリティクスをバックエンド サービスに送信する
- アプリデータをサーバーと定期的に同期する
つまり、確実に定周期で何かをする、などということには対応していない、ということですね。
利用する際に必要なこと
必要なことは下記の4点です
- 依存関係の記載
- バックグラウンドタスクの作成
- タスクを実行する方法とタイミングを設定とタスクをシステムに引き渡す処理
依存関係の記載
build.gradle
に記載する内容
build.gradle
には下記の依存関係の記載を行います。
(公式ドキュメントより)
dependencies {
def work_version = "2.3.1"
// (Java only)
implementation "androidx.work:work-runtime:$work_version"
// Kotlin + coroutines
implementation "androidx.work:work-runtime-ktx:$work_version"
// optional - RxJava2 support
implementation "androidx.work:work-rxjava2:$work_version"
// optional - GCMNetworkManager support
implementation "androidx.work:work-gcm:$work_version"
// optional - Test helpers
androidTestImplementation "androidx.work:work-testing:$work_version"
}
- 必須設定
Java
-
Kotlin + Coroutines
-
Java
で使う場合は、Java
の設定が、Kotlin
で使う場合はKotlin
の設定が必須となります。
-
- オプション設定
RxJava2
GCMNetworkManager
-
Test helpers
- これらはオプションなので、利用する場合は記載します。
Sunflower
リポジトリの場合
Sunflower
リポジトリの場合はどのようになっているかを見てみましょう
buildscript {
// Define versions in a single place
ext {
:
// App dependencies
:
workVersion = '2.1.0'
}
:
}
dependencies {
:
implementation "androidx.work:work-runtime-ktx:$rootProject.workVersion"
:
// Testing dependencies
:
androidTestImplementation "androidx.work:work-testing:$rootProject.workVersion"
:
}
Sunflower
リポジトリの場合は、下記設定となっていました。
-
Kotlin
で利用しています - オプションは
Test helpers
を利用しています
バックグラウンド タスクを作成する
それではSunflower
のバックグラウンドタスクを見てみましょう。
class SeedDatabaseWorker(
context: Context,
workerParams: WorkerParameters
) : CoroutineWorker(context, workerParams) {
override suspend fun doWork(): Result = coroutineScope {
.
.
.
- タスクは通常は
Worker
クラスを使用して定義します -
Workmanager
から提供されるバックグラウンドスレッドでdowork()
メソッドが同期的
に実行されます - ただし、
Sunflower
リポジトリではCoroutineWorker
を利用しています。- こちらは
Kotlin
を使う際にオススメです - その場合
doWork()
メソッドはsuspend
関数になり、サブスレッドで実行されます。- 実行するスレッドは、
Worker
クラスとは異なり、Configuration
で指定されたExecutor
ではありません。 -
Dispatchers.Default
で実行されます。
- 実行するスレッドは、
- こちらは
try {
applicationContext.assets.open(PLANT_DATA_FILENAME).use { inputStream ->
JsonReader(inputStream.reader()).use { jsonReader ->
val plantType = object : TypeToken<List<Plant>>() {}.type
val plantList: List<Plant> = Gson().fromJson(jsonReader, plantType)
val database = AppDatabase.getInstance(applicationContext)
database.plantDao().insertAll(plantList)
Result.success()
}
}
} catch (ex: Exception) {
Log.e(TAG, "Error seeding database", ex)
Result.failure()
}
}
companion object {
private val TAG = SeedDatabaseWorker::class.java.simpleName
}
}
- 以降には、バックグラウンドタスクで実施する処理が記載されています。
- ここでは
assets
内のJsonファイルを読み込んだあと、パースしてデータベースに保存しています。
- ここでは
-
doWork()
メソッドはResult
の各メソッドを返します。-
Result.success()
: タスクが正常に終了したかどうか -
Result.failure()
: タスクが失敗したかどうか -
Result.retry()
: 後でタスクを再試行する必要があるかどうか- ここでは、データベースに保存ができれば正常終了、例外が発生した場合は失敗となっています。
-
タスク実行方法とタイミングを設定およびタスクをシステムに引き渡す処理
概要
-
Worker(CoroutineWorker)
が作業単位を定義しますが、それに対し、WorkRequest
は作業を実行する方法とタイミングを定義します。 - タスクには1回実行するものと、定期的に実行されるものがあります
- 1回のもの →
OneTimeWorkRequest
- 定期的なもの →
PeriodicWorkRequest
- 1回のもの →
-
WorkRequest
を定義した後、WorkManager
でenqueue()
メソッドを使用してスケジュールを設定できます。
Sunflower
リポジトリ
// Create and pre-populate the database. See this article for more details:
// https://medium.com/google-developers/7-pro-tips-for-room-fbadea4bfbd1#4785
private fun buildDatabase(context: Context): AppDatabase {
return Room.databaseBuilder(context, AppDatabase::class.java, DATABASE_NAME)
.addCallback(object : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
val request = OneTimeWorkRequestBuilder<SeedDatabaseWorker>().build()
WorkManager.getInstance(context).enqueue(request)
}
})
.build()
}
Sunflower
リポジトリでは下記の処理になっています。
- データベース作成時に一度だけ実施する処理
OneTimeWorkRequestBuilder
でSeedDatabaseWorker
を実施している。
まとめ
- バックグラウンドタスクの作成は
Worker
クラスを作成する。Kotlin
の場合はCoroutineWorker
がおすすめ -
Worker
クラスのdowork()
メソッド内に実行する処理を書く。 - タスクの実行の際、1回しか実施しないことは、
OneTimeWorkRequest
、定期的に実施するものはPeriodicWorkRequest
で実行する。
以上です!
参考サイト
- Sunflowerリポジトリ
- WorkManager: 公式ドキュメント