OKHttp 数据读写进度监听分析


请尊重原创,转载请注明出处 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 的方式可以简洁对请求数据或者响应数据进行装饰。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在 Android 中使用 OkHttp 实现带进度的文件上传,可以使用 OkHttp 提供的 Interceptor 和 RequestBody 类。下面是一个简单的示例代码: ```java public class ProgressRequestBody extends RequestBody { private static final int DEFAULT_BUFFER_SIZE = 2048; private final File file; private final String contentType; private final ProgressListener listener; public ProgressRequestBody(File file, String contentType, ProgressListener listener) { this.file = file; this.contentType = contentType; this.listener = listener; } @Override public MediaType contentType() { return MediaType.parse(contentType); } @Override public long contentLength() { return file.length(); } @Override public void writeTo(BufferedSink sink) throws IOException { long fileLength = file.length(); byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; long uploaded = 0; try (InputStream in = new FileInputStream(file)) { int read; while ((read = in.read(buffer)) != -1) { uploaded += read; sink.write(buffer, 0, read); if (listener != null) { listener.onProgress(uploaded, fileLength); } } } } public interface ProgressListener { void onProgress(long uploaded, long total); } } ``` 这个类继承自 OkHttp 的 RequestBody 类,并实现了上传文件进度监听。在 writeTo() 方法中使用 InputStream 从文件中数据,并将数据入 BufferedSink 中。同时,每次数据都会调用 onProgress() 方法通知进度监听器。 然后,创建一个 OkHttpClient,添加一个 Interceptor,该 Interceptor 使用 ProgressRequestBody 替换 RequestBody,从而实现进度监听: ```java OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request original = chain.request(); // 使用 ProgressRequestBody 替换 RequestBody ProgressRequestBody requestBody = new ProgressRequestBody(file, contentType, listener); Request request = original.newBuilder() .method(original.method(), requestBody) .build(); return chain.proceed(request); } }) .build(); ``` 其中,file 是要上传的文件,contentType 是文件的 MIME 类型,listener 是进度监听器。最后,使用 OkHttpClient 发起一个上传文件的 Request: ```java Request request = new Request.Builder() .url(uploadUrl) .post(requestBody) .build(); Call call = client.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { // 上传失败 } @Override public void onResponse(Call call, Response response) throws IOException { // 上传成功 } }); ``` 在 onResponse() 方法中处理上传结果即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值