OKHttp 数据读写进度监听分析

版权声明:本文为“剑西”原创文章,转载需注明出处! https://blog.csdn.net/mabeijianxi/article/details/77992315


请尊重原创,转载请注明出处 http://blog.csdn.net/mabeijianxi/article/details/77992315


大致架构图



(窃图地址: https://blog.piasy.com/2016/07/11/Understand-OkHttp/,可以先详细看看)


写入进度监听(比如上传)

可以对 RequestBody 进行装饰, writeTo() 中 对 Sink 再进行装饰,可以直接利用 ForwardingSink ,重写其 write(), 其内即可获得每次写入的字节数,RequestBody具体装饰如下:

public class CountingRequestBody extends RequestBody
{

    protected RequestBody delegate;
    protected Listener listener;

    protected CountingSink countingSink;

    public CountingRequestBody(RequestBody delegate, Listener listener)
    {
        this.delegate = delegate;
        this.listener = listener;
    }

    @Override
    public MediaType contentType()
    {
        return delegate.contentType();
    }

    @Override
    public long contentLength()
    {
        try
        {
            return delegate.contentLength();
        } catch (IOException e)
        {
            e.printStackTrace();
        }
        return -1;
    }

    @Override
    public void writeTo(BufferedSink sink) throws IOException
    {

        countingSink = new CountingSink(sink);
        BufferedSink bufferedSink = Okio.buffer(countingSink);

        delegate.writeTo(bufferedSink);

        bufferedSink.flush();
    }

    protected final class CountingSink extends ForwardingSink
    {

        private long bytesWritten = 0;

        public CountingSink(Sink delegate)
        {
            super(delegate);
        }

        @Override
        public void write(Buffer source, long byteCount) throws IOException
        {
            super.write(source, byteCount);

            bytesWritten += byteCount;
            listener.onRequestProgress(bytesWritten, contentLength());
        }

    }

    public static interface Listener
    {
        public void onRequestProgress(long bytesWritten, long contentLength);
    }

}

返回数据的进度监听(比如下载):

可以在这个责任链中添加一个 NetWorkInterceptor ,对返回的 ResponseBody 进行包装,在 source() 函数中对 Source 再进行一个包装,可以直接利用 ForwdingSource ,重写其 read() 函数即可,ResponseBody 具体包装如下:

private static class OkHttpProgressResponseBody extends ResponseBody {
        private final HttpUrl url;
        private final ResponseBody responseBody;
        private final ResponseProgressListener progressListener;
        private BufferedSource bufferedSource;

        OkHttpProgressResponseBody(HttpUrl url, ResponseBody responseBody,
                                   ResponseProgressListener progressListener) {
            this.url = url;
            this.responseBody = responseBody;
            this.progressListener = progressListener;
        }

        @Override
        public MediaType contentType() {
            return responseBody.contentType();
        }

        @Override
        public long contentLength() {
            return responseBody.contentLength();
        }

        @Override
        public BufferedSource source() {
            if (bufferedSource == null) {
                bufferedSource = Okio.buffer(source(responseBody.source()));
            }
            return bufferedSource;
        }

        private Source source(Source source) {
            return new ForwardingSource(source) {
                long totalBytesRead = 0L;

                @Override
                public long read(Buffer sink, long byteCount) throws IOException {
                    long bytesRead = super.read(sink, byteCount);
                    long fullLength = responseBody.contentLength();
                    if (bytesRead == -1) { // this source is exhausted
                        totalBytesRead = fullLength;
                    } else {
                        totalBytesRead += bytesRead;
                    }
                    progressListener.update(url, totalBytesRead, fullLength);
                    return bytesRead;
                }
            };
        }
    }

然后就是需要的时候添加一个 NetWorkInterceptor即可,如下:

 OkHttpClient client = new OkHttpClient.Builder()
            .addNetworkInterceptor(new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    Request request = chain.request();
                    Response response = chain.proceed(request);
                    return response.newBuilder()
                            .body(new OkHttpProgressResponseBody(request.url(), response.body(),
                                    progressListener))
                            .build();
                }
            }).build();

progressListener 接口自己定义一个即可。

总结

通过添加 Interceptor 的方式可以简洁对请求数据或者响应数据进行装饰。

猜你喜欢

转载自blog.csdn.net/mabeijianxi/article/details/77992315
今日推荐