基于RxJava2.0+Retrofit2.0的文件下载实现(带进度,非覆写ResponseBody和拦截器)

基于RxJava2.0+Retrofit2.0的文件下载实现(带进度,非覆写ResponseBody和拦截器)

前言

本文主要讲解RxJava2.0+Retrofit2.0实现下载文件并带进度效果,如果按照传统方法是很容易实现的。但是,发现网上搜索的例子都是通过OkHttpClient的拦截器去拦截Response来实现进度显示(侵入性有点强),个人发现bug不少,问题都是在UI更新方面出了问题,只要记住UI刷新在主线程更新都容易解决,下面介绍两种非修改拦截器实现文件下载的方法:

效果图 

基于RxJava2.0+Retrofit2.0的文件下载实现(带进度,非覆写ResponseBody和拦截器)_第1张图片

图中下载速度较快,是基于在公司专线环境测试

依赖添加

 /*Retrofit是一款类型安全的网络框架,基于HTTP协议,服务于Android和java语言,集成了okhttp依赖*/
    compile 'com.squareup.retrofit2:retrofit:2.3.0'
    compile 'com.squareup.retrofit2:converter-gson:2.3.0'
    compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'

    /*RxAndroid一款Android客户端组件间异步通信的框架,1和2差别很大*/
    compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
    compile 'io.reactivex.rxjava2:rxjava:2.1.8'

具体实现 

 方法1:使用Handler更新下载进度

/**
     * 下载文件法1(使用Handler更新UI)
     *
     * @param observable      下载被观察者
     * @param destDir         下载目录
     * @param fileName        文件名
     * @param progressHandler 进度handler
     */
    public static void downloadFile(Observable observable, final String destDir, final String fileName, final DownloadProgressHandler progressHandler) {
        final DownloadInfo downloadInfo = new DownloadInfo();
        observable
                .subscribeOn(Schedulers.io())
                .observeOn(Schedulers.io())
                .subscribe(new Observer() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        addDisposable(d);
                    }

                    @Override
                    public void onNext(ResponseBody responseBody) {
                        InputStream inputStream = null;
                        long total = 0;
                        long responseLength;
                        FileOutputStream fos = null;
                        try {
                            byte[] buf = new byte[2048];
                            int len;
                            responseLength = responseBody.contentLength();
                            inputStream = responseBody.byteStream();
                            final File file = new File(destDir, fileName);
                            downloadInfo.setFile(file);
                            downloadInfo.setFileSize(responseLength);
                            File dir = new File(destDir);
                            if (!dir.exists()) {
                                dir.mkdirs();
                            }
                            fos = new FileOutputStream(file);
                            int progress = 0;
                            int lastProgress;
                            long startTime = System.currentTimeMillis(); // 开始下载时获取开始时间
                            while ((len = inputStream.read(buf)) != -1) {
                                fos.write(buf, 0, len);
                                total += len;
                                lastProgress = progress;
                                progress = (int) (total * 100 / responseLength);
                                long curTime = System.currentTimeMillis();
                                long usedTime = (curTime - startTime) / 1000;
                                if (usedTime == 0) {
                                    usedTime = 1;
                                }
                                long speed = (total / usedTime); // 平均每秒下载速度
                                // 如果进度与之前进度相等,则不更新,如果更新太频繁,则会造成界面卡顿
                                if (progress > 0 && progress != lastProgress) {
                                    downloadInfo.setSpeed(speed);
                                    downloadInfo.setProgress(progress);
                                    downloadInfo.setCurrentSize(total);
                                    progressHandler.sendMessage(DownloadProgressHandler.DOWNLOAD_PROGRESS, downloadInfo);
                                }
                            }
                            fos.flush();
                            downloadInfo.setFile(file);
                            progressHandler.sendMessage(DownloadProgressHandler.DOWNLOAD_SUCCESS, downloadInfo);

                        } catch (final Exception e) {
                            downloadInfo.setErrorMsg(e);
                            progressHandler.sendMessage(DownloadProgressHandler.DOWNLOAD_FAIL, downloadInfo);
                        } finally {
                            try {
                                if (fos != null) {
                                    fos.close();
                                }
                                if (inputStream != null) {
                                    inputStream.close();
                                }
                            } catch (Exception e) {
                                e.printStackTrace();
                            }

                        }
                    }

                    @Override
                    public void onError(Throwable e) {//new Consumer
                        downloadInfo.setErrorMsg(e);
                        progressHandler.sendMessage(DownloadProgressHandler.DOWNLOAD_FAIL, downloadInfo);

                    }

                    @Override
                    public void onComplete() {// new Action()

                    }
                });
    }

 

