基于GET进行文件下载,写法与GET请求基本一致,只不过一般GET请求的时候是通过获取ResponseBody的String,从String中获取服务器的回调信息,而下载则是通过获取ResponseBody的InputStream,然后将InputStream保存到本地文件中
由于文件下载不仅仅涉及网络请求,还涉及文件的读写,不建议在主线程中进行,因此建议使用异步方式进行请求
文件下载-不带进度
1 | OkHttpClient client = new OkHttpClient(); |
文件下载-进度
与文件上传一样,OkHttp在进行文件下载的时候默认是不提供下载进度的
文件上传的时候,是将文件写入RequestBody中进行上传,而下载的时候是在ResponseBody中读取的,所以如果想要读取文件下载的进度,和上传的时候类似,上传的时候重写了RequestBody,下载就需要对ResponseBody进行重写
与上传还有一个区别,上传的时候RequestBody是请求体,请求的时候旧直接设置了,而下载的时候ResponseBody是服务器的响应结果,并不能直接进行处理,但是OkHttp可以设置拦截器,通过拦截器就可以设置ResponseBody了
设置拦截器
1 | Interceptor interceptor = new Interceptor() { |
在Interceptor中可以看到,其返回了一个Response,这个Response就是Callback返回的Response,同时为这个Response设置一个自定义的ResponseBody,然后就可以通过自定义的Response来进行下载进度的监听
重写ResponseBody
ResponseBody分析
首先查看ResponseBody发现需要重写的方法主要有三个1
2
3
4
5
6
7
8
9public abstract MediaType contentType();
/**
* Returns the number of bytes in that will returned by {@link #bytes}, or {@link #byteStream}, or
* -1 if unknown.
*/
public abstract long contentLength();
public abstract BufferedSource source();
与RequestBody对比,RequestBody中使用writeTo进行写入,而ResponseBody中使用source返回数据,所以监听BufferedSource的大小就可以获取到下载的进度
通过RequestBody和Response的源码可以发现BufferedSource其实就是一个Buffer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 > public ResponseBody peekBody(long byteCount) throws IOException {
> BufferedSource source = body.source();
> source.request(byteCount);
> Buffer copy = source.buffer().clone();
> // There may be more than byteCount bytes in source.buffer(). If there is, return a prefix.
> Buffer result;
> if (copy.size() > byteCount) {
> result = new Buffer();
> result.write(copy, byteCount);
> copy.clear();
> } else {
> result = copy;
> }
> return ResponseBody.create(body.contentType(), result.size(), result);
> }
>
从原RequestBody将数据读取到重写的RequestBody中
但是重写的ResponseBody是没有数据的,因此需要从原来的ResponseBody中获取
Okio类还提供了从一个Source中读取到BufferSource的方法1
2
3
4
5
6
7
8/**
* Returns a new source that buffers reads from {@code source}. The returned
* source will perform bulk reads into its in-memory buffer. Use this wherever
* you read a source to get an ergonomic and efficient access to data.
*/
public static BufferedSource buffer(Source source) {
return new RealBufferedSource(source);
}
以及传递类1
2/** A {@link Source} which forwards calls to another. Useful for subclassing. */
public abstract class ForwardingSource implements Source
通过这两个类就可以轻松将原数据写入到新的BufferedSource中1
2
3
4
5
6Okio.buffer(new ForwardingSource(mResponseBody.source()) {
public long read(Buffer sink, long byteCount) throws IOException {
return super.read(sink, byteCount);
}
});
然后通过read方法中的buffer就可以进行进度回调了
重写ResponseBody
1 | public class ProgressResponseBody extends ResponseBody { |
在构造方法中传入一个ResponseBody和进度回调,传入的ResponseBody就是原ResponseBody,需要从中读取数据
完整调用
1 | Interceptor interceptor = new Interceptor() { |