Running background tasks is a very difficult task in Android. Working with AlarmManager, Firebase JobDispatcher or Job Dispatcher does not guarantee execution of tasks in Android. Is this a big deal?
WorkManager has solved this problem for us.
What is WorkManager?
WorkManager is part of the Android Jetpack library, which makes it easy to schedule asynchronous, asynchronous tasks that are expected to run even when the application exits or restarts the device, i.e. even Your application restarts due to any Job Manager task that ensures a scheduled task will execute again. Is that cool?
In this blog, we’ll talk about how WorkManager is integrated into your project and much more advanced features and customization make it easy for your life to schedule tasks.
So let’s get started.
To integrate job manager in your project,
1 2 3 4 5 | dependencies <span class="token punctuation">{</span> def work_version <span class="token operator">=</span> <span class="token string">"2.2.0"</span> implementation <span class="token string">"androidx.work:work-runtime: <span class="token interpolation variable">$work_version</span> "</span> <span class="token punctuation">}</span> |
Now, as a next step, we will create a Worker class. The Worker class is responsible for performing work synchronously on a background thread provided by the job manager.
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">class</span> <span class="token function">YourWorkerClass</span> <span class="token punctuation">(</span> appContext <span class="token operator">:</span> Context <span class="token punctuation">,</span> workerParams <span class="token operator">:</span> WorkerParameters <span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token function">Worker</span> <span class="token punctuation">(</span> appContext <span class="token punctuation">,</span> workerParams <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">doWork</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">:</span> Result <span class="token punctuation">{</span> <span class="token comment">// Your work here.</span> <span class="token comment">// Your task result</span> <span class="token keyword">return</span> Result <span class="token punctuation">.</span> <span class="token function">success</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
In the upper class,
- The doWork () method is responsible for executing your task in the background thread. Any task you want to perform must be written here.
- The result returns the status of the job performed in the doWork () method. If it returns result.success (), the action is successful if the status is result.failure (), the task fails and finally, if it returns result.retry (), it means The task will reappear after some time.
Now, let’s configure how to perform the task
We need to create a WorkRequest to determine how and when the job should be run. It has two types,
- OneTimeWorkRequest – Run the task only once
- PeriodicWorkTimeRequest – Run the task after a certain amount of time.
1 2 | <span class="token keyword">val</span> yourWorkRequest <span class="token operator">=</span> OneTimeWorkRequestBuilder <span class="token operator"><</span> YourWorkerClass <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">build</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> |
and once we have determined our work requirements, we can schedule the task using
1 2 | WorkManager <span class="token punctuation">.</span> <span class="token function">getInstance</span> <span class="token punctuation">(</span> context <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">enqueue</span> <span class="token punctuation">(</span> yourWorkRequest <span class="token punctuation">)</span> |
That’s it, this is how you can schedule your task using Work Manager. Is this simple?
Now, let’s talk about the customizations we can do during the task execution process.
We can add specific constraints in WorkRequest to customize it. To add constraints we use
1 2 3 4 5 6 7 8 | <span class="token keyword">val</span> myConstraints <span class="token operator">=</span> Constraints <span class="token punctuation">.</span> <span class="token function">Builder</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setRequiresDeviceIdle</span> <span class="token punctuation">(</span> <span class="token boolean">true</span> <span class="token punctuation">)</span> <span class="token comment">//checks whether device should be idle for the WorkRequest to run</span> <span class="token punctuation">.</span> <span class="token function">setRequiresCharging</span> <span class="token punctuation">(</span> <span class="token boolean">true</span> <span class="token punctuation">)</span> <span class="token comment">//checks whether device should be charging for the WorkRequest to run</span> <span class="token punctuation">.</span> <span class="token function">setRequiredNetworkType</span> <span class="token punctuation">(</span> NetworkType <span class="token punctuation">.</span> CONNECTED <span class="token punctuation">)</span> <span class="token comment">//checks whether device should have Network Connection</span> <span class="token punctuation">.</span> <span class="token function">setRequiresBatteryNotLow</span> <span class="token punctuation">(</span> <span class="token boolean">true</span> <span class="token punctuation">)</span> <span class="token comment">// checks whether device battery should have a specific level to run the work request</span> <span class="token punctuation">.</span> <span class="token function">setRequiresStorageNotLow</span> <span class="token punctuation">(</span> <span class="token boolean">true</span> <span class="token punctuation">)</span> <span class="token comment">// checks whether device storage should have a specific level to run the work request</span> <span class="token punctuation">.</span> <span class="token function">build</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> |
And to add it in our work request we use
1 2 3 4 | <span class="token keyword">val</span> yourWorkRequest <span class="token operator">=</span> OneTimeWorkRequestBuilder <span class="token operator"><</span> YourWorkerClass <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setConstraints</span> <span class="token punctuation">(</span> myConstraints <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">build</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> |
Here, we set the above defined constraints to the previously defined workRequest. Now, this job request will only run if all the constraints are met.
We may also set Recurring Quests to run after a certain amount of time. To run a recurring workRequest, we use,
1 2 3 4 5 | <span class="token keyword">val</span> yourPeriodicWorkRequest <span class="token operator">=</span> PeriodicWorkRequestBuilder <span class="token operator"><</span> YourPeriodicWorkerClass <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token number">1</span> <span class="token punctuation">,</span> TimeUnit <span class="token punctuation">.</span> HOURS <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setConstraints</span> <span class="token punctuation">(</span> myConstraints <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">build</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> |
This will run every 1 hour periodically as we have set the interval to 1 hour.
The minimum time to run a recurring task is 15 minutes.
If you do not want the task to run immediately, you can specify your work to start after a minimum initial delay by using:
1 2 3 4 | <span class="token keyword">val</span> yourWorkRequest <span class="token operator">=</span> OneTimeWorkRequestBuilder <span class="token operator"><</span> YourWorkerClass <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setInitialDelay</span> <span class="token punctuation">(</span> <span class="token number">10</span> <span class="token punctuation">,</span> TimeUnit <span class="token punctuation">.</span> MINUTES <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">build</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> |
This will run after an initial delay of 10 minutes.
Now let’s check out some of the great features we have in Job Manager
Check the status of your WorkManager
If we want to perform some task when WorkManager successfully executes a task such as displaying a Toast or something else, we need to check the status of the task. To check the status we use,
1 2 3 4 5 6 7 | WorkManager <span class="token punctuation">.</span> <span class="token function">getInstance</span> <span class="token punctuation">(</span> context <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">getWorkInfoByIdLiveData</span> <span class="token punctuation">(</span> yourWorkRequest <span class="token punctuation">.</span> id <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">observe</span> <span class="token punctuation">(</span> lifecycleOwner <span class="token punctuation">,</span> Observer <span class="token punctuation">{</span> workInfo <span class="token operator">-></span> <span class="token keyword">if</span> <span class="token punctuation">(</span> workInfo <span class="token operator">!=</span> <span class="token keyword">null</span> <span class="token operator">&&</span> workInfo <span class="token punctuation">.</span> state <span class="token operator">==</span> WorkInfo <span class="token punctuation">.</span> State <span class="token punctuation">.</span> SUCCEEDED <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">//Toast</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> |
Here, we observe the status of LiveData to update the user interface
Stringing of tasks
We can chain multiple tasks in two ways.
- Chaining in Series
- Parallel Chaining
Talk about them one by one.
Chaining in Series
Suppose we have two workRequest,
1 2 3 | <span class="token keyword">val</span> yourWorkRequestOne <span class="token operator">=</span> <span class="token operator">..</span> <span class="token punctuation">.</span> <span class="token keyword">val</span> yourWorkRequestTwo <span class="token operator">=</span> <span class="token operator">..</span> <span class="token punctuation">.</span> |
and we need to string them into strings. To do this, we will use
1 2 3 4 | WorkManager <span class="token punctuation">.</span> <span class="token function">getInstance</span> <span class="token punctuation">(</span> context <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">beginWith</span> <span class="token punctuation">(</span> yourWorkRequestOne <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">then</span> <span class="token punctuation">(</span> yourWorkRequestTwo <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">enqueue</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> |
Here, first, the implementation will start with your WorkRequest WorkRequest and then it will execute the second one. This is called a sequence of tasks.
Parallel Chaining
In this case, we can chain parallel tasks using the following
1 2 3 4 5 6 | WorkManager <span class="token punctuation">.</span> <span class="token function">getInstance</span> <span class="token punctuation">(</span> myContext <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">beginWith</span> <span class="token punctuation">(</span> <span class="token function">listOf</span> <span class="token punctuation">(</span> work1 <span class="token punctuation">,</span> work2 <span class="token punctuation">,</span> work3 <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">then</span> <span class="token punctuation">(</span> work4 <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">then</span> <span class="token punctuation">(</span> work5 <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">enqueue</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> |
Here, work1, work2 and work3 will execute in parallel and then when all of them are executed, only work4 will execute and work5 sequentially.
Note: If the first task fails, the next task executes a failure response and if the first task is canceled, all of the following tasks will also be canceled.
To cancel the task we use,
1 2 | WorkManager <span class="token punctuation">.</span> <span class="token function">cancelWorkById</span> <span class="token punctuation">(</span> workRequest <span class="token punctuation">.</span> id <span class="token punctuation">)</span> |
Input / output for your task
While running a job / task, we may need some input for the employee to run. For example, fetching the current user details will require the user id of the user. To overcome that as the input we use,
1 2 3 4 5 6 | <span class="token keyword">val</span> userId <span class="token operator">=</span> <span class="token function">workDataOf</span> <span class="token punctuation">(</span> Constants <span class="token punctuation">.</span> USER_ID <span class="token keyword">to</span> String <span class="token punctuation">)</span> <span class="token keyword">val</span> yourWorkRequest <span class="token operator">=</span> OneTimeWorkRequestBuilder <span class="token operator"><</span> YourWorkerClass <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setInputData</span> <span class="token punctuation">(</span> userId <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">build</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> |
and to get input into the worker class we have,
1 2 3 4 5 6 7 8 9 10 11 12 13 | <span class="token keyword">class</span> <span class="token function">YourWorkerClass</span> <span class="token punctuation">(</span> appContext <span class="token operator">:</span> Context <span class="token punctuation">,</span> workerParams <span class="token operator">:</span> WorkerParameters <span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token function">Worker</span> <span class="token punctuation">(</span> appContext <span class="token punctuation">,</span> workerParams <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">doWork</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">:</span> Result <span class="token punctuation">{</span> <span class="token keyword">val</span> userId <span class="token operator">=</span> <span class="token function">getInputData</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">getString</span> <span class="token punctuation">(</span> Constants <span class="token punctuation">.</span> USER_ID <span class="token punctuation">)</span> <span class="token keyword">val</span> userDetail <span class="token operator">=</span> <span class="token function">getDetail</span> <span class="token punctuation">(</span> userId <span class="token punctuation">)</span> <span class="token comment">// Create the output of the work</span> <span class="token keyword">val</span> firstName <span class="token operator">=</span> <span class="token function">workDataOf</span> <span class="token punctuation">(</span> Constants <span class="token punctuation">.</span> DETAIL <span class="token keyword">to</span> userDetail <span class="token punctuation">.</span> firstName <span class="token punctuation">)</span> <span class="token comment">// Return the output</span> <span class="token keyword">return</span> Result <span class="token punctuation">.</span> <span class="token function">success</span> <span class="token punctuation">(</span> firstName <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Here, we get the input using getInputData (). GetString () and we pass it to getDetail () as a parameter to get the user details.
Now, to convert the User’s name as an output, we pass the output in Result.Success (/ ** Your Output ** /).
This is how we can integrate the job manager in our Android project.
Wish you happy learning