WorkManagerとは?
Android Jetpackのライブラリの一つ。
ForegroundServiceやJobSchedulerの代わりになる。
https://developer.android.com/topic/libraries/architecture/workmanager?hl=ja
やりたいこと
- アプリから切り離して長時間の処理を行わせたい
- ロジック部分はWorkManager外に定義したい
- ついでに通知に結果を流し込みたい
1. アプリから切り離して長時間の処理を行わせたい
WorkManager基礎部分
class MyWorker constructor(
private val context: Context,
private val params: WorkerParameters
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
// 処理
return Result.success()
}
}
class MyViewModel : ViewModel() {
fun run(){
WorkManager.getInstance(context)
.enqueue(OneTimeWorkRequest.from(MyWorker::class.java))
}
}
WorkerManagerをViewModel等から呼び出し、一度きり指定でMyWorkerを実行させます。
doWorkの中の処理がバックグラウンドで実行され、終わるとResult.success()が返却されます。
ForeGroundService化
override suspend fun doWork(): Result {
setForeground(ForegroundInfo(notificationId, notificationBuilder.build()))
// 処理
}
}
普通にNotificationBuilderを生成したら(割愛)、ForegroundInfo()に突っ込んでsetForegroundを呼び出します。
ForegroundServiceと違ってWorkManager側が実行をForeground化するかどうか決定出来るので、見る場所がスマートになっていい感じ。
#2. ロジック部分はWorkManager外に定義したい
MVVMパターンとHiltによるDIと組み合わせて、Repositoryに書いた処理をworkManagerから実行させたい。
@AndroidEntryPoint// つけられない
class MyWorker constructor(
private val context: Context,
private val params: WorkerParameters
) : CoroutineWorker(context, params) {
@Inject
lateinit var myRepository: MyRepository
override suspend fun doWork(): Result {
myRepository.doSomething()// not initialized
return Result.success()
}
}
WorkManagerはServiceではないので@AndroidEntryPointが設定出来ません。
そのため、Injectが作用せずnot initializedと怒られてしまいます。
ではどうするか。
@HiltAndroidApp
class MyApplication @Inject constructor() : Application() {
lateinit var myRepository: MyRepository
override fun create(){
WorkManager.initialize(
this,
Configuration.Builder()
.setWorkerFactory(MyWorkerFactory(myRepository)).build()
)
}
}
class ThetaWorkerFactory @Inject constructor(
private val myRepository: MyRepository
) : WorkerFactory() {
override fun createWorker(
appContext: Context,
workerClassName: String,
workerParameters: WorkerParameters
): ListenableWorker? {
return MyWorker(myRepository, appContext, workerParameters)
}
}
class MyWorker constructor(
private val myRepository: MyRepository,
private val context: Context,
private val params: WorkerParameters
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
myRepository.doSomething()
return Result.success()
}
}
<application>
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
android:exported="false"
tools:node="remove" />
ApplicationクラスでWorkerFactoryを上書きし、Workerに引数でrepositoryを渡すことで達成出来ました。
Workerは初期化が一度しか出来ないので、Manifestに初期化するなよとおまじないしなきゃいけないっていうやつは使うたび忘れそう気が利かない。
3. ついでに通知に結果を流し込みたい
長くなったので別記事に分けようと思います。