Search code examples
androidandroid-workmanager

How do I limit a number of workers running in parallel in WorkManager?


I enqueue files (chosen by the user) to upload and then update sync status with WorkManager like this:

fun schedule(files: List<String>) {
    var cont = workManager
                .beginUniqueWork(issueId, APPEND, files.map { workRequest(it) })
                .then(updateSyncStatusWork)
                .enqueue()
}

It works well. But when user choose a lot of files looking to logs I see that a lot of files are uploading at the same time (around 10 or even all of them). And quite a lot of timeouts happens. I believe that reducing a number of parallel uploads will decrease the number of timeouts but I can not find any API in WorkManager or WorkRequest that allows doing that.

PS I do not consider chaining them as failed upload will drop upload of all the files after it.


Solution

  • The number of jobs that can run at the same time are actually determined by a thread pool that you configure. The default Executor is defined here.

    Typically when you are using the Worker base class, you are associating an instance of the Worker to a thread on this Executor. If you want greater control on which thread your Worker is associated with, you might want to take a look at CoroutineWorker or ListenableWorker.

    The number of threads in the default Executor are determined by the number of cores on the device. If you want only N jobs to run at the same time you have to do the following:

    • Disable the default WorkManager initializer (by disabling manifest merging for the content provider).

    • Initialize WorkManager on Application.onCreate() or your own ContentProvider. You need to do this here because the OS can ask previously scheduled Workers to run. For more information look at this.

    val configuration = Configuration.Builder()
        // Defines a thread pool with 2 threads. 
        // This can be N (typically based on the number of cores on the device) 
        .setExecutor(Executors.newFixedThreadPool(2))
        .build()
    
    WorkManager.initialize(context, configuration)
    

    In the above example I am creating a fixed size thread pool with 2 threads (which in turn can handle 2 Workers simultaneously). Now when you enqueue your Workers you will see only 2 of them execute at the same time.