Notes: 09. Implement WorkManager in Complex Apps
The student materials have been reviewed and are updated as of SEPTEMBER 2022.
Now that you’ve set up the projects and everything you need to work through this course, you can implement the work manager! :] Let’s move to the project!
The WorkManager dependency is already within the build.gradle file, so you don’t have to manually add it. Now it’s time to create your first worker, just like you had it in the previous part of the course.
Create a package called worker, and add a new class called DownloadImageWorker.kt. Then add the next snippet of code to the class:
class DownloadImageWorker(context: Context, workerParameters: WorkerParameters) :
Worker(context, workerParameters) {
override fun doWork(): Result {
}
}
Your class will extend the worker class, and override doWork(), like before. Then add the code to create a file, to which you’ll save the image:
class DownloadImageWorker(context: Context, workerParameters: WorkerParameters) :
Worker(context, workerParameters) {
override fun doWork(): Result {
val imageDownloadPath = inputData.getString("image_path") ?: return Result.failure()
val imageUrl = URL(imageDownloadPath)
val connection = imageUrl.openConnection() as HttpURLConnection
connection.doInput = true
connection.connect()
val imagePath = "${System.currentTimeMillis()}.jpg"
val inputStream = connection.inputStream
val file = File(applicationContext.externalMediaDirs.first(), imagePath)
}
}
By using an HTTP connection, you’ll create an input stream and download the image to a local file. To finish the worker code, you need to add the output code, to store the bytes into the file:
class DownloadImageWorker(context: Context, workerParameters: WorkerParameters) :
Worker(context, workerParameters) {
override fun doWork(): Result {
val imageDownloadPath = inputData.getString("image_path") ?: return Result.failure()
val imageUrl = URL(imageDownloadPath)
val connection = imageUrl.openConnection() as HttpURLConnection
connection.doInput = true
connection.connect()
val imagePath = "${System.currentTimeMillis()}.jpg"
val inputStream = connection.inputStream
val file = File(applicationContext.externalMediaDirs.first(), imagePath)
val outputStream = FileOutputStream(file)
outputStream.use { output ->
val buffer = ByteArray(4 * 1024)
var byteCount = inputStream.read(buffer)
while (byteCount > 0) {
output.write(buffer, 0, byteCount)
byteCount = inputStream.read(buffer)
}
output.flush()
}
val output = workDataOf("image_path" to file.absolutePath)
return Result.success(output)
}
}
In the end, you’re returning the image path as the output data, to be able to load the image. With that complete, head over to the ImagesFragment, and add the following code to the onImageDownload function:
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.NOT_ROAMING)
.setRequiresBatteryNotLow(true)
.setRequiresStorageNotLow(true)
.build()
val downloadImageWorker = OneTimeWorkRequestBuilder<DownloadImageWorker>()
.setInputData(workDataOf("image_path" to imageUrl))
.setConstraints(constraints)
.build()
You begin by creating the necessary constraints - which are enough storage & battery, and a network which isn’t roaming. That way you’re making sure the user is not wasting resources or data unless they can afford it!
Then you build a worker, and add the imageUrl as its input data. Once you’ve built the worker, you need to enqueue it and observe its info:
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.NOT_ROAMING)
.setRequiresBatteryNotLow(true)
.setRequiresStorageNotLow(true)
.build()
val downloadImageWorker = OneTimeWorkRequestBuilder<DownloadImageWorker>()
.setInputData(workDataOf("image_path" to imageUrl))
.setConstraints(constraints)
.build()
val workManager = WorkManager.getInstance(requireActivity())
workManager.enqueue(downloadImageWorker)
workManager.getWorkInfoByIdLiveData(downloadImageWorker.id)
.observe(this, Observer { workInfo ->
if (workInfo?.state?.isFinished == true) {
activity?.toast("Image downloaded!")
}
})
If the image was successfully downloaded, you will show a toast! Once you finish the upload worker, you’ll be able to download images you’ve uploaded to the backend, but for now, add a return statement after the hardcoded image in getAllImages(). Now run the app, and try to long click the image, to promt a dialog to download it!
Once you’re done, you’ll see the toast saying the download has been successful! Then open the device file explorer, and check if the image really is in the app folder. If it is, everything works fine! :]
Next, you’ll proceed to build a worker, to upload an image! See you in the next episode.