起因
对于广大Android开发者来说,最近用的最多的网络库,莫过于Okhttp啦(Retrofit依赖Okhttp)。
Okhttp不像SDK内置的HttpUrlConnection一样,可以明确的获取数据读写的过程,我们需要执行一些操作。
介绍
Retrofit依赖Okhttp、Okhttp依赖于Okio。那么Okio又是什么鬼?别急,看官方介绍:
Okio is a library that complements java.io and java.nio to make it much easier to access, store, and process your data.
翻译过来就是,Okio是一个实现了java.io和java.nio的一个类库,它让连接,存储,处理你的数据更加轻松~(Okio既是读写相关类库,获取进度要从Okio入手)。
好吧,对于广大开发者来说,内心是这样的:TM又要看你文档和用例,按你规则走,轻松个毛啊!
其实,读下API,看下Example熟悉后,人家设计的还是很棒哒。
废话不多说,先看效果。
效果
实际代码:
//添加下载拦截器(this参数是实现下载进度接口的对象) mDownClient = new OkHttpClient.Builder() //只需要一行代码就行了 .addNetworkInterceptor(new DownloadInterceptor(this)) .build(); //添加上传拦截器(this参数是实现上传回调接口的对象) mUploadClient = new OkHttpClient.Builder() //只需要一行代码就行了 .addNetworkInterceptor(new UploadInterceptor(this)) .build();
你只需要一行代码是不行的!我为什么行?因为这是我写的封装类库啊~(最后放地址)
思路
Okhttp依赖Okio进行了数据的读写动作,我们需要找到Okio进行处理。那么,如何加上呢?
Okhttp可以添加Interceptor(拦截器),我们可以通过拦截器的接口方法,获取对应的responseBody、requestBody对象进行操作。然后我们就获取了读写相关的实现方法。具体实现是通过Source、Sink对象。
Source官方解释:Supplies a stream of bytes. Use this interface to read data from wherever it's located。
Sink官方解释:Receives a stream of bytes. Use this interface to write data wherever it's needed。
一句话概括:Source对象是对输入流的包装(下载读数据),Sink是对输出流的包装(写数据上传)。
实现
根据需要添加下载、上传Interceptor
//添加下载拦截器(this参数是实现下载进度接口的对象) mDownClient = new OkHttpClient.Builder() .addNetworkInterceptor(new DownloadInterceptor(this)) .build(); //添加上传拦截器(this参数是实现上传回调接口的对象) mUploadClient = new OkHttpClient.Builder() .addNetworkInterceptor(new UploadInterceptor(this)) .build();
拦截器具体实现
//下载拦截器 public class DownloadInterceptor implements Interceptor { private OnDownloadListener mListener; public DownloadInterceptor( OnDownloadListener listener) { mListener = listener; } @Override public Response intercept(Chain chain) throws IOException { //封装ressponse对象 Response response = wrapResponse(chain.proceed(chain.request())); return response; } private Response wrapResponse(Response response) { if (response == null || response.body() == null) { return response; } //获取处理后的response对象 Response wrapResponse = getWrapResponse(response); return wrapResponse; } private Response getWrapResponse(Response response) { ProgressInfo info = new ProgressInfo(); info.setTime(System.currentTimeMillis()+""); info.setUrl(response.request().url().toString()); Response.Builder builder = response.newBuilder(); //封装responseBody,传入相关参数,获取进度数据回调 return builder.body(new WrapResponseBody(response.body(),info,mListener)).build(); } } --------------------------------------分割--------------------------------------- //上传拦截器 public class UploadInterceptor implements Interceptor { private OnUploadListener mListener; public UploadInterceptor(OnUploadListener listener) { mListener = listener; } @Override public Response intercept(Chain chain) throws IOException { //封装request对象 Request request = wrapRequest(chain.request()); Response response = chain.proceed(request); return response; } private Request wrapRequest(Request request) { if (request == null || request.body() == null) { return request; } Request.Builder builder = request.newBuilder(); ProgressInfo info = new ProgressInfo(); HttpUrl url = request.url(); info.setUrl(url.toString()); info.setTime(System.currentTimeMillis()+""); //封装requestBody,传入参数,获取数据进度回调 builder.method(request.method(),new WrapRequestBody(request.body(),info,mListener)); return builder.build(); } } responseBody、requestBody相关实现 //继承ResponseBody实现具体方法 public class WrapResponseBody extends ResponseBody { private Handler mHandler = new Handler(Looper.getMainLooper()); private ResponseBody mResponseBody; private OnDownloadListener mListener; private ProgressInfo mInfo; private BufferedSource mBufferedSource; private boolean mDoProgress; //传入进度,以及监听对象 public WrapResponseBody(ResponseBody responseBody, ProgressInfo info, OnDownloadListener listener) { mResponseBody = responseBody; mInfo = info; mListener = listener; } @Nullable @Override public MediaType contentType() { //接口方法,返回类型 return mResponseBody.contentType(); } @Override public long contentLength() { long contentLength = mResponseBody.contentLength(); //gzip压缩格式会返回-1,目前处理是在请求头信息指定("Accept-Encoding","identity")表示不压缩 if (contentLength == -1) { mDoProgress = false; mHandler.post(new Runnable() { @Override public void run() { //切换线程,进行失败回调 mListener.onDownLoadGetContentLengthFail(mInfo); } }); } else { mDoProgress = true; } return contentLength; } @Override public BufferedSource source() { //WrapSource(继承ForwardingSource,ForwardingSource实现了Source接口) if (mBufferedSource == null) { mInfo.setContentLength(contentLength()); //传入参数,读取具体进度信息,并回调 WrapSource wrapSource = new WrapSource(mResponseBody.source(), mInfo, mListener,mDoProgress); mBufferedSource = Okio.buffer(wrapSource); } return mBufferedSource; } } --------------------------------------分割--------------------------------------- //继承ResquestBody实现具体方法 public class WrapRequestBody extends RequestBody { private RequestBody mRequestBody; private OnUploadListener mListener; private ProgressInfo mInfo; private boolean mDoProgress; private Handler mHandler = new Handler(Looper.getMainLooper()); //传入进度,以及监听对象 public WrapRequestBody(RequestBody requestBody, ProgressInfo info, OnUploadListener listener) { mRequestBody = requestBody; mListener = listener; mInfo = info; } @Override public MediaType contentType() { //接口方法,返回类型 return mRequestBody.contentType(); } @Override public long contentLength() throws IOException { try { //上传内容长度,有异常走failWrok处理 long l = mRequestBody.contentLength(); mDoProgress = true; return l; } catch (IOException e) { e.printStackTrace(); failWork(); return -1; } } //进行失败处理 private void failWork() { mDoProgress = false; mHandler.post(new Runnable() { @Override public void run() { //切换线程,回调失败信息 mListener.onUploadGetContentLengthFail(mInfo); } }); } @Override public void writeTo(BufferedSink sink) throws IOException { mInfo.setContentLength(contentLength()); // WrapSink (继承ForwardingSink,ForwardingSink实现了Sink接口) ///传入参数,读取具体进度信息,并回调 WrapSink wrapSink = new WrapSink(sink, mInfo, mListener, mDoProgress); BufferedSink buffer = Okio.buffer(wrapSink); mRequestBody.writeTo(buffer); buffer.flush(); } } WrapSource、WrapSink相关实现 //继承ForwardingSource 实现具体方法 public class WrapSource extends ForwardingSource { private Handler mHandler = new Handler(Looper.getMainLooper()); private Source mSource; private ProgressInfo mInfo; private OnDownloadListener mListener; private boolean mDoProgress; public WrapSource(Source source, ProgressInfo info, OnDownloadListener listener, boolean doProgress) { //传入源Source、进度信息、监听进度等信息。 super(source); mSource = source; mInfo = info; mListener = listener; //传入是否继续执行回调boolean参数,如果之前执行有异常,则不再继续执行回调 mDoProgress = doProgress; } @Override public long read(Buffer sink, long byteCount) throws IOException { //获取具体进度信息,来到了熟悉的具体IO long read = super.read(sink, byteCount); if (read != -1) { long l = mInfo.getCurrentLength() + read; mInfo.setCurrentLength(l); mHandler.post(new Runnable() { @Override public void run() { if (mDoProgress) { //切换到主线程,回调数据 mListener.onDownLoadProgress(mInfo); } } }); } return read; } } --------------------------------------分割--------------------------------------- //继承ForwardingSink 实现具体方法 public class WrapSink extends ForwardingSink { private Handler mHandler = new Handler(Looper.getMainLooper()); public OnUploadListener mListener; public ProgressInfo mInfo; public boolean mDoProgress; public WrapSink(Sink delegate, ProgressInfo info, OnUploadListener listener, boolean doProgress) { //传入源Source、进度信息、监听进度等信息。 super(delegate); mInfo = info; mListener = listener; //传入是否继续执行回调boolean参数,如果之前执行有异常,则不再继续执行回调 mDoProgress = doProgress; } @Override public void write(Buffer source, long byteCount) throws IOException { super.write(source, byteCount); //获取具体进度信息,来到了熟悉的具体IO long l = mInfo.getCurrentLength() + byteCount; mInfo.setCurrentLength(l); mHandler.post(new Runnable() { @Override public void run() { if (mDoProgress) { //切换到主线程,回调数据 mListener.onUpLoadProgress(mInfo); } } }); } }
总结
以上就是具体的流程了,按照步骤其实很简单。大家了解下挺好的,我这边也封装好了具体的类库和Demo,大家可以直接依赖(查看README.md,使用简单)。
地址:https://github.com/HoldMyOwn/TNetProgress
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持自由互联。