3

I'm uploading files with retrofit. I want to listen to the retrofit progress in Kotlin and show it with the progress bar on ui. When I did research on this, I found a good example of this in java using PublishSubject on this page. Is it possible to show progress bar when upload image via Retrofit 2? I want to adapt this to my own code, but I can't decide whether I should use Flow or Channel instead of PublishSubject in Kotlin.

My requestbody class with broadcastChannel:

class ProgressRequestBody : RequestBody {
    val mFile: File
    val ignoreFirstNumberOfWriteToCalls : Int

    constructor(mFile: File) : super(){
        this.mFile = mFile
        ignoreFirstNumberOfWriteToCalls = 0
    }

    constructor(mFile: File, ignoreFirstNumberOfWriteToCalls : Int) : super(){
        this.mFile = mFile
        this.ignoreFirstNumberOfWriteToCalls = ignoreFirstNumberOfWriteToCalls
    }
    var numWriteToCalls = 0

    val getProgress = BroadcastChannel<Float>(1)


    override fun contentType(): MediaType? {
        return "image/*".toMediaTypeOrNull()
    }

    @Throws(IOException::class)
    override fun contentLength(): Long {
        return mFile.length()
    }

    @Throws(IOException::class)
    override fun writeTo(sink: BufferedSink) {
        numWriteToCalls++

        val fileLength = mFile.length()
        val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
        val `in` = FileInputStream(mFile)
        var uploaded: Long = 0

        try {
            var read: Int
            var lastProgressPercentUpdate = 0.0f
            read = `in`.read(buffer)
            while (read != -1) {

                uploaded += read.toLong()
                sink.write(buffer, 0, read)
                read = `in`.read(buffer)

                // when using HttpLoggingInterceptor it calls writeTo and passes data into a local buffer just for logging purposes.
                // the second call to write to is the progress we actually want to track
                if (numWriteToCalls > ignoreFirstNumberOfWriteToCalls ) {
                    val progress = (uploaded.toFloat() / fileLength.toFloat()) * 100f
                    //prevent publishing too many updates, which slows upload, by checking if the upload has progressed by at least 1 percent
                    if (progress - lastProgressPercentUpdate > 1 || progress == 100f) {
                        // publish progress
                        getProgress.send(progress)
                        lastProgressPercentUpdate = progress
                    }
                }
            }
        } finally {
            `in`.close()
        }
    }


    companion object {

        private val DEFAULT_BUFFER_SIZE = 2048
    }
}

The problem is that broadcastChannel.send(progress) wants the function to suspend but the RequestBody writeTo method should not suspend. In this case, I am confused. Should I use Flow or BroadCastChannel? Can you help me?

tametahh
  • 85
  • 6
  • 1
    If you're on JVM or Android, you should be able to use `sendBlocking()` instead of `send()`, which does not suspend (but blocks the current thread). Note that you always have the option to use `runBlocking` in situations where you are bridging blocking code with suspending code (again, only on the JVM) – Joffrey Mar 21 '21 at 19:51
  • [StateFlow](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-state-flow/) – IR42 Mar 21 '21 at 20:05

2 Answers2

3

You should use MutableStateFlow:

val getProgress = MutableStateFlow<Float>(0f) // Initial value is 0

Then:

getProgress.value = progress

See more: StateFlow and SharedFlow

1
val getProgress = MutableSharedFlow<Float>(extraBufferCapacity = 1)

getProgress.tryEmit(progress)