WorkManger doesn't trigger after manually stopped- Kotlin

Viewed 28

I want to use workMager to do some work every 15min,at the same time I want to stop workManger when I clicked on the button "StopThread" below is my Code:

    val workManager = WorkManager.getInstance(applicationContext)

    val workRequest = PeriodicWorkRequest.Builder(
        RandomNumberGeneratorWorker::class.java,
        15,
        TimeUnit.MINUTES
    ).addTag("API_Worker")
     .build()


    binding.buttonThreadStarter.setOnClickListener {
        workManager.enqueue(workRequest)
    }

    binding.buttonStopthread.setOnClickListener {
        workManager.cancelAllWorkByTag("API_Worker")
    }

And this is the RandomNumberGeneratorWorker

class RandomNumberGeneratorWorker(
    context: Context,
    params: WorkerParameters
) :
    Worker(context, params) {

    private val MIN = 0
    private val MAX = 100
    private var mRandomNumber = 0

    override fun doWork(): Result {
        Log.d("worker_info","Job Started")
        startRandomNumberGenerator();
        return Result.success();
    }

    override fun onStopped() {
        super.onStopped()
        Log.i("worker_info", "Worker has been cancelled")
    }

    private fun startRandomNumberGenerator() {
        Log.d("worker_info","startRandomNumberGenerator triggered")
        var i = 0
        while (i < 100 && !isStopped) {
            try {
                Thread.sleep(1000)

                    mRandomNumber = (Math.random() * (MAX - MIN + 1)).toInt() + MIN
                    Log.i(
                        "worker_info",
                        "Thread id: " + Thread.currentThread().id + ", Random Number: " + mRandomNumber
                    )
                    i++

            } catch (e: InterruptedException) {
                Log.i("worker_info", "Thread Interrupted")
            }
        }
    }
}

The issue that I'm facing is when I stopped the workManger it didn't work again when I clicked on buttonThreadStarter enter image description here

I did a little research and I found that I can start-stop-start..etc workManger with the code below :

   val workRequest =  OneTimeWorkRequest.from(RandomNumberGeneratorWorker::class.java)

    binding.buttonThreadStarter.setOnClickListener {
       
         workManager.beginUniqueWork("WorkerName",ExistingWorkPolicy.REPLACE,workRequest)
    }

    binding.buttonStopthread.setOnClickListener {
        workManager.cancelAllWork()
    }

but as you can see it's working when I used OneTimeWorkRequest and with that, I can't repeat the work every 15 mins , Any suggestion in how to resolve this issue

1 Answers

WorkManager is not designed for periodic works with exact timing. In reality, the works "are not even periodic".

As you can see here from the logs:

https://developer.android.com/topic/libraries/architecture/workmanager/how-to/debugging#use-alb-shell0dumpsys-jobscheduler

WorkManager delegates to the JobScheduler. JS jobs work in a way that you have a number of explicit(you set them) and implicit(set by the system) constraints and after all of them are satisfied - the job starts.

When you have a period there is an extra constraint - TIMING_DELAY. So if your 15min pass - this doesn't mean in no way that the job will be executed. There might be, and be sure that there will be other constraints. That is the case because WM is designed for resource optimization and it will ensure that the work will finish at some point, even on device restart. But it is not designed to be exact. It is quite the opposite.

And after all the constraints are satisfied - it might take a day, the job is no longer needed and a new job is created with again your 15min constraint - TIMING_DELAY. And the process starts again.

Also - when you say "doesn't trigger" - please, check why. Try to check the debug output from the JS and see if there is work at all. If there is - check what constraints are not satisfied.

But long story short - "every 15min" is not something for WorkManager. Normally you should use AlaramManager for exact timing, but with such a short interval you should try to consider using a Service.

Also, it is dangerous to call: cancelAllWork(). You might break the code of some library in your app. You should better use tags and cancel by tag.

Related