我无法使任何答案对我生效。问题在于,进度条在上传图像之前就已经到达了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()
val body = CountingRequestBody(builder.build()) { max, value ->
}
到这个阶段,我已经取得了进展,但是每当数据正在上传时,我会看到100%,然后是长时间的等待。关键在于套接字在我的设置中默认配置为缓冲3145728字节的发送数据。嗯,我的图片大小稍小于此并且进度显示了填充套接字发送缓冲区的进度。为了减轻这种情况,需要为OkHttpClient创建一个SocketFactory。
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文件的flavor,以便我的应用程序可以在没有网络的情况下运行,这样我就可以进行测试而不会造成负担。 为了使此代码工作,Dispatcher需要读取body数据。 我创建了这个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)
}
}
val response = child.dispatch(request)
return response
}
}
您可以在MockWebServer中使用以下内容:
var server = MockWebServer()
server.setDispatcher(BodyReadingDispatcher(QueueDispatcher()))
这是我项目中的所有工作代码,我提取出来只是为了举例。如果它不适用于你的情况,我很抱歉。
OkHttp3
示例中有一个配方,它展示了如何显示下载进度。如果您仔细查看,可能会创建一个上传进度监视器。在此处找到:https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/Progress.java - Elvis Chweya