热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

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

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


推荐阅读
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • 打开文件管理器_【教程】模组管理器3.1食用指南
    文编:byakko最近有部分小伙伴反应还不会使用unity模组管理器,现在我就给大家讲一下unity模组管理器——从下载到使用。完整视频版以下是无WiF ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 学习笔记(34):第三阶段4.2.6:SpringCloud Config配置中心的应用与原理第三阶段4.2.6SpringCloud Config配置中心的应用与原理
    立即学习:https:edu.csdn.netcourseplay29983432482?utm_sourceblogtoedu配置中心得核心逻辑springcloudconfi ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
  • 【Windows】实现微信双开或多开的方法及步骤详解
    本文介绍了在Windows系统下实现微信双开或多开的方法,通过安装微信电脑版、复制微信程序启动路径、修改文本文件为bat文件等步骤,实现同时登录两个或多个微信的效果。相比于使用虚拟机的方法,本方法更简单易行,适用于任何电脑,并且不会消耗过多系统资源。详细步骤和原理解释请参考本文内容。 ... [详细]
  • 20211101CleverTap参与度和分析工具功能平台学习/实践
    1.应用场景主要用于学习CleverTap的使用,该平台主要用于客户保留与参与平台.为客户提供价值.这里接触到的原因,是目前公司用到该平台的服务~2.学习操作 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 安卓select模态框样式改变_微软Office风格的多端(Web、安卓、iOS)组件库——Fabric UI...
    介绍FabricUI是微软开源的一套Office风格的多端组件库,共有三套针对性的组件,分别适用于web、android以及iOS,Fab ... [详细]
  • 使用在线工具jsonschema2pojo根据json生成java对象
    本文介绍了使用在线工具jsonschema2pojo根据json生成java对象的方法。通过该工具,用户只需将json字符串复制到输入框中,即可自动将其转换成java对象。该工具还能解析列表式的json数据,并将嵌套在内层的对象也解析出来。本文以请求github的api为例,展示了使用该工具的步骤和效果。 ... [详细]
author-avatar
这个世界我看不懂2011_595
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有