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

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

2、基于Ok+Rxjava+Retrofit实现断点续传下载

上一篇博客中介绍了基于Ok+Rxjava实现断点续传下载,这一篇给大家介绍下基于Ok+Rxjava+Retrofit实现断点续传下载,

demo下载地址,效果图跟上一篇图片一样,哈哈

                                                             基于Ok+Rxjava+retrofit 实现断点续传下载_第1张图片

 说下我的大致思路吧(跟上一篇略有不同):根据文件下载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 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>() {
                            @Override
                            public ObservableSource apply(String s) throws Exception {
                                return Observable.just(createDownInfo(s));
                            }
                        })
                        .map(new Function() {
                            @Override
                            public DownloadInfo apply(DownloadInfo s) throws Exception {
                                return getRealFileName(s);
                            }
                        })
                        .flatMap(new Function>() {
                            @Override
                            public Observable apply(DownloadInfo downInfo) throws Exception {
                                return httpservice.download("bytes=" + downInfo.getProgress() + "-", downInfo.getUrl());
                            }
                        })//下载
                        .map(new Function() {
                            @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() {
                            @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地址:https://github.com/SuperDearKs/DownManager 欢迎star

你可能感兴趣的:(基于Ok+Rxjava+retrofit 实现断点续传下载)