Home > Back-end >  How to schedule different works as a unique work?
How to schedule different works as a unique work?

Time:11-22

So I replaced all of my Jobs to these Work representation.

I have two problem in the current state of refactoring.

I have many worker classes, let's say these are W1,W2,W3...

My problem is, when I schedule a work as onTime and periodic as well. I use uniq works with uniq tags. My problem is, when I queue W1 as a periodic with "W1_TAG" tag and after that I would like to create a onTime from W1 with the same tag, it doesn't starts, because there is an another Work with the same tag.

I would like to achive that, If I start W1 worker, and after that start W2 worker as periodic, and after that I start W2 as onTime, the operation will be the following:

-W1 starts -W2 periodic sees that, W1 runs, so he will wait untill w1 succeeded -w2 onTime sees that, there is an another W2 (doesn't matter that it is periodic), and he will be removed? by WorkManager, so w2 periodic remains and w2 on time wont run

I know there is an enqueUniqWork possibility, but It only append, or keep the pervious worker, and this is not working different tags.

So currently I solved this, by add different tags, but I wondering, can I samehow resolve this?

Work scheduling

        fun schedulePeriodicAsync(context: Context) {
            val constraint =
                Constraints.Builder()
                    .setRequiredNetworkType(NetworkType.CONNECTED).build()

            val data = Data.Builder()
            data.putBoolean(IS_PERIODIC_KEY, true)

            val sendCertificatesWorker =
                PeriodicWorkRequest.Builder(
                    SendCertificatesWorker::class.java, 900000, TimeUnit.MICROSECONDS
                )
                    .setConstraints(constraint)
                    .setInputData(data.build())
                    .setBackoffCriteria(
                        BackoffPolicy.LINEAR,
                        OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
                        TimeUnit.MILLISECONDS
                    ).build()

            WorkManager.getInstance(context).enqueueUniquePeriodicWork(
                TAG,
                ExistingPeriodicWorkPolicy.KEEP, sendCertificatesWorker
            )
        }

        fun scheduleNowAsync(context: Context, workCallback: JobCallback? = null) {
            jobCallback = workCallback

            val constraint =
                Constraints.Builder()
                    .setRequiredNetworkType(NetworkType.CONNECTED).build()

            val data = Data.Builder()
            data.putBoolean(IS_PERIODIC_KEY, false)

            val sendCertificatesWorker =
                OneTimeWorkRequest.Builder(SendCertificatesWorker::class.java)
                    .setConstraints(constraint)
                    .setInputData(data.build())
                    .setBackoffCriteria(
                        BackoffPolicy.LINEAR,
                        OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
                        TimeUnit.MILLISECONDS
                    )
                    .build()

            WorkManager.getInstance(context).enqueueUniqueWork(
                TAG_NOW,
                ExistingWorkPolicy.KEEP, sendCertificatesWorker
            )
        }

Worker class:
class SyncPricesWorker(
    val healthCheckApi: HealthCheckApi,
    val synchronizer: Synchronizer,
    val sharedPreferences: SharedPreferences,
    private val context: Context,
    workerParams: WorkerParameters
) : CoroutineWorker(context, workerParams) {

    override suspend fun doWork(): Result {
        Timber.d("SyncWorker starts")
        var workResult = Result.success()
        
        if (runAttemptCount >= 3) {
            return Result.failure()
        }

        workCallback?.jobStart()

        withContext(Dispatchers.IO){
            Timber.d("SyncWorker middle")
            try {
                healthCheckApi.checkHealth(ApiModule.API_KEY).await()

                synchronizer.sendInvoices()
            } catch (e: Exception) {
                workCallback?.jobEnd()

                workResult = Result.retry()
            }
        }

        if (workResult == Result.success()) {

            workCallback?.jobEnd()

            sharedPreferences.edit().putLong(ApiParameters.KEY_LATEST_PRODUCT_PRICE_SYNC_DATE, Date().time).apply()
        }

        Timber.d("SyncWorker end")
        return workResult
    }

Synchronization class method:
    suspend fun sendInvoices(invoiceHeaderIdToSend: String? = null): SendResult {
        val asd = invoiceRepository.getInvoicesWithStatus(0, invoiceHeaderIdToSend ?: "")
        if (asd.isEmpty()) return SendResult.success(10)

        val asd2 = salesApi.sendInvoices(asd).await()
        return if (asd2.isSuccessful) {
            val invoicesList = arrayListOf<InvoiceHeader>()
            invoicesList.addAll(asd)

            invoicesList.forEach { it.status = Constants.STATUS_SENT.code }

            invoiceRepository.updateInvoiceHeaders(invoicesList)
            SendResult.success(asd.size)
        } else {
            Timber.tag("sendInvoices").e(asd2.errorBody()?.string().toString())
            SendResult.error(asd.size, asd2.errorBody()?.string().toString())
        }
    }

CodePudding user response:

WorkManager's TAG are properties of your WorkRequest, that you set using addTag() in the WorkRequest.Builder:

val sendCertificatesWorker =
        PeriodicWorkRequest.Builder(
           SendCertificatesWorker::class.java, 900000, TimeUnit.MICROSECONDS
        )
                           .setConstraints(constraint)
                           .setInputData(data.build())
                           .setBackoffCriteria(
                               BackoffPolicy.LINEAR,
                               OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
                               TimeUnit.MILLISECONDS
                            )
                           .addTag(TAG) // <- ADDING A TAG
                           .build()

When you enqueue an unique work, you're using a uniqueName to identify that unique work.

WorkManager.getInstance(context).enqueueUniqueWork(
        UNIQUE_NAME_NOW,
        ExistingWorkPolicy.KEEP, sendCertificatesWorker
    )

Said that, WorkManager allows to set chain of workers for OneTimeWorkers, but not PeriodicWorkers.

It seems to me that the best option to achieve what you want is to add some controls at the start of W2 that checks the state of the oneTimeWorkers (using unique TAGs), and apply there the logic you need to achieve your desired outcome.

You need to pay some attention if you want to cover the case where W2 Periodic is already running, and you launch OneTime W1. In this case, or you cancel Periodic W2 from W1 (and handle cancellation in W2) or you may end up with both W1 and W2 running at the same time.

  • Related