服务器之家

服务器之家 > 正文

基于Ok+Rxjava+retrofit实现断点续传下载

时间:2019-06-25 19:41     来源/作者:Super_Ks

上一篇博客中介绍了基于Ok+Rxjava实现断点续传下载,这一篇给大家介绍下基于Ok+Rxjava+Retrofit实现断点续传下载,demo下载地址,效果图跟上一篇图片一样,哈哈

基于Ok+Rxjava+retrofit实现断点续传下载

 说下我的大致思路吧(跟上一篇略有不同):根据文件下载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

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

相关文章

热门资讯

叠猫猫队长可以退出吗 叠猫猫队长怎么换队伍
叠猫猫队长可以退出吗 叠猫猫队长怎么换队伍 2019-06-05
抖音撒撒累累是什么歌 撒撒累累张艺兴歌曲名字
抖音撒撒累累是什么歌 撒撒累累张艺兴歌曲名字 2019-06-05
淘宝合合卡可以送好友吗 淘宝合合卡快速获得方式
淘宝合合卡可以送好友吗 淘宝合合卡快速获得方式 2019-06-13
叠猫猫可以邀请多少人 叠猫猫大挑战一个队最多多少人
叠猫猫可以邀请多少人 叠猫猫大挑战一个队最多多少人 2019-06-05
淘宝618叠猫猫怎么退出队伍?每天可退出几次队伍
淘宝618叠猫猫怎么退出队伍?每天可退出几次队伍 2019-06-06
返回顶部

701
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40