I am implementing a custom WorkManager (with AppStartup & Hilt) for a periodic task but after a lot of tinkering, researching and waiting the task is not firing off.
Below is my set-up
Overrides doWork()
returning Result.success()
or Result.error()
. I have a Companion work with a static schedule()
to set up Constraints, Request and call WorkManager.getInstance(context)
class ScoresWorker @AssistedInject constructor(
@Assisted private val context: Context,
@Assisted params: WorkerParameters,
private val db: ScoresDatabase,
private val api: ScoresAPI
) : CoroutineWorker(context, params) {
private val dao = db.dao
override suspend fun doWork(): Result {
Timber.i("DoWork Called")
return try {
val response = api.getFixturesByDate()
dao.clearFixtures()
dao.insertFixtures(response.response.map { it.toEntity() })
Result.success()
} catch (e: Exception) {
Timber.i("WorKError - Exception")
Result.failure(workDataOf(SCORES_WORKER_ERROR_KEY to e.localizedMessage))
}
}
companion object {
private const val SCORES_WORKER_ID = "scores_worker"
fun schedule(context: Context) {
Timber.i("companion Obj schedule() called")
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
val request = PeriodicWorkRequestBuilder<ScoresWorker>(
15, TimeUnit.MINUTES, 5, TimeUnit.MINUTES
).setConstraints(constraints)
.build()
WorkManager.getInstance(context)
.enqueueUniquePeriodicWork(
SCORES_WORKER_ID, ExistingPeriodicWorkPolicy.KEEP, request
)
}
}
}
Singleton helper class to call the above ScoresWorker.schedule(context)
and ensure WorkManager is only initialized once and there is only one instance.
@Singleton
class WorkManagerInitializer @Inject constructor(
@ApplicationContext private val context: Context, ) {
private var isInitialized: Boolean = false
fun initializeWorkManager() {
Timber.i("initialization called - value: $isInitialized")
if (isInitialized) return
isInitialized = true
ScoresWorker.schedule(context)
}
}
To improve app startup performance I remove heavy stuff like scheduling WorkManager from the app startup sequence moving it instead to onStart().
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
@Inject
lateinit var initializationHelper: WorkManagerInitializer
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent { .... }
}
override fun onStart() {
super.onStart()
initializationHelper.initializeWorkManager()
}
}
The Application class implement the Configuration.Provider
interface
@HiltAndroidApp
class InstantScoreApp : Application(), Configuration.Provider {
override fun onCreate() { ... }
@Inject
lateinit var workerFactory: HiltWorkerFactory
override fun getWorkManagerConfiguration(): Configuration {
return Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()
}
}
First tried this approach as described on the Docs
...
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove"
>
</provider>-->
</application>
This too did not work.
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="androidx.work.WorkManagerInitializer"
android:value="androidx.startup"
tools:node="remove" />
</provider>
I am on work-runtime-ktx v 2.8.0
and startup-runtime v1.1
.
I need help on what I could possibly be missing for this set-up to work. Pls Help.
Here is my working code, at manifest file :
android:name=".MainApplication"
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove">
</provider>
at app build gradle file :
implementation "androidx.work:work-runtime-ktx:2.7.0"
implementation("androidx.hilt:hilt-work:1.0.0")
kapt("androidx.hilt:hilt-compiler:1.0.0")
kapt "com.google.dagger:hilt-android-compiler:2.39.1"
At MainApplication class :
@Inject
lateinit var workerFactory: HiltWorkerFactory
override fun onCreate() {
super.onCreate()
context = applicationContext
WorkManager.initialize(this, workManagerConfiguration)
}
override fun getWorkManagerConfiguration() =
Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()
companion object {
lateinit var context: Context
}
}
My periodic worker class :
@HiltWorker
class PeriodicWorker @AssistedInject constructor(
@Assisted appContext: Context, @Assisted workerParams: WorkerParameters,
val repository: Repository
) : Worker(appContext, workerParams){
override fun doWork(): Result {
try {
val applicationContext = applicationContext
// do nceessary work
return Result.success()
} catch (e: Exception) {
Log.e("UploadWorker", e.printStackTrace().toString())
return Result.failure()
}
}
}
initialize periodic worker in activity class :
private lateinit var periodicWorkRequest: WorkRequest
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
periodicWorkRequest = PeriodicWorkRequestBuilder<PeriodicWorker(15, TimeUnit.MINUTES)
.build()
WorkManager.getInstance(applicationContext).enqueue(periodicWorkRequest)
}