基于Ok+Rxjava+retrofit实现断点续传下载
作者:Super_Ks 发布时间:2021-08-27 02:21:49
本文为大家分享了实现断点续传下载的具体代码,供大家参考,具体内容如下
1、基于Ok+Rxjava实现断点续传下载
2、基于Ok+Rxjava+Retrofit实现断点续传下载
上一篇博客中介绍了基于Ok+Rxjava实现断点续传下载,这一篇给大家介绍下基于Ok+Rxjava+Retrofit实现断点续传下载,demo下载地址,效果图跟上一篇图片一样,哈哈
说下我的大致思路吧(跟上一篇略有不同):根据文件下载url按照自己定义的规则生成文件名,判断本地同路径下是否存在此文件,如果存在,文件大小与服务器上获取的文件大小一致的情况下,则覆盖本地文件重新下载;如果文件比服务器获取的文件大小小,则执行断点下载,从本地文件长度处开始下载。如果文件不存在,则从0字节开始下载。
还有的不同是,这里需要重新ResponseBody的source()方法,在这里监听文件下载的进度,然后通过我么自定义的Downloadinterceptor把我们重新的DownloadResponseBody给设置进去,从而完成我们的进度监听工作。
下面还是上主要代码:
首先重写ResponseBody
public class DownloadResponseBody extends ResponseBody {
private ResponseBody responseBody;
//进度回调接口
private DownFileCallback downFileCallback;
private BufferedSource bufferedSource;
private String downUrl;
public DownloadResponseBody(ResponseBody responseBody, DownFileCallback downFileCallback, String downUrl) {
this.responseBody = responseBody;
this.downFileCallback = downFileCallback;
this.downUrl = downUrl;
}
@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;
File file = new File(DownloadManager.getInstance().getTemporaryName(downUrl));
@Override
public long read(Buffer sink, long byteCount) throws IOException {
long bytesRead = super.read(sink, byteCount);
totalBytesRead += bytesRead != -1 ? bytesRead : 0;
if (null != downFileCallback) {
if (bytesRead != -1) {
long loacalSize = file.length();//本地已下载的长度
long trueTotal = loacalSize + responseBody.contentLength() - totalBytesRead;//文件真实长度
downFileCallback.onProgress(trueTotal,loacalSize);
} else {
}
}
return bytesRead;
}
};
}
}
重写Interceptor
public class Downloadinterceptor implements Interceptor {
private DownFileCallback downFileCallback;
private String downUrl;
public Downloadinterceptor(DownFileCallback listener,String downUrl) {
this.downFileCallback = listener;
this.downUrl = downUrl;
}
@Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
return response.newBuilder()
.body(new DownloadResponseBody(response.body(), downFileCallback,downUrl))
.build();
}
}
然后我们的service
public interface HttpService {
/*大文件需要加入Streaming这个判断,防止下载过程中写入到内存中,造成oom*/
@Streaming
@GET
Observable<ResponseBody> download(@Header("range") String start, @Url String url);
}
接下来我们的DownloadManager中download方法
/**
* 开始下载
* @param url 下载地址
* @param downFileCallback 进度回调接口
*/
public void download(final String url, final DownFileCallback downFileCallback) {
/*正在下载不处理*/
if (url == null || submap.get(url) != null) {
return;
}
Downloadinterceptor interceptor = new Downloadinterceptor(downFileCallback, url);
okHttpClient = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.build();
Retrofit retrofit = new Retrofit.Builder()
.client(okHttpClient)
.baseUrl("http://imtt.dd.qq.com")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
final HttpService httpservice = retrofit.create(HttpService.class);
ProgressDownSubscriber subscriber =
Observable.just(url)
.flatMap(new Function<String, ObservableSource<DownloadInfo>>() {
@Override
public ObservableSource<DownloadInfo> apply(String s) throws Exception {
return Observable.just(createDownInfo(s));
}
})
.map(new Function<DownloadInfo, DownloadInfo>() {
@Override
public DownloadInfo apply(DownloadInfo s) throws Exception {
return getRealFileName(s);
}
})
.flatMap(new Function<DownloadInfo, Observable<ResponseBody>>() {
@Override
public Observable<ResponseBody> apply(DownloadInfo downInfo) throws Exception {
return httpservice.download("bytes=" + downInfo.getProgress() + "-", downInfo.getUrl());
}
})//下载
.map(new Function<ResponseBody, DownloadInfo>() {
@Override
public DownloadInfo apply(ResponseBody responsebody) {
try {
return writecache(responsebody, url);
} catch (IOException e) {
//*失败抛出异常*//
e.printStackTrace();
}
return null;
}
})
.observeOn(AndroidSchedulers.mainThread())//在主线程回调
.subscribeOn(Schedulers.io())//在子线程执行
.subscribeWith(new ProgressDownSubscriber<DownloadInfo>() {
@Override
public void onNext(DownloadInfo downInfo) {
downFileCallback.onSuccess(downInfo);
submap.remove(downInfo.getUrl());
}
@Override
public void onError(Throwable t) {
downFileCallback.onFail(t.getMessage());
submap.remove(url);
}
});
submap.put(url, subscriber);
}
然后暂停操作:
/**
* 暂停下载
*/
public void stop(String url) {
if (url == null) return;
if (submap.containsKey(url)) {
ProgressDownSubscriber subscriber = submap.get(url);
subscriber.dispose();
submap.remove(url);
}
}
从服务器获取文件长度
/**
* 从服务器获取文件长度
*
* @param downloadUrl
* @return
*/
private long getContentLength(String downloadUrl) {
Request request = new Request.Builder()
.url(downloadUrl)
.build();
try {
Response response = mClient.newCall(request).execute();
if (response != null && response.isSuccessful()) {
long contentLength = response.body().contentLength();
response.close();
return contentLength == 0 ? DownloadInfo.TOTAL_ERROR : contentLength;
}
} catch (IOException e) {
e.printStackTrace();
}
return DownloadInfo.TOTAL_ERROR;
}
从服务器获取文件长度的时候注意一下,Android P之后,也就是api 28以上禁止明文网络传输。需要在你的AndroidManifest中的application标签中声明"android:usesCleartextTraffic="true",允许应用进行明文传输。
使用方法:首先要获取sd卡权限
DownloadManager.getInstance().downloadPath(本地存放地址).download(url1, new DownFileCallback() {
@Override
public void onSuccess(DownloadInfo info) {
Toast.makeText(MainActivity.this, url1 + "下载完成", Toast.LENGTH_SHORT).show();
}
@Override
public void onFail(String msg) {
Toast.makeText(MainActivity.this, url1 + "下载失败", Toast.LENGTH_SHORT).show();
}
@Override
public void onProgress(final long totalSize, final long downSize) {
// 需要注意的是,如果文件总大小为50M,已下载的大小为10M,
// 再次下载时onProgress返回的totalSize是文件总长度
// 减去 已下载大小 10M, 即40M,downSize为本次下载的已下载量
// 好消息是,我已经在内部做过处理,放心使用吧,但是这个问题大家还是要知道的
runOnUiThread(new Runnable() {
@Override
public void run() {
int progress = (int) (downSize * 100 / totalSize);
progress1.setProgress(progress);
}
});
}
});
好了今天就到这里,希望能帮到大家,这对我来说也是一种加深印象的笔记。
demo下载地址
github地址:DownManager 欢迎star
来源:https://blog.csdn.net/zlzlzl1988/article/details/90634279
猜你喜欢
- 用VS2010 + OpenCV 2.4.9 实现简单人脸识别,供大家参考,具体内容如下首先放效果图(为了防止辣眼睛,后期处理了下):首先声
- 成员类型访问权限低于字段本身现在假设你有一个小兵类,他的访问权限是仅限当前程序集。internal class 小兵{public int
- 格式要求:SU MO TU WE TH FR SA &nb
- 本文实例为大家分享了Java操作MongoDB模糊查询和分页查询,供大家参考,具体内容如下模糊查询条件:1、完全匹配Pattern patt
- 本文实例讲述了C#远程获取图片文件流的方法。分享给大家供大家参考,具体如下:protected void Page_Load(object
- 动态 sql 简单来讲就是我们能通过条件的设置生成不同的 sql,MyBatis 中常用的动态 sql 表达式主要是有五种:ifchoose
- 在 C# 9 中,foreach 循环可以使用扩展方法。在本文中,我们将通过例子回顾 C# 9 中如何扩展 foreach 循环。代码演示下
- 1)打开idea,开始创建SpringBoot项目2)选择 Spring Initializr ,选择合适的jdk版本,点击Next在操作到
- 引言在平时的工作中多多少少都会接触到注册中心,当你的应用从单机到拆分成多个服务,每个服务又有多个实例的情况时,那么对服务IP地址管理的要求就
- 哈希表(HashMap)hash查询的时间复杂度是O(1)按值传递Character,Short,Integer,Long, Float,D
- SpringBoot项目中新增脱敏功能项目背景目前正在开发一个SpringBoot项目,此项目有Web端和微信小程序端。web端提供给工作人
- Gradle 属性( Gradle build environment)[详细信息]("https://docs.gradle.o
- maven插件主要是为maven中生命周期中的阶段服务的,maven中只是定义了3套生命周期,以及每套生命周期中有哪些阶段,具体每个阶段中执
- fifter、servlet、interceptorfifter用来处理请求头、请求参数、编码的一些设置,然后转交给servlet,处理业务
- 延迟加载1 使用延迟加载意义在进行数据查询时,为了提高数据库查询性能,尽量使用单表查询,因为单表查询比多表关联查询速度要快。如果查询单表就可
- 本文实例讲述了C#实现图形位置组合转换的方法。分享给大家供大家参考。具体实现方法如下:using System;using System.C
- 微软官方的MSDN上说async和await是“异步”,但是不少人(包括笔者自己)都有一些误区需要澄清:为什么await语句之后没有执行?不
- 引言Spring Boot的一个便捷功能是外部化配置,可以轻松访问属性文件中定义的属性。本文将详细介绍@ConfigurationPrope
- 英文设置加粗可以在xml里面设置: <SPAN style="FONT-SIZE: 18px">androi
- 一.前言Unity3D是如今最火爆的游戏开发引擎,它可以让我们能轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型的互动内容。它支持2