上一篇文章介绍了用Retrofit实现文件的上传与下载,但是我们发现没办法监听上传下载的进度,毕竟我们在做开发的时候经常是要显示上传或者下载的进度了.虽然Retrofit没有给我们提供现成的api来监听进度,但是Retrofit很灵活,它底层网络访问是用的okhttp实现的,当然我们也可以设置其他第三方网络请求库,因为Retrofit可以设置client,我们可以由此来扩展下载上传的进度监听.
本文使用okhttp作为client来做,其实说白了跟用okhttp做下载上传进度监听几乎一样,参考了这篇文章:Android OkHttp文件上传与下载的进度监听扩展
1. 首先我们写两个接口用来下载和上传的进度监听回调:
1 2 3 4 5 6 7 8 9 10 11 12 13
|
public interface ProgressRequestListener { void onRequestProgress(long bytesWritten, long contentLength, boolean done); }
public interface ProgressResponseListener { void onResponseProgress(long bytesRead, long contentLength, boolean done); }
|
2. 实现 ProgressRequestBody、ProgressResponseBody
ProgressRequestBody类继承RequestBody用于请求进度监听,用于文件上传进度监听:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
| import java.io.IOException; import okhttp3.MediaType; import okhttp3.RequestBody; import okio.Buffer; import okio.BufferedSink; import okio.ForwardingSink; import okio.Okio; import okio.Sink; import rx.Observable;
public class ProgressRequestBody extends RequestBody { private RequestBody requestBody; private ProgressRequestListener progressListener; private BufferedSink bufferedSink;
public ProgressRequestBody(RequestBody requestBody, ProgressRequestListener progressListener) { this.requestBody = requestBody; this.progressListener = progressListener; }
@Override public MediaType contentType() { return requestBody.contentType(); }
@Override public long contentLength() throws IOException { return requestBody.contentLength(); }
@Override public void writeTo(BufferedSink sink) throws IOException { if (bufferedSink == null) {
bufferedSink = Okio.buffer(sink(sink));
} requestBody.writeTo(bufferedSink); bufferedSink.flush();
}
private Sink sink(Sink sink) { return new ForwardingSink(sink) { long bytesWritten = 0L; long contentLength = 0L;
@Override public void write(Buffer source, long byteCount) throws IOException { super.write(source, byteCount); if (contentLength == 0) { contentLength = contentLength(); } bytesWritten += byteCount; if(progressListener != null){ progressListener.onRequestProgress(bytesWritten, contentLength, bytesWritten == contentLength); } } }; } }
|
ProgressResponseBody继承ResponseBody用于下载进度监听:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
| package com.cm.retrofit.http;
import java.io.IOException;
import okhttp3.MediaType; import okhttp3.ResponseBody; import okio.Buffer; import okio.BufferedSource; import okio.ForwardingSource; import okio.Okio; import okio.Source;
public class ProgressResponseBody extends ResponseBody { private final ResponseBody responseBody; private final ProgressResponseListener progressListener; private BufferedSource bufferedSource;
public ProgressResponseBody(ResponseBody responseBody, ProgressResponseListener progressListener) { 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); totalBytesRead += bytesRead != -1 ? bytesRead : 0; if(progressListener != null){ progressListener.onResponseProgress(totalBytesRead, responseBody.contentLength(), bytesRead == -1); } return bytesRead; } }; } }
|
上面两个类里的writeTo
跟source
方法用到了sink
跟Source
,这两个类属于Okio,也是Square开源的java io补充库,有兴趣的可以去了解一下,这里就不做详细介绍了.
3. 实现HttpClientHelper类
HttpClientHelper用于创建okhttp client,并对okhttpclient添加拦截事件,将requestBody和responseBody替换成我们自己实现的ProgressRequestBody和ProgressResponseBody
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| package com.cm.retrofit.http;
import java.io.IOException;
import okhttp3.Interceptor; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response;
public class HttpClientHelper {
public static OkHttpClient addProgressResponseListener(final ProgressResponseListener progressListener){ OkHttpClient.Builder client = new OkHttpClient.Builder(); client.addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Response originalResponse = chain.proceed(chain.request());
return originalResponse.newBuilder() .body(new ProgressResponseBody(originalResponse.body(), progressListener)) .build(); } }); return client.build(); }
public static OkHttpClient addProgressRequestListener(final ProgressRequestListener progressListener){ OkHttpClient.Builder client = new OkHttpClient.Builder(); client.addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request original = chain.request();
Request request = original.newBuilder() .method(original.method(), new ProgressRequestBody(original.body(),progressListener)) .build(); return chain.proceed(request); } }); return client.build(); } }
|
4. 使用
ServiceGenerator类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| package com.cm.retrofit.service;
import com.cm.retrofit.http.HttpClientHelper; import com.cm.retrofit.http.ProgressRequestListener; import com.cm.retrofit.http.ProgressResponseListener;
import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; import retrofit2.converter.protobuf.ProtoConverterFactory; import retrofit2.converter.scalars.ScalarsConverterFactory;
public class ServiceGenerator { private static final String HOST = "http://www.xxx.com/ ";
private static Retrofit.Builder builder = new Retrofit.Builder() .baseUrl(HOST) .addConverterFactory(ProtoConverterFactory.create()) .addConverterFactory(ScalarsConverterFactory.create()) .addConverterFactory(GsonConverterFactory.create());
public static <T> T createService(Class<T> tClass){ return builder.build().create(tClass); }
public static <T> T createResponseService(Class<T> tClass, ProgressResponseListener listener){ return builder .client(HttpClientHelper.addProgressResponseListener(listener)) .build() .create(tClass); }
public static <T> T createReqeustService(Class<T> tClass, ProgressRequestListener listener){ return builder .client(HttpClientHelper.addProgressRequestListener(listener)) .build() .create(tClass); }
}
|
使用:
1 2 3 4 5
| UpLoadService service = ServiceGenerator.createReqeustService(UpLoadService.class,this);
DownloadService downloadService = ServiceGenerator.createResponseService(DownloadService.class, this);
|
使用方法跟之前的一样,只是生成对应api service的时候调用带进度接口的方法就可以了,然后传入实现了回调接口的对象就可以.然后在回调里面进行界面的展示.
这样我们就实现了对上传下载的进度监听
补充:
上传进度监听我们也可以不用在okhttp的拦截里设置,可以在请求封装的requestbody的时候将requestbody封装成ProgressRequestBody,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| UpLoadService service = ServiceGenerator.createService(UpLoadService.class); File file = new File(fileUri); RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file); MultipartBody.Part body = MultipartBody.Part.createFormData("file", file.getName(), requestFile); Call<ResponseBody> call = service.upload(body);
UpLoadService service = ServiceGenerator.createService(UpLoadService.class); File file = new File(fileUri); RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
MultipartBody.Part body = MultipartBody.Part.createFormData("file", file.getName(), new ProgressRequestBody(requestFile,this)); Call<ResponseBody> call = service.upload(body);
|
这样一样也可以实现对上传的进度监听.