方法2:使用RxJava发射器更新下载进度 

 /**
     * 下载文件法2(使用RXJava更新UI)
     *
     * @param observable
     * @param destDir
     * @param fileName
     * @param progressHandler
     */
    public static void downloadFile2(Observable observable, final String destDir, final String fileName, final DownloadProgressHandler progressHandler) {
        final DownloadInfo downloadInfo = new DownloadInfo();
        observable
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .flatMap(new Function>() {

                    @Override
                    public ObservableSource apply(final ResponseBody responseBody) throws Exception {

                        return Observable.create(new ObservableOnSubscribe() {
                            @Override
                            public void subscribe(ObservableEmitter emitter) throws Exception {
                                InputStream inputStream = null;
                                long total = 0;
                                long responseLength = 0;
                                FileOutputStream fos = null;
                                try {
                                    byte[] buf = new byte[2048];
                                    int len = 0;
                                    responseLength = responseBody.contentLength();
                                    inputStream = responseBody.byteStream();
                                    final File file = new File(destDir, fileName);
                                    downloadInfo.setFile(file);
                                    downloadInfo.setFileSize(responseLength);
                                    File dir = new File(destDir);
                                    if (!dir.exists()) {
                                        dir.mkdirs();
                                    }
                                    fos = new FileOutputStream(file);
                                    int progress = 0;
                                    int lastProgress = 0;
                                    long startTime = System.currentTimeMillis(); // 开始下载时获取开始时间
                                    while ((len = inputStream.read(buf)) != -1) {
                                        fos.write(buf, 0, len);
                                        total += len;
                                        lastProgress = progress;
                                        progress = (int) (total * 100 / responseLength);
                                        long curTime = System.currentTimeMillis();
                                        long usedTime = (curTime - startTime) / 1000;
                                        if (usedTime == 0) {
                                            usedTime = 1;
                                        }
                                        long speed = (total / usedTime); // 平均每秒下载速度
                                        // 如果进度与之前进度相等,则不更新,如果更新太频繁,则会造成界面卡顿
                                        if (progress > 0 && progress != lastProgress) {
                                            downloadInfo.setSpeed(speed);
                                            downloadInfo.setProgress(progress);
                                            downloadInfo.setCurrentSize(total);
                                            emitter.onNext(downloadInfo);
                                        }
                                    }
                                    fos.flush();
                                    downloadInfo.setFile(file);
                                    emitter.onComplete();
                                } catch (Exception e) {
                                    downloadInfo.setErrorMsg(e);
                                    emitter.onError(e);
                                } finally {
                                    try {
                                        if (fos != null) {
                                            fos.close();
                                        }
                                        if (inputStream != null) {
                                            inputStream.close();
                                        }
                                    } catch (Exception e) {
                                        e.printStackTrace();
                                    }

                                }
                            }
                        });
                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        addDisposable(d);
                    }

                    @Override
                    public void onNext(DownloadInfo downloadInfo) {
                        progressHandler.onProgress(downloadInfo.getProgress(), downloadInfo.getFileSize(), downloadInfo.getSpeed());
                    }

                    @Override
                    public void onError(Throwable e) {
                        progressHandler.onError(e);
                    }

                    @Override
                    public void onComplete() {
                        LogUtils.i("下载完成");
                        progressHandler.onCompleted(downloadInfo.getFile());
                    }
                });
    }

相关代码:

api服务接口类

DownloadApi.java

public interface DownloadApi {


    /**
     * 下载Apk1文件
     *
     */
    @Streaming
    @GET("imtt.dd.qq.com/16891/C527A902F14C1FFD8AA9C13872D5F92F.apk?mkey=5c41136cb711c35d&f=0c2f&fsname=com.tencent.moyu_1.4.0_1.apk&csr=1bbd&cip=183.17.229.168&proto=https")
    Observable downloadApkFile1();

    /**
     * 下载Apk2文件
     *
     */
    @Streaming
    @GET("https://cc849cacb0e96648f8dd4bb35ff8365b.dd.cdntips.com/imtt.dd.qq.com/16891/5BB89032B0755F5922C80DA8C2CAF735.apk?mkey=5c415b9fb711c35d&f=07b4&fsname=com.tencent.mobileqq_7.9.7_994.apk&csr=1bbd&cip=183.17.229.168&proto=https")
    Observable downloadApkFile2();

    /**
     * 下载Apk3文件
     *
     */
    @Streaming
    @GET("https://cc849cacb0e96648f8dd4bb35ff8365b.dd.cdntips.com/imtt.dd.qq.com/16891/BEC5EEF53983300D9F0AB46166EC9EA7.apk?mkey=5c41a20bda11e60f&f=184b&fsname=com.tencent.pao_1.0.61.0_161.apk&csr=1bbd&cip=218.17.192.250&proto=https")
    Observable downloadApkFile3();
}

下载进度Handler类

DownloadProgressHandler.java 

