如何使用Retrofit+RxJava实现带进度条的文件下载
创始人
2024-12-21 18:42:47
0次

先说一下版本控制吧,通用做法基本上是通过接口获取服务器存储的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); } }

相关内容

热门资讯

空调连续工作多久需要停机休息 空调连续工作的时间取决于多个因素,包括空调的型号、功率、使用环境和维护情况等。一般来说,空调可以连续...
空调哪个符号是除湿的 空调除湿功能通常用一个水滴形状的符号表示。不同品牌和型号的空调遥控器可能会有所差异,但一般来说,除湿...
格力空调多长时间加一次氟利昂 1、一般情况下,空调对氟利昂的使用量是非常小的,所以在空调使用了四年左右的时候再考虑加也没问题。2、...
空调用多长时间需要加氟一次 一般情况下是五年。当出现以下这几种情况时,空调才需要加氟。1、空调的室内机和室外机各有两个接口,当接...
格调生活·C标思享荟:卡萨帝在... 9月12日的郑州龙湖金融岛,夜风轻拂,灯光璀璨。“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑...
品牌价值破千亿、高端份额持续领... 9月12日,“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑州站”在郑州龙湖金融岛举办。2026...
空调主板坏了修800还是换新的 空调主板坏了是修还是换新,需要考虑多个因素,以下是一些建议:1. 维修成本:如果维修费用较低,比如 ...
空调开多久需要休息多长时间 问题:空调开多久需要休息多长时间回答:1、在使用空调的过程中,一般空调开7~8个小时左右需要休息2~...
中央空调多长时间清洗一次 1、一般空调开机前清洗一次即可,空调开机中间时段再清洗一次,空调关机时清洗一次,这样比较合理。2、空...
中央空调氟利昂多久加一次 看空调制热和制冷程度,很多人的观点认为氟利昂必须一年放一次,其实这个观点是错误的,理解上面有点偏差,...