(窃图地址: 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 的方式可以简洁对请求数据或者响应数据进行装饰。