/**
 * 下载进度Handler
 *
 * @author Kelly
 * @version 1.0.0
 * @filename DownloadProgressHandler.java
 * @time 2018/7/25 15:25
 * @copyright(C) 2018 song
 */
public abstract class DownloadProgressHandler implements DownloadCallBack {
    public static final int DOWNLOAD_SUCCESS = 0;
    public static final int DOWNLOAD_PROGRESS = 1;
    public static final int DOWNLOAD_FAIL = 2;

    protected ResponseHandler mHandler = new ResponseHandler(this, Looper.getMainLooper());

    /**
     * 发送消息,更新进度
     *
     * @param what
     * @param downloadInfo
     */
    public void sendMessage(int what, DownloadInfo downloadInfo) {
        mHandler.obtainMessage(what, downloadInfo).sendToTarget();
    }


    /**
     * 处理消息
     * @param message
     */
    protected void handleMessage(Message message) {
        DownloadInfo progressBean = (DownloadInfo) message.obj;
        switch (message.what) {
            case DOWNLOAD_SUCCESS://下载成功
                onCompleted(progressBean.getFile());
                removeMessage();
                break;
            case DOWNLOAD_PROGRESS://下载中
                onProgress(progressBean.getProgress(), progressBean.getFileSize(),progressBean.getSpeed());
                break;
            case DOWNLOAD_FAIL://下载失败
                onError(progressBean.getErrorMsg());
                break;
            default:
                removeMessage();
                break;
        }
    }

    private void removeMessage() {
        if (mHandler != null){
            mHandler.removeCallbacksAndMessages(null);
        }
    }


    protected static class ResponseHandler extends Handler {

        private DownloadProgressHandler mProgressHandler;

        public ResponseHandler(DownloadProgressHandler mProgressHandler, Looper looper) {
            super(looper);
            this.mProgressHandler = mProgressHandler;
        }

        @Override
        public void handleMessage(Message msg) {
            mProgressHandler.handleMessage(msg);
        }
    }
}

 

 DownloadCallBack.java

/**
 * 下载回调
 */

public interface DownloadCallBack {

    /**
     * 进度,运行在主线程
     *
     * @param progress 下载进度
     * @param total 总大小
     * @param speed 下载速率
     */
    void onProgress(int progress, long total,long speed);

    /**
     * 运行在主线程
     *
     * @param file
     */
    void onCompleted(File file);

    /**
     * 运行在主线程
     *
     * @param e
     */
    void onError(Throwable e);

}

文件下载信息类

DownloadInfo.java

/**
 * 下载文件信息
 *
 * @author Kelly
 * @version 1.0.0
 * @filename DownloadInfo.java
 * @time 2018/7/25 14:27
 * @copyright(C) 2018 song
 */
public class DownloadInfo {
    private File file;
    private String fileName;
    private long fileSize;//单位 byte
    private long currentSize;//当前下载大小
    private int progress;//当前下载进度
    private long speed;//下载速率
    private Throwable errorMsg;//下载异常信息


    public File getFile() {
        return file;
    }

    public void setFile(File file) {
        this.file = file;
    }

    public String getFileName() {
        return fileName;
    }

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    public long getFileSize() {
        return fileSize;
    }

    public void setFileSize(long fileSize) {
        this.fileSize = fileSize;
    }

    public long getCurrentSize() {
        return currentSize;
    }

    public void setCurrentSize(long currentSize) {
        this.currentSize = currentSize;
    }

    public int getProgress() {
        return progress;
    }

    public void setProgress(int progress) {
        this.progress = progress;
    }

    public long getSpeed() {
        return speed;
    }

    public void setSpeed(long speed) {
        this.speed = speed;
    }

    public Throwable getErrorMsg() {
        return errorMsg;
    }

    public void setErrorMsg(Throwable errorMsg) {
        this.errorMsg = errorMsg;
    }
}

调用方法

 DownloadApi apiService = RetrofitHelper.getInstance().getApiService(DownloadApi.class);

        FileDownloader.downloadFile(apiService.downloadApkFile1(), DOWNLOAD_APK_PATH, "test.apk", new DownloadProgressHandler() {


            @Override
            public void onProgress(int progress, long total, long speed) {
                LogUtils.i("progress:" + progress + ",speed:" + speed);
                mProgress.setText(progress + "%");
                mFileSize.setText(FileUtils.formatFileSize(total));
                mRate.setText(FileUtils.formatFileSize(speed)+"/s");
            }

            @Override
            public void onCompleted(File file) {
                LogUtils.i("下载apk文件成功");
                FileDownloader.clear();
            }

            @Override
            public void onError(Throwable e) {
                LogUtils.e("下载apk文件异常", e);
                FileDownloader.clear();
            }
        });

下载地址

https://download.csdn.net/download/u011082160/11261106

你可能感兴趣的:(Android基础知识)