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

Retrofit+RxJava实现带进度条的文件下载

这篇文章主要为大家详细介绍了Retrofit+RxJava实现带进度条的文件下载,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

项目中需要使用到更新版本,因此研究了一下Retrofit的下载文件,和进度条效果,其间也遇到了一些坑,写出来加深一下记忆,也为别的同学提供一下思路。

先说一下版本控制吧,通用做法基本上是通过接口获取服务器存储的app版本号,与应用的版本号进行比较,版本较低就去更新,先看一下如何获取应用版本号吧

PackageManager packageManager = mActivity.getPackageManager();

 PackageInfo packageInfo = null;

 try {
 packageInfo = packageManager.getPackageInfo(mActivity.getPackageName(), 0);
 } catch (PackageManager.NameNotFoundException e) {
 e.printStackTrace();
 }

 String versiOnName= packageInfo.versionName;

可以看到使用的是Context中的getPackageManager方法来获取PackageManager 对象,该对象可用于获取版本的一些信息。

上面的属于附内容,接下来就是关于Retrofit+RxJava实现进度条下载文件的功能,Retrofit本身不提供进度条显示的功能,但Retrofit默认使用Okhttp来进行网络请求,这里就可以自定义拦截器来进行拦截,实现进度。Okhttp的Demo中也为我们提供了一份代码,需要的可以去参考一下Progress.javar,可以看到拦截器的设置:

public class ProgressResponseBody extends ResponseBody {

 private ResponseBody responseBody;

 private ProgressListener progressListener;

 private BufferedSource bufferedSource;


 public ProgressResponseBody(ResponseBody responseBody,ProgressListener 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);
 //增加当前读取的字节数,如果读取完成了bytesRead会返回-1
 totalBytesRead += bytesRead != -1 ? bytesRead : 0;
 //回调,如果contentLength()不知道长度,会返回-1

 progressListener.onProgress(totalBytesRead,responseBody.contentLength(),bytesRead,bytesRead==-1);
 return bytesRead;
 }
 };
 }
}

ProgressListener 用来监听进度变化,回调到ProgressInterceptor中,ProgressInterceptor是一个自定义的拦截器,可以看一下代码

public class ProgressInterceptor implements Interceptor {

 @Override
 public Response intercept(Chain chain) throws IOException {

 Response respOnse=chain.proceed(chain.request());
 return response.newBuilder().body(new ProgressResponseBody(response.body(),progressListener)).build();
 }
 static final ProgressListener progressListener=new ProgressListener() {
 @Override
 public void onProgress(long progress, long total, long speed, boolean done) {

 Log.i("log","progress="+progress+"total="+total);
 }
 };
}

为了便于获取progress,可以通过OkHttpClient的addNetworkInterceptor方法直接添加一个自定义的拦截器,例如:

 //为Okhttp设置拦截器
 OkHttpClient client = new OkHttpClient.Builder()
 .addNetworkInterceptor(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();
 }
 })
 .build();

 //进度回调监听
 ProgressListener progressListener=new ProgressListener() {
 @Override
 public void onProgress(long progress, long total, long speed, boolean done) {

 Message message=new Message();

 message.obj=new AmallLoadBean(progress,total);

 progressHandler.sendMessage(message);
 }
 };

这里通过一个创建一个继承自Handler的ProgressHandler静态内部类用于在主线程中刷新进度,顺带提一下,使用static修饰ProgressHandler是因为静态内部类默认不持有外部类对象的引用,需要注意一下Handler的内存泄漏,使用一下写法:

//处理下载版本进度
 public class ProgressHandler extends Handler{

 private WeakReference mActivityWeakReference;
 public ProgressHandler(Activity activity){

 mActivityWeakReference=new WeakReference(activity);

 }

 @Override
 public void handleMessage(Message msg) {
 if(mActivityWeakReference.get()!=null){
 AmallLoadBean amallLoadBean= (AmallLoadBean) msg.obj;
 long progress=amallLoadBean.getProgress();
 long total=amallLoadBean.getTotal();
 float cp=(float)progress/(float)total;

 }
 }
 }

继续回到下载文件中,我才用的是Retrofit+RxJava的方法来实现,写之前也看了一下别人写的,好像不全,下满也遇到了一些小坑,讲一下吧:

observable.subscribeOn(Schedulers.io())
 .observeOn(Schedulers.io())
 .doOnNext(new Action1() {
  @Override
  public void call(ResponseBody responseBody) {

  saveFiles(responseBody);
  }
 })
 .observeOn(AndroidSchedulers.mainThread())
 .subscribe(new Observer() {
  @Override
  public void onCompleted() {
  installApk();

  }

  @Override
  public void onError(Throwable e) {

  ToastUtils.getInstance().showToast("请到应用市场下载最新版本");
  }

  @Override
  public void onNext(ResponseBody responseBody) {

  }
 });
 }

通过RxJava的doOnNext在subscribe方法之前存储文件,这里需要注意的是doOnNext方法需要在子线程中执行,调用.observeOn(Schedulers.io())方法,然后再切换到主线程,否则文件下载不下来。当文件下载完成时,在onCompleted方法中执行installApk()方法安装app。需要注意的是这里需要做权限的适配,因为我的是自己封装的因为就不拿出来了,挺简单就自己写吧。保存文件的代码给大家放出来了,通俗的语言:

