美文网首页安卓开发博客Android技术知识Android开发经验谈
Android 史上最优雅的实现文件上传、下载及进度的监听

Android 史上最优雅的实现文件上传、下载及进度的监听

作者: 不怕天黑_0326 | 来源:发表于2019-08-02 09:34 被阅读29次

    本文已授权「刘望舒」微信公众号独家原创发布

    前言

    本文将直接使用RxHttp库实现文件上传、下载、断点下载、进度的监听,不对RxHttp做过多讲解,如果对RxHttp不了解,请移步

    RxHttp 一条链发送请求,新一代Http请求神器(一)

    RxHttp 一条链发送请求之强大的数据解析功能(二)

    RxHttp 一条链发送请求之强大的Param类(三)

    RxHttp 一条链发送请求之注解处理器 Generated API(四)

    本文目的在于让更多的读者知道RxHttp库,如果您已阅读上面4篇文章,本文可直接跳过,感谢你的支持。🙏🙏。

    上传

      RxHttp.postForm("http://...") //发送Form表单形式的Post请求
            .add("key", "value")
            .add("file1", new File("xxx/1.png")) //添加file对象
            .add("file2", new File("xxx/2.png"))
            .asString() //asXXX操作符,是异步操作
            .as(RxLife.asOnMain(this))  //感知生命周期,并在主线程回调
            .subscribe(s -> { 
                //上传成功,拿到Http返回值,这里返回值为String类型
            }, throwable -> {
                //上传失败
            });
    

    注:如果需要对Http的返回值做解析,可在使用asParser操作符时,传入一个解析器Parser

    带进度上传

    带进度上传使用uploadProgress操作符,并结合doOnNextfiltermap即可

      RxHttp.postForm("http://www.......") //发送Form表单形式的Post请求
            .add("file1", new File("xxx/1.png"))
            .add("file2", new File("xxx/2.png"))
            .add("key1", "value1")//添加参数,非必须
            .add("key2", "value2")//添加参数,非必须
            .addHeader("versionCode", "100") //添加请求头,非必须
            .asUploadProgress() //注:如果需要监听上传进度,使用asUploadProgress操作符
            .observeOn(AndroidSchedulers.mainThread()) //主线程回调
            .doOnNext(progress -> {
                //上传进度回调,0-100,仅在进度有更新时才会回调,最多回调101次,最后一次回调Http执行结果
                int currentProgress = progress.getProgress(); //当前进度 0-100
                long currentSize = progress.getCurrentSize(); //当前已上传的字节大小
                long totalSize = progress.getTotalSize();     //要上传的总字节大小
                String result = progress.getResult(); //Http执行结果,最后一次回调才有内容
            })
            .filter(Progress::isCompleted)//过滤事件,上传完成,才继续往下走
            .map(Progress::getResult) //到这,说明上传完成,拿到Http返回结果并继续往下走
            .as(RxLife.as(this))  //感知生命周期
            .subscribe(s -> { //s为String类型,由SimpleParser类里面的泛型决定的
                //上传成功,处理相关逻辑
            }, throwable -> {
                //上传失败,处理相关逻辑
            });
    

    注:如果需要对Http的返回值做解析,可在使用uploadProgress操作符时,传入一个解析器Parser

    下载

      //文件存储路径
      String destPath = getExternalCacheDir() + "/" + System.currentTimeMillis() + ".apk";
      RxHttp.get("http://update.9158.com/miaolive/Miaolive.apk")
            .asDownload(destPath) //注意这里使用asDownload操作符,并传入本地路径
            .as(RxLife.asOnMain(this))  //感知生命周期,并在主线程回调
            .subscribe(s -> {
                //下载成功,回调文件下载路径
            }, throwable -> {
                //下载失败
            });
    

    带进度下载

    带进度下载使用downloadProgress操作符,并结合doOnNextfiltermap即可

      //文件存储路径
      String destPath = getExternalCacheDir() + "/" + System.currentTimeMillis() + ".apk";
      RxHttp.get("http://update.9158.com/miaolive/Miaolive.apk")
            .asDownloadProgress(destPath) //注:如果需要监听下载进度,使用asDownloadProgress操作符
            .observeOn(AndroidSchedulers.mainThread())
            .doOnNext(progress -> {
                //下载进度回调,0-100,仅在进度有更新时才会回调,最多回调101次,最后一次回调文件存储路径
                int currentProgress = progress.getProgress(); //当前进度 0-100
                long currentSize = progress.getCurrentSize(); //当前已下载的字节大小
                long totalSize = progress.getTotalSize();     //要下载的总字节大小
                String filePath = progress.getResult(); //文件存储路径,最后一次回调才有内容
            })
            .filter(Progress::isCompleted)//下载完成,才继续往下走
            .map(Progress::getResult) //到这,说明下载完成,返回下载目标路径
            .as(RxLife.as(this)) //感知生命周期
            .subscribe(s -> {//s为String类型,这里为文件存储路径
                //下载完成,处理相关逻辑
            }, throwable -> {
                //下载失败,处理相关逻辑
            });
    

    断点下载

    断点下载相较于下载,仅需要调用setRangeHeader方法传入开始及结束位置即可(结束位置不传默认为文件末尾),其它没有任何差别

      String destPath = getExternalCacheDir() + "/" + "Miaobo.apk";
      long length = new File(destPath).length(); //已下载的文件长度
      RxHttp.get("http://update.9158.com/miaolive/Miaolive.apk")
            .setRangeHeader(length)  //设置开始下载位置,结束位置默认为文件末尾
            .asDownload(destPath)
            .as(RxLife.asOnMain(this)) //加入感知生命周期的观察者
            .subscribe(s -> { //s为String类型
                Log.e("LJX", "breakpointDownloadAndProgress=" + s);
                //下载成功,处理相关逻辑
            }, throwable -> {
                //下载失败,处理相关逻辑
            });
    

    带进度断点下载

    带进度断点下载相较于带进度下载仅需要调用setRangeHeader方法传入开始及结束位置即可(结束位置不传默认为文件末尾),其它没有任何差别

      String destPath = getExternalCacheDir() + "/" + "Miaobo.apk";
      long length = new File(destPath).length(); //已下载的文件长度
      RxHttp.get("http://update.9158.com/miaolive/Miaolive.apk")
            .setRangeHeader(length)  //设置开始下载位置,结束位置默认为文件末尾
            .asDownloadProgress(destPath)
            .observeOn(AndroidSchedulers.mainThread()) //主线程回调
            .doOnNext(progress -> {
                //下载进度回调,0-100,仅在进度有更新时才会回调
                int currentProgress = progress.getProgress(); //当前进度 0-100
                long currentSize = progress.getCurrentSize(); //当前已下载的字节大小
                long totalSize = progress.getTotalSize();     //要下载的总字节大小
            })
            .filter(Progress::isCompleted)//过滤事件,下载完成,才继续往下走
            .map(Progress::getResult) //到这,说明下载完成,拿到Http返回结果并继续往下走
            .as(RxLife.as(this)) //加入感知生命周期的观察者
            .subscribe(s -> { //s为String类型
                //下载成功,处理相关逻辑
            }, throwable -> {
                //下载失败,处理相关逻辑
            });
    

    注:上面带进度断点下载中,返回的进度会从0开始,如果需要衔接上次下载的进度,则可以在downloadProgress操作符中再传入上次已经下载好的长度,如下:

      String destPath = getExternalCacheDir() + "/" + "Miaobo.apk";
      long length = new File(destPath).length(); //已下载的文件长度
      RxHttp.get("http://update.9158.com/miaolive/Miaolive.apk")
            .setRangeHeader(length)  //设置开始下载位置,结束位置默认为文件末尾
            .asDownloadProgress(destPath, length)
            .observeOn(AndroidSchedulers.mainThread()) //主线程回调
            .doOnNext(progress -> {
                //下载进度回调,0-100,仅在进度有更新时才会回调
                int currentProgress = progress.getProgress(); //当前进度 0-100
                long currentSize = progress.getCurrentSize(); //当前已下载的字节大小
                long totalSize = progress.getTotalSize();     //要下载的总字节大小
            })
            .filter(Progress::isCompleted)//过滤事件,下载完成,才继续往下走
            .map(Progress::getResult) //到这,说明下载完成,拿到Http返回结果并继续往下走
            .as(RxLife.as(this)) //加入感知生命周期的观察者
            .subscribe(s -> { //s为String类型
                //下载成功,处理相关逻辑
            }, throwable -> {
                //下载失败,处理相关逻辑
            });
    

    多任务下载

    多任务下载我们可以使用RxJava的merge操作符,如下:

    List<Observable<String>> downList = new ArrayList<>();
    for (int i = 0; i < 3; i++) {
        String destPath = getExternalCacheDir() + "/" + i + ".apk";
        String url = "http://update.9158.com/miaolive/Miaolive.apk"
        Observable<String> down = RxHttp.get(url)
                .asDownload(destPath);
        downList.add(down);
    }
    
    //通过RxJava内部线程池,多任务并行下载
    Observable.merge(downList)
            .as(RxLife.as(this))
            .subscribe(s -> {
                //单个任务下载完成
            }, throwable -> {
                //下载出错
            }, () -> {
                //所有任务下载完成
            });
    
    

    如果想监听每个任务的下载进度,也简单,用老方法即可,如下:

    List<Observable<String>> downList = new ArrayList<>();
    for (int i = 0; i < 3; i++) {
        String destPath = getExternalCacheDir() + "/" + i + ".apk";
        String url = "http://update.9158.com/miaolive/Miaolive.apk"
        Observable<String> down = RxHttp.get(url)
                .asDownloadProgress(destPath)//注意这里使用DownloadParser解析器,并传入本地路径
                .observeOn(AndroidSchedulers.mainThread())
                .doOnNext(progress -> {
                    //单个下载任务进度回调
                })
                .filter(Progress::isCompleted)//过滤事件,下载完成,才继续往下走
                .map(Progress::getResult);//到这,说明下载完成,拿到Http返回结果并继续往下走
        downList.add(down);
    }
    
    //通过RxJava内部线程池,多任务并行下载
    Observable.merge(downList)
            .as(RxLife.as(this))
            .subscribe(s -> {
                //单个任务下载完成
            }, throwable -> {
                //下载出错
            }, () -> {
                //所有任务下载完成
            });
    
    

    多任务上传

    与多任务下载同理,不再讲述。

    小结

    好了,文件上传、下载相关就介绍到这里了,到这你会发现,涉及到进度的监听,都使用了RxJava的doOnNextfiltermap这3个操作符,一切都那么的相似,极大的降低了学习成本。怎么样?是不是很优雅,欢迎打脸!!

    最后,很大一部分功劳都要归功于RxJava的强大,感谢RxJava,向它致敬!!!!

    转载请注明出处,谢谢🙏

    相关文章

      网友评论

        本文标题:Android 史上最优雅的实现文件上传、下载及进度的监听

        本文链接:https://www.haomeiwen.com/subject/zeskdctx.html