はじめに
JetPakckのWorkerへDI(コンストラクタインジェクション)する方法を日本語で解説している物がなかったのでメモ
※Qiita初投稿なので、稚拙なところや間違いはどんどんまさかりを投げていただけると嬉しいです。
問題点
WorkerはAndroidフレームワークがインスタンスの生成を行うため、デフォルトではContextとWorkerParametersしか受け取ることができない
val workManager = WorkManager.getInstance()
workManager.enqueue(OneTimeWorkRequest.from(LoggerWorker::class.java))
class LoggerWorker (ctx: Context,params: WorkerParameters) : Worker(ctx, params) {
override fun doWork(): Result {
Log.i(this.javaClass.simpleName,"ログが出力されています")
return Result.success()
}
}
解決方法
WorkerFactoryを使う
https://developer.android.com/reference/androidx/work/WorkerFactory
Workerを生成するときに呼び出される、WorkerFactoryを上書きすることで、workerへDIできるようになる
実装方法
例として、Android標準のLogをラップしただけのMyLoggerをLoggerWorkerへDIします。
class MyLogger {
fun log(tag:String,message:String){ |
Log.i(tag,message) |
}
}
class LoggerWorker (
ctx: Context,
params: WorkerParameters,
private val logger: MyLogger
){}
1.WorkerFactoryを継承したMyWorkerFactoryを作成します。
createWorkerで生成したいWorkerのクラス名が渡ってくるので、それに基づいてWorkerのインスタンスを返します。
class MyWorkerFactory : WorkerFactory() {
override fun createWorker(
appContext: Context,
workerClassName: String,
workerParameters: WorkerParameters
): ListenableWorker? {
return when (Class.forName(workerClassName)) {
LoggerWorker::class.java -> LoggerWorker(appContext, workerParameters, MyLogger())
else -> throw IllegalArgumentException("unknown worker class name: $workerClassName")
}
}
}
2.作成したMyWorkerFactoryをアプリケーションクラスを作成しWorkManager.initialize()メソッド経由でWorkerManagerへ登録します。(アプリケーションクラスのマニュフェストへの登録を忘れずに。)
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
WorkManager.initialize(this, Configuration.Builder().setWorkerFactory(MyWorkerFactory()).build())
}
}
3.WorkerManagerはWorkerManager.initialize()で一度しか初期化できず、デフォルトでは自動的に初期化されるため、これを無効化します。
<manifest >
<application>
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
android:exported="false"
tools:node="remove"/>
</application>
</manifest>
4.ここままで準備は完了のため、あとは今まで通り呼び出します。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val workManager = WorkManager.getInstance()
workManager.enqueue(OneTimeWorkRequest.from(LoggerWorker::class.java))
}
}
コード全体
https://github.com/Cespresso/DaggerDIToWorker/tree/dont_use_dagger
WorkerへのDIをDaggerを使う前提で作成したものを流用しているので、masterブランチはdaggerを使ったパターンの物があります。
daggerのViewModelProvider.Factry等でよく使われているマルチバインディング等でいい感じ()になってます。そのうちこちらにまとめるかもしれませんが、参考の一番上の記事を元に実装したので気になる方は、そちらを参考にしてみてください。
参考
https://proandroiddev.com/dagger-2-setup-with-workmanager-a-complete-step-by-step-guild-bb9f474bde37
https://developer.android.com/reference/androidx/work/WorkManager