/**
 * 保存文件
 */
 public void saveFiles(ResponseBody responseBody){

 InputStream inputStream = null;

 FileOutputStream fileOutputStream = null;

 byte[] buffer=new byte[2048];

 int len;

 File file=new File(saveFileName);

 if(!file.exists()){

 file.mkdirs();
 }

 try {
 inputStream=responseBody.byteStream();

 fileOutputStream=new FileOutputStream(file);

 while ((len=inputStream.read(buffer))!=-1){

 fileOutputStream.write(buffer,0,len);
 }

 inputStream.close();

 fileOutputStream.close();

 } catch (Exception e) {
 e.printStackTrace();
 }


 }

在安装文件的时候,需要注意7.0以后的适配,代码看看就好,和拍照适配的原理一直,都是Android对私密性文件的权限问题

 /**
 * 安装apk
 *
 */
 private void installApk() {
 File apkfile = new File(saveFileName);
 if (!apkfile.exists()) {
 return;
 }
 //判断版本号

 if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){
 Uri apkUri = FileProvider.getUriForFile(activity, "******.fileprovider", apkfile);
 Intent install = new Intent(Intent.ACTION_VIEW);
 install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 //添加这一句表示对目标应用临时授权该Uri所代表的文件
 install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
 install.setDataAndType(apkUri, "application/vnd.android.package-archive");
 activity.startActivity(install);

 }else{

 Intent i = new Intent(Intent.ACTION_VIEW);
 i.setDataAndType(Uri.parse("file://" + apkfile.toString()), "application/vnd.android.package-archive");
 activity.startActivity(i);
 }

 }

基本上就这些,后续我会在此篇文章上继续补充。

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


推荐阅读
  • 优化局域网SSH连接延迟问题的解决方案
    本文介绍了解决局域网内SSH连接到服务器时出现长时间等待问题的方法。通过调整配置和优化网络设置,可以显著缩短SSH连接的时间。 ... [详细]
  • 本文介绍如何通过SSH协议使用Xshell远程连接到Ubuntu系统。为了实现这一目标,需要确保Ubuntu系统已安装并配置好SSH服务器,并保证网络连通性。 ... [详细]
  • 本文详细介绍如何使用arm-eabi-gdb调试Android平台上的C/C++程序。通过具体步骤和实用技巧,帮助开发者更高效地进行调试工作。 ... [详细]
  • 掌握远程执行Linux脚本和命令的技巧
    本文将详细介绍如何利用Python的Paramiko库实现远程执行Linux脚本和命令,帮助读者快速掌握这一实用技能。通过具体的示例和详尽的解释,让初学者也能轻松上手。 ... [详细]
  • 本文详细分析了Hive在启动过程中遇到的权限拒绝错误,并提供了多种解决方案,包括调整文件权限、用户组设置以及环境变量配置等。 ... [详细]
  • 使用Python在SAE上开发新浪微博应用的初步探索
    最近重新审视了新浪云平台(SAE)提供的服务,发现其已支持Python开发。本文将详细介绍如何利用Django框架构建一个简单的新浪微博应用,并分享开发过程中的关键步骤。 ... [详细]
  • 深入探讨CPU虚拟化与KVM内存管理
    本文详细介绍了现代服务器架构中的CPU虚拟化技术,包括SMP、NUMA和MPP三种多处理器结构,并深入探讨了KVM的内存虚拟化机制。通过对比不同架构的特点和应用场景,帮助读者理解如何选择最适合的架构以优化性能。 ... [详细]
  • 解决JAX-WS动态客户端工厂弃用问题并迁移到XFire
    在处理Java项目中的JAR包冲突时,我们遇到了JaxWsDynamicClientFactory被弃用的问题,并成功将其迁移到org.codehaus.xfire.client。本文详细介绍了这一过程及解决方案。 ... [详细]
  • 本题探讨如何通过最大流算法解决农场排水系统的设计问题。题目要求计算从水源点到汇合点的最大水流速率,使用经典的EK(Edmonds-Karp)和Dinic算法进行求解。 ... [详细]
  • 深入解析 Spring Security 用户认证机制
    本文将详细介绍 Spring Security 中用户登录认证的核心流程,重点分析 AbstractAuthenticationProcessingFilter 和 AuthenticationManager 的工作原理。通过理解这些组件的实现,读者可以更好地掌握 Spring Security 的认证机制。 ... [详细]
  • 本文介绍如何在现有网络中部署基于Linux系统的透明防火墙(网桥模式),以实现灵活的时间段控制、流量限制等功能。通过详细的步骤和配置说明,确保内部网络的安全性和稳定性。 ... [详细]
  • Git管理工具SourceTree安装与使用指南
    本文详细介绍了Git管理工具SourceTree的安装、配置及团队协作方案,旨在帮助开发者更高效地进行版本控制和项目管理。 ... [详细]
  • 深入解析ESFramework中的AgileTcp组件
    本文详细介绍了ESFramework框架中AgileTcp组件的设计与实现。AgileTcp是ESFramework提供的ITcp接口的高效实现,旨在优化TCP通信的性能和结构清晰度。 ... [详细]
  • 本文介绍如何配置SecureCRT以正确显示Linux终端的颜色,并解决中文显示问题。通过简单的步骤设置,可以显著提升使用体验。 ... [详细]
  • CentOS 7.2 配置防火墙端口开放
    本文介绍如何在 CentOS 7.2 系统上配置防火墙以开放特定的服务端口,包括 FTP 服务的临时与永久开放方法,以及如何验证配置是否生效。 ... [详细]
author-avatar
欠你的情意
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有