专栏首页恩蓝脚本Okhttp、Retrofit进度获取的方法(一行代码搞定)

Okhttp、Retrofit进度获取的方法(一行代码搞定)

起因

对于广大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

以上就是本文的全部内容,希望对大家的学习有所帮助。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Android实现简单下拉筛选框

    最近接到一个新的项目,项目时间比较紧张,有一个功能类似于58同城,京东的一个下拉筛选框,为了节省时间,从网上面拷贝了一份封装好的代码,进行的自己的一些修改,感觉...

    砸漏
  • XListView实现网络加载图片和下拉刷新

    本文实例为大家分享了XListView实现网络加载图片,和下拉刷新的功能,供大家参考,具体内容如下

    砸漏
  • Android仿微信录音功能(录音后的raw文件转mp3文件)

    现在很多时候需要用到录音,然后如果我们的App是ios和android两端的话,就要考虑录音的文件在两端都能使用,这个时候就需要适配,两端的录音文件都要是mp3...

    砸漏
  • 震惊!我逆向了Android代码居然看见……

    用户1907613
  • 基础知识 | 每日一练(101)

    士人有百折不回之真心,才有万变不穷之妙用。立业建功,事事要从实地着脚,若少慕声闻,便成伪果;讲道修德,念念要从虚处立基,若稍计功效,便落尘情。 ...

    小林C语言
  • Facebook、谷歌、Big Switch在开放硬件上演示网络操作系统

    SDNLAB
  • c#实现redis客户端(一)

    蘑菇先生
  • 传统电商做小程序的好处,拒绝盲目跟风

    ​​​2018年小程序的发展速度加快,越来越多的行业加入小程序的行列,用小程序开启了新的发展。

    场景录小程序
  • 分销、诱导分享、对赌玩法各存诱惑与风险,宠物与二次元小程序齐飞 | 晓榜

    知晓君
  • 【玩转Redis面试第2讲】面试官再问Redis事务把这篇文章扔给他

    原子性:一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被恢复(Rollbac...

    爱笑的架构师

扫码关注云+社区

领取腾讯云代金券