0

I'm implementing my first WorkManager for daily sync with the server which could return an HTTP error hence make the createWork() flow to fail and to call onError() so a Result.failure() is "thrown" in the flow's subscription.

I would like to make sure Result.failure() is never called and always call Result.retry().

Here is my RxWorker

class DailySyncWorker (appContext: Context, workerParams: WorkerParameters): RxWorker(appContext, workerParams) {

    private val disposable: CompositeDisposable = CompositeDisposable()
    private val httpManager = AppFactory.httpManager()
    private val dbManager = AppFactory.dbManager()
    private val prefsManager = AppFactory.sharedManager(appContext)

    companion object {
        const val WORKER_NAME = "com.blabla.blah.DAILY_SYNC_WORKER"
    }

    override fun createWork(): Single<Result> {
        Timber.e("Worker - Executing ${System.currentTimeMillis() - prefsManager.lastMillis}")
        prefsManager.lastMillis = System.currentTimeMillis()

        return dbManager.alarmDAO.getNewAlarms(prefsManager.lastSyncedAlarm)
            .flatMap { alarms -> syncWithServer(alarms)
                .doOnSuccess {
                    if(alarms.size != 0)
                        prefsManager.lastSyncedAlarm = alarms[0].date // The query is in descending order, the first record is the last recorded alarm
                }
            }
    }

    private fun syncWithServer(alarms: MutableList<Alarm>): Single<Result> {
        val syncRequestModel = SyncRequestModel(alarms)

        return if(alarms.size != 0)
            httpManager.service().create(SyncService::class.java).sync(syncRequestModel)
                    .observeOn(Schedulers.io())
                    .flatMap { result ->
                        if(result == "OK") { // TODO - the service has not been created yet
                            Timber.e("Worker - success")
                            Single.just(Result.success())
                        } else {
                            Timber.e("Worker - failure")
                            Single.just(Result.retry())
                        }
                    }
        else {
            Timber.e("Worker - no sync is necessary")
            Single.just(Result.success())
        }
    }
}

I tried calling Result.retry() in many different onError() of the flow but it seems that Result.failure() is being called in the onError() of the subscription which I can't override.

So how can I make sure my worker never fails?

Cliff Burton
  • 3,414
  • 20
  • 33
  • 47
  • use onErrorXXX, maybe duplicate of https://stackoverflow.com/questions/28969995/how-to-ignore-error-and-continue-infinite-stream – bubbles Jan 03 '20 at 20:46

1 Answers1

1

In syncWithServer() function, you only check result in flatMap part. However, there might be cases that error might happen before reaching flatMap part which means Result.retry() might never be returned. Also, you do not need to specify specific thread as workers run on background thread by default. Here is your worker with changes I mentioned above:

class DailySyncWorker (appContext: Context, workerParams: WorkerParameters): RxWorker(appContext, workerParams) {

    private val disposable: CompositeDisposable = CompositeDisposable()
    private val httpManager = AppFactory.httpManager()
    private val dbManager = AppFactory.dbManager()
    private val prefsManager = AppFactory.sharedManager(appContext)

    companion object {
        const val WORKER_NAME = "com.blabla.blah.DAILY_SYNC_WORKER"
    }

    override fun createWork(): Single<Result> {
        Timber.e("Worker - Executing ${System.currentTimeMillis() - prefsManager.lastMillis}")
        prefsManager.lastMillis = System.currentTimeMillis()

        return dbManager.alarmDAO.getNewAlarms(prefsManager.lastSyncedAlarm)
            .flatMap { alarms -> syncWithServer(alarms)
                .doOnSuccess {
                    if(alarms.size != 0)
                        prefsManager.lastSyncedAlarm = alarms[0].date // The query is in descending order, the first record is the last recorded alarm
                }
            }
            .onErrorReturnItem(Result.retry()) // Here is error handling
    }

    private fun syncWithServer(alarms: MutableList<Alarm>): Single<Result> {
        val syncRequestModel = SyncRequestModel(alarms)

        return if(alarms.size != 0)
            httpManager.service().create(SyncService::class.java).sync(syncRequestModel)
                    .flatMap { result ->
                        if(result == "OK") { // TODO - the service has not been created yet
                            Timber.e("Worker - success")
                            Single.just(Result.success())
                        } else {
                            Timber.e("Worker - failure")
                            Single.just(Result.retry())
                        }
                    }
        else {
            Timber.e("Worker - no sync is necessary")
            Single.just(Result.success())
        }
    }
}
Natig Babayev
  • 3,128
  • 15
  • 23