源代码
GitHub源代码
本文目标
根据分析okhttp的源码来实现上传进度监听
文件上传进度监听
先来看下okhttp上传文件的基本写法
private void UploadFile() {
File file = new File(Environment.getExternalStorageDirectory(), "test.png");
//1.1创建okHttpClient
OkHttpClient httpClient = new OkHttpClient();
//1.2创建RequestBody对象
MultipartBody.Builder builder = new MultipartBody
.Builder()
.setType(MultipartBody.FORM);
builder.addFormDataPart("platform", "android");
builder.addFormDataPart("file", file.getName(),
RequestBody.create(MediaType.parse(guessMimeType(file.getAbsolutePath())), file));
//1.3创建Request对象
Request request = new Request.Builder()
.url("https://www.hyd.com/xxxxx")
.post(builder.build()).build();
//2.把Request对象封装成call对象
Call call = httpClient.newCall(request);
//3.发起异步请求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.e("TAG", response.body().string());
}
});
}
okhttp是没有提供监听上传进度的方法的,需要我们自己去写,要怎么写呢?
首先,我们要搞明白okhttp真正把客户端数据写给服务器的地方是在 CallServerInterceptor 这个拦截器中,让我们把这行代码找出来
public final class CallServerInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
......
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
// 调用 RequestBody的writeTo方法向服务器去写数据
request.body().writeTo(bufferedRequestBody);
......
}
}
就是这行代码,我们可以分析出最终肯定会来到 MultipartBody 中的 writeTo 的方法。那么我们在这个方法里面去做处理不就行了,我们自己扩展一个 MyMultipartBody 去继承 MultipartBody 然后复写 writeTo 方法做处理,但是发现 MultipartBody 是一个 final 类,不能继承这个时候要怎么办呢?我们可以使用静态代理去实现
public class MyMultipartBody extends RequestBody {
// 被代理的对象
private RequestBody mRequestBody;
//回调接口
private UploadProgressListener mListener;
//当前的长度
private long mCurrentLength;
//总长度
private long mTotalLength;
public MyMultipartBody(RequestBody requestBody, UploadProgressListener listener) {
this.mRequestBody = requestBody;
this.mListener = listener;
}
@Override
public long contentLength() throws IOException {
return mRequestBody.contentLength();
}
@Override
public MediaType contentType() {
return mRequestBody.contentType();
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
//总长度
mTotalLength = contentLength();
//这里也是静态代理
//把sink传进去,也是跟MyMultipartBody这个静态代理如出一辙的手法,这样可以弄个中间件过一层
ForwardingSink forwardingSink = new ForwardingSink(sink) {
@Override
public void write(Buffer source, long byteCount) throws IOException {
//写内容就会来这里,其实也是sink在写
mCurrentLength += byteCount;
if (mListener != null) {
mListener.onProgress(mTotalLength, mCurrentLength);
}
super.write(source, byteCount);
}
};
//包装成BufferedSink
BufferedSink buffer = Okio.buffer(forwardingSink);
// 最终调用者还是被代理对象的方法
mRequestBody.writeTo(buffer);
// 刷新,RealConnection 连接池
buffer.flush();
}
}
然后在外界页面中,如下代码就能得到上传的实时进度,当然我们还需要用回调接口把进度返出去
private void uploadFile() {
File file = new File(Environment.getExternalStorageDirectory(), "test.png");
//1.1创建okHttpClient
OkHttpClient httpClient = new OkHttpClient();
//1.2创建RequestBody对象(MultipartBody是继承RequestBody)
MultipartBody.Builder builder = new MultipartBody
.Builder()
.setType(MultipartBody.FORM);
builder.addFormDataPart("platform", "android");
builder.addFormDataPart("file", file.getName(),
RequestBody.create(MediaType.parse(guessMimeType(file.getAbsolutePath())), file));
//1.3包装MultipartBody成MyMultipartBody
MyMultipartBody myMultipartBody = new MyMultipartBody(builder.build(), new UploadProgressListener() {
@Override
public void onProgress(long total, long current) {
//回调接口打印总进度和当前进度
Log.e("TAG", total + " : " + current);
}
});
//1.4创建Request对象
Request request = new Request.Builder()
.url("https://www.hyd.com/xxxxx")
.post(myMultipartBody)
.build();
//2.把Request对象封装成call对象
Call call = httpClient.newCall(request);
//3.发起异步请求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.e("TAG", response.body().string());
}
});
}
回调接口
public interface UploadProgressListener {
void onProgress(long total, long current);
}
至此我们就实现了okhttp上传文件的进度监听