photo by Paul Downey

Show download progress in a RecyclerView with Ktor-Client and Flow asynchronous data stream

Francesco Gatto
Kt. Academy
Published in
3 min readNov 25, 2019

--

In the first story about download files with Ktor and Koin (I really suggest to read it before starting to read this story!), we learned how to download some files from a recycler view, but I’m pretty sure that many of you have wonderedHey, but how about to show a determinate progress bar?”. In fact, for a better user experience, is important for the user to know how much time has to wait for a file to be downloaded. So in this story, we will see how Coroutines comes in handy again to save our developers life!

The first thing to think about is: “How Can I get the progress of a downloading file with Ktor-client?”. Simply like that:

We aren’t doing any magic, just set our responsein a variable, create a data ByteArray where to save each chunk of the file and read it. In the while loop, we have the progress for each read chunk.
Now that we know how to get the download progress, we should think about how to send it to the caller… and it is the perfect scenario for a Sealed Class!

Sealed classes are used for representing restricted class hierarchies, when a value can have one of the types from a limited set, but cannot have any other type

The caller method needs to know the Progress, the Success and the Failure of the current download. In particular, Failure can transport a message to show directly to the user.

The sealed class is not enough to send real-time progress to the list adapter and, for our purpose, the Kotlin Flow is perfect!

Flow is a cold asynchronous data stream that sequentially emits values and completes normally or with an exception.

We can see it as a Collector of Emits, where the collector receives every signal (emit) and do some operation with each one. This is a very simple explanation, but you can find a very nice depth study in Simple Design of Kotlin Flow!
Now we can modify the downloadFile method to Emit DownloadResult

… and Collect the results in the MainActivity:

What to do in different cases?

  • Success: set the download status to false
  • Error: show the message and set the error state
  • Progress: show the progress and update the progress bar

Don’t forget to do all these actions on the main thread using withContext(Dispatchers.Main)

Let’s see now how our adapter changed:

From the code above you can see the use of the onBindViewHolder with payload to choose exactly what updates for each view in the adapter and this avoids annoying flickering on each item for every setProgress

So this is the final result:

You can find the example project here. Happy coding!

Click the 👏 to say “thanks!” and help others find this article.

To be up-to-date with great news on Kt. Academy, subscribe to the newsletter, observe Twitter and follow us on medium.

If you need a Kotlin workshop, check how we can help you: kt.academy.

--

--