okhttp下载文件设置进度监听 |
您所在的位置:网站首页 › 优酷怎么看下载进度 › okhttp下载文件设置进度监听 |
okhttp下载文件设置进度监听
仿照上传写一个responseBody网上广为流传的写法另一种写法okhttp执行监听原理
okhttp下载文件监听与
上传文件监听原理类似,上传通过requestBody的writeTo将报文发出。下载就是通过responseBody的source方法将报文接收。
仿照上传写一个responseBody
public class ProgressResponseBody extends ResponseBody {
...
@Override
public MediaType contentType() {
return responseBody.contentType();
}
@Override
public BufferedSource source() {
if (bufferedSource == null) {
bufferedSource = Okio.buffer(new ProgressBufferedSource(responseBody.source()));
}
return bufferedSource;
}
class ProgressBufferedSource extends ForwardingSource {
ProgressBufferedSource(Source delegate) {
super(delegate);
}
@Override
public long read(Buffer sink, long byteCount) throws IOException {
long readLength = super.read(sink, byteCount);
bytesRead += readLength > 0 ? readLength : 0;
progressListener.onProgress(bytesRead, responseBody.contentLength());
return readLength;
}
}
}
现在就差把这个responseBody放到这次请求当中。 网上广为流传的写法了解okhttp的部分执行原理,它最终执行的是一个操作链Interceptors。链每个节点都会做一部分http请求操作。 通过自定义一个Interceptor,把responseBody插入执行链中。 public class ProgressResponseInterceptor implements Interceptor { private ProgressResponseBody.ProgressListener progressListener; public ProgressResponseInterceptor(ProgressResponseBody.ProgressListener progressListener) { this.progressListener = progressListener; } @Override public Response intercept(Chain chain) throws IOException { Response responseResponse = chain.proceed(chain.request()); return responseResponse.newBuilder() .body(new ProgressResponseBody(responseResponse.headers(), responseResponse.body(), progressListener)) .build(); } } OkHttpClient client = new OkHttpClient().newBuilder() .addInterceptor(new ProgressResponseInterceptor(new ProgressResponseBody.ProgressListener() { @Override public void onProgress(long currentBytes, long contentLength) { callback.progress(currentBytes, contentLength); } })) .build(); 另一种写法经过跟踪执行,我发现了一个现象,于是我尝试换一种写法。 我把Callback方法按照代理的方式封装了一下,然后在onResponse回调中插入responseBody class MyCallback implements Callback { Callback delegete; MyCallback(Callback callback){ delegete = callback; } @Override public void onFailure(Call call, IOException e) { delegete.onFailure(call, e); } @Override public void onResponse(Call call, Response response) throws IOException { delegete.onResponse(call,response.newBuilder() .body(new ProgressResponseBody(response.headers(), response.body(), new ProgressResponseBody.ProgressListener() { @Override public void onProgress(long currentBytes, long contentLength) { callback.progress(currentBytes, contentLength); } })) .build()); } }这么写结果竟然也是对的。 我跟踪执行发现onResponse回调竟然在onProgress之前调用。这时我猜测有两种可能,一种是我监听了个寂寞,其实报文早已传输完成,监听的只不过是把内存中的数据存入外存的过程。这个经过测试感觉时间对不上不太可能。那就是另一种可能,onResponse回调执行时,responseBody还没有开始接收。 okhttp执行监听原理我们从回调onResponse倒着找源码,先找到调用onResponse的地方 @Override protected void execute() { try { Response response = getResponseWithInterceptorChain(); if (retryAndFollowUpInterceptor.isCanceled()) { signalledCallback = true; responseCallback.onFailure(RealCall.this, new IOException("Canceled")); } else { signalledCallback = true; responseCallback.onResponse(RealCall.this, response); } } }response对象是从getResponseWithInterceptorChain中获取的。我们找到链的后一步 public final class CallServerInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { httpStream.writeRequestHeaders(request); if (HttpMethod.permitsRequestBody(requesthod()) && request.body() != null) { Sink requestBodyOut = httpStream.createRequestBody(request, request.body().contentLength()); BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut); request.body().writeTo(bufferedRequestBody); bufferedRequestBody.close(); } httpStream.finishRequest(); Response response = httpStream.readResponseHeaders() .request(request) .handshake(streamAllocation.connection().handshake()) .sentRequestAtMillis(sentRequestMillis) .receivedResponseAtMillis(System.currentTimeMillis()) .build(); if (!forWebSocket || response.code() != 101) { response = response.newBuilder() .body(httpStream.openResponseBody(response)) .build(); } return response; } }发现这里request是直接head、body全部传输到httpstream中,而response只接收了head。并没有接收body 再从response的source()方法入手 我们看BufferSource的实现类RealBufferedSource,在onResponse回调中通过is = response.body().byteStream()获取流的,这个方法获取的就是RealBufferedSource 中inputStream方法返回的流对象,这个对象中实际调用了source的read方法。 final class RealBufferedSource implements BufferedSource { ... @Override public InputStream inputStream() { return new InputStream() { @Override public int read() throws IOException { if (closed) throw new IOException("closed"); if (buffer.size == 0) { long count = source.read(buffer, Segment.SIZE); if (count == -1) return -1; } return buffer.readByte() & 0xff; } @Override public int read(byte[] data, int offset, int byteCount) throws IOException { if (closed) throw new IOException("closed"); checkOffsetAndCount(data.length, offset, byteCount); if (buffer.size == 0) { long count = source.read(buffer, Segment.SIZE); if (count == -1) return -1; } return buffer.read(data, offset, byteCount); } }; } }终于找到调用responseBody中read的位置了。 okhttp在进行网络请求的时候,responseBody是在onResponse后获取的,通过以下方法都可以触发获取responseBody。不调用这些方法在okhttp默认情况下responseBody都不会获取。 response.body().string(); is = response.body().byteStream(); is.read(); response.body().bytes(); |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |