Я не смог заставить ни один из ответов работать на меня. Проблема заключалась в том, что прогресс достигал 100% до того, как изображение было загружено, намекая на то, что какой-то буфер заполнялся до отправки данных по сети. После некоторых исследований я обнаружил, что это действительно так, и этот буфер был буфером отправки Socket. Предоставление SocketFactory для OkHttpClient, наконец, сработало. Мой код Kotlin выглядит следующим образом...
Во-первых, как и у других, у меня есть CountingRequestBody, который используется для упаковки MultipartBody.
class CountingRequestBody(var delegate: RequestBody, private var listener: (max: Long, value: Long) -> Unit): RequestBody() {
override fun contentType(): MediaType? {
return delegate.contentType()
}
override fun contentLength(): Long {
try {
return delegate.contentLength()
} catch (e: IOException) {
e.printStackTrace()
}
return -1
}
override fun writeTo(sink: BufferedSink) {
val countingSink = CountingSink(sink)
val bufferedSink = Okio.buffer(countingSink)
delegate.writeTo(bufferedSink)
bufferedSink.flush()
}
inner class CountingSink(delegate: Sink): ForwardingSink(delegate) {
private var bytesWritten: Long = 0
override fun write(source: Buffer, byteCount: Long) {
super.write(source, byteCount)
bytesWritten += byteCount
listener(contentLength(), bytesWritten)
}
}
}
Я использую это в Retrofit2. Общее использование будет примерно таким:
val builder = MultipartBody.Builder()
// Add stuff to the MultipartBody via the Builder
val body = CountingRequestBody(builder.build()) { max, value ->
// Progress your progress, or send it somewhere else.
}
В этот момент у меня был прогресс, но я видел 100%, а затем долго ждал, пока загружались данные. Ключевым моментом было то, что сокет по умолчанию в моей настройке был настроен на буферизацию 3145728 байт отправляемых данных. Ну, мои изображения были как раз под этим, и прогресс показывал ход заполнения этого буфера отправки сокета. Чтобы смягчить это, создайте SocketFactory для OkHttpClient.
class ProgressFriendlySocketFactory(private val sendBufferSize: Int = DEFAULT_BUFFER_SIZE) : SocketFactory() {
override fun createSocket(): Socket {
return setSendBufferSize(Socket())
}
override fun createSocket(host: String, port: Int): Socket {
return setSendBufferSize(Socket(host, port))
}
override fun createSocket(host: String, port: Int, localHost: InetAddress, localPort: Int): Socket {
return setSendBufferSize(Socket(host, port, localHost, localPort))
}
override fun createSocket(host: InetAddress, port: Int): Socket {
return setSendBufferSize(Socket(host, port))
}
override fun createSocket(address: InetAddress, port: Int, localAddress: InetAddress, localPort: Int): Socket {
return setSendBufferSize(Socket(address, port, localAddress, localPort))
}
private fun setSendBufferSize(socket: Socket): Socket {
socket.sendBufferSize = sendBufferSize
return socket
}
companion object {
const val DEFAULT_BUFFER_SIZE = 2048
}
}
И во время настройки установите его.
val clientBuilder = OkHttpClient.Builder()
.socketFactory(ProgressFriendlySocketFactory())
Как уже упоминалось, регистрация тела запроса может повлиять на это и привести к тому, что данные будут считываться более одного раза. Либо не регистрируйте тело, либо я отключаю его для CountingRequestBody. Для этого я написал свой собственный HttpLoggingInterceptor, и он решает эту и другие проблемы (например, ведение журнала MultipartBody). Но это выходит за рамки этого вопроса.
if(requestBody is CountingRequestBody) {
// don't log the body in production
}
Другие проблемы были с MockWebServer. У меня есть вариант, который использует файлы MockWebServer и json, поэтому мое приложение может работать без сети, поэтому я могу тестировать без этой нагрузки. Чтобы этот код работал, Dispatcher должен прочитать данные тела. Я создал этот Диспетчер именно для этого. Затем он перенаправляет отправку другому Dispatcher, такому как QueueDispatcher по умолчанию.
class BodyReadingDispatcher(val child: Dispatcher): Dispatcher() {
override fun dispatch(request: RecordedRequest?): MockResponse {
val body = request?.body
if(body != null) {
val sink = ByteArray(1024)
while(body.read(sink) >= 0) {
Thread.sleep(50) // change this time to work for you
}
}
val response = child.dispatch(request)
return response
}
}
Вы можете использовать это в MockWebServer как:
var server = MockWebServer()
server.setDispatcher(BodyReadingDispatcher(QueueDispatcher()))
Это весь рабочий код в моем проекте. Я вытащил его из иллюстративных целей. Если у вас не работает из коробки, прошу прощения.
person
ptoinson
schedule
29.04.2020
OkHttp3
образцах есть рецепт. Он показывает, как показать прогресс загрузки. Если вы просмотрите его, вы сможете создать монитор прогресса загрузки. Найдите его здесь https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/Progress.java - person Elvis Chweya   schedule 20.02.2016