okhttp下载文件设置进度监听

您所在的位置:网站首页 优酷怎么看下载进度 okhttp下载文件设置进度监听

okhttp下载文件设置进度监听

2024-07-10 11:24| 来源: 网络整理| 查看: 265

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