Search code examples
androidandroid-workmanager

Periodic Custom WorkManager not Triggering doWork()


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

Worker Class

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
                )
        }
    }
}

Helper Class

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)
    }
}

OnStart

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()
    }
}

Application Class and

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()
    }
}

Manifest

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.


Solution

  • 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)
    
        }