【Android架构】基于MVP模式的Retrofit2+RXjava封装之文件下载(二)

栏目: Java · 发布时间: 6年前

内容简介:上篇中我们介绍了基于MVP的Retrofit2+RXjava封装,这一篇我们来说说文件下载的实现。我们先在ApiServer定义好调用的接口接着定义一个接口,下载成功后用来回调

上篇中我们介绍了基于MVP的Retrofit2+RXjava封装,这一篇我们来说说文件下载的实现。

我们先在ApiServer定义好调用的接口

@GET
    Observable<ResponseBody> downloadFile(@Url String fileUrl);
复制代码

接着定义一个接口,下载成功后用来回调

public interface FileView extends BaseView {

    void onSuccess(File file);
}
复制代码

接着是Observer,建议与处理普通接口的Observer区分处理

public abstract class FileObsever extends BaseObserver<ResponseBody> {
    private String path;

    public FileObsever(BaseView view, String path) {
        super(view);
        this.path = path;
    }

    @Override
    protected void onStart() {
    }

    @Override
    public void onComplete() {
    }

    @Override
    public void onSuccess(ResponseBody o) {

    }

    @Override
    public void onError(String msg) {

    }

    @Override
    public void onNext(ResponseBody o) {
        File file = FileUtil.saveFile(path, o);
        if (file != null && file.exists()) {
            onSuccess(file);
        } else {
            onErrorMsg("file is null or file not exists");
        }
    }

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


    public abstract void onSuccess(File file);

    public abstract void onErrorMsg(String msg);
}

复制代码

FileUtil 注:如果需要写入文件的进度,可以在将这段方法放在onNext中,在FileObsever这个类写个方法,然后回调。

public static File saveFile(String filePath, ResponseBody body) {
        InputStream inputStream = null;
        OutputStream outputStream = null;
        File file = null;
        try {
            if (filePath == null) {
                return null;
            }
            file = new File(filePath);
            if (file == null || !file.exists()) {
                file.createNewFile();
            }


            long fileSize = body.contentLength();
            long fileSizeDownloaded = 0;
            byte[] fileReader = new byte[4096];

            inputStream = body.byteStream();
            outputStream = new FileOutputStream(file);

            while (true) {
                int read = inputStream.read(fileReader);
                if (read == -1) {
                    break;
                }
                outputStream.write(fileReader, 0, read);
                fileSizeDownloaded += read;

            }

            outputStream.flush();


        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return file;
    }
复制代码

下来是FilePresenter

public class FilePresenter extends BasePresenter<FileView> {
    public FilePresenter(FileView baseView) {
        super(baseView);
    }

    public void downFile(String url, final String path) {

        addDisposable(apiServer.downloadFile(url), new FileObsever(baseView, path) {


            @Override
            public void onSuccess(File file) {
                if (file != null && file.exists()) {
                    baseView.onSuccess(file);
                } else {
                    baseView.showError("file is null");
                }
            }

            @Override
            public void onErrorMsg(String msg) {
                baseView.showError(msg);

            }
        });

    }
    }
复制代码

最后在Activity中调用

private void downFile() {
        String url = "http://download.sdk.mob.com/apkbus.apk";

        String state = Environment.getExternalStorageState();
        if (state.equals(Environment.MEDIA_MOUNTED)) {// 检查是否有存储卡
            dir = Environment.getExternalStorageDirectory() + "/ceshi/";
            File dirFile = new File(dir);
            if (!dirFile.exists()) {
                dirFile.mkdirs();
            }
        }
        presenter.downFile(url, dir + "app-debug.apk");
    }
复制代码

就在我以为万事大吉的时候,APP崩溃了,错误信息如下: [图片上传失败...(image-39e2a7-1532052138950)]

原来是加入日志监听器,会导致每次都把整个文件加载到内存,那我们就去掉这个

修改FilePresenter#downFile如下:

public void downFile(String url, final String path) {

        OkHttpClient client = new OkHttpClient.Builder().build();
        Retrofit retrofit = new Retrofit.Builder().client(client)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl("https://wawa-api.vchangyi.com/").build();

        apiServer = retrofit.create(ApiServer.class);

        addDisposable(apiServer.downloadFile(url), new FileObsever(baseView, path) {
            @Override
            public void onSuccess(File file) {
                if (file != null && file.exists()) {
                    baseView.onSuccess(file);
                } else {
                    baseView.showError("file is null");
                }
            }

            @Override
            public void onErrorMsg(String msg) {
                baseView.showError(msg);

            }
        });
    }
复制代码

这次倒是下载成功了,不过官方建议10M以上的文件用Streaming标签,我们加上Streaming标签试试 修改ApiServer

@Streaming
    @GET
    /**
     * 大文件官方建议用 @Streaming 来进行注解,不然会出现IO异常,小文件可以忽略不注入
     */
    Observable<ResponseBody> downloadFile(@Url String fileUrl);
复制代码

这次又崩溃了,错误信息如下: [图片上传失败...(image-fc8c28-1532052138951)]

这是怎么回事,我们网络请求是在子线程啊。无奈之下只得翻翻官方文档,原来使用该注解表示响应用字节流的形式返回.如果没使用该注解,默认会把数据全部载入到内存中。我们可以在主线程中处理写入文件(不建议),但不能在主线程中处理字节流。所以,我们需要将处理字节流、写入文件都放在子线程中。

于是,修改FilePresenter#downFile如下:

OkHttpClient client = new OkHttpClient.Builder().build();
        Retrofit retrofit = new Retrofit.Builder().client(client)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl("https://wawa-api.vchangyi.com/").build();

        apiServer = retrofit.create(ApiServer.class);
          apiServer
                .downloadFile(url)
                .map(new Function<ResponseBody, String>() {
                    @Override
                    public String apply(ResponseBody body) throws Exception {
                        File file = FileUtil.saveFile(path, body);
                        return file.getPath();
                    }
                }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeWith(new FileObserver(baseView) {
                    @Override
                    public void onSuccess(File file) {
                        baseView.onSuccess(file);
                    }

                    @Override
                    public void onError(String msg) {
                        baseView.showError(msg);
                    }
                });
复制代码

这样,下载文件算是完成了,好像还缺点什么?对,缺个下载进度,还记得拦截器吗,我们可以从这里入手:

public class ProgressResponseBody extends ResponseBody {
    private ResponseBody responseBody;
    private BufferedSource bufferedSource;
    private ProgressListener progressListener;

    public ProgressResponseBody(ResponseBody responseBody, ProgressListener progressListener) {
        this.responseBody = responseBody;
        this.progressListener = progressListener;
    }


    @Nullable
    @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;

            @Override
            public long read(Buffer sink, long byteCount) throws IOException {
                long bytesRead = super.read(sink, byteCount);
                totalBytesRead += bytesRead;
                progressListener.onProgress(responseBody.contentLength(), totalBytesRead);
                return bytesRead;
            }
        };
    }

    public interface ProgressListener {
        void onProgress(long totalSize, long downSize);
    }
}

复制代码

在BaseView 中定义接口,个人建议放在BaseView 中,在BaseActivity中实现BaseView,方便复用

/**
     * 下载进度
     *
     * @param totalSize
     * @param downSize
     */

    void onProgress(long totalSize, long downSize);
复制代码

再次修改FilePresenter#downFile如下:

public void downFile(final String url, final String path) {


        OkHttpClient client = new OkHttpClient.Builder()
                .addNetworkInterceptor(new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {
                        Response response = chain.proceed(chain.request());
                        return response.newBuilder().body(new ProgressResponseBody(response.body(),
                                new ProgressResponseBody.ProgressListener() {
                                    @Override
                                    public void onProgress(long totalSize, long downSize) {
                                        baseView.onProgress(totalSize, downSize);
                                    }
                                })).build();
                    }
                }).build();

        Retrofit retrofit = new Retrofit.Builder().client(client)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl("https://wawa-api.vchangyi.com/").build();

        apiServer = retrofit.create(ApiServer.class);

        apiServer
                .downloadFile(url)
                .map(new Function<ResponseBody, String>() {
                    @Override
                    public String apply(ResponseBody body) throws Exception {
                        File file = FileUtil.saveFile(path, body);
                        return file.getPath();
                    }
                }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeWith(new FileObserver(baseView) {
                    @Override
                    public void onSuccess(File file) {
                        baseView.onSuccess(file);
                    }

                    @Override
                    public void onError(String msg) {
                        baseView.showError(msg);
                    }
                });


    }
复制代码

至此,使用Retrofit下载文件暂时告一段落。


以上所述就是小编给大家介绍的《【Android架构】基于MVP模式的Retrofit2+RXjava封装之文件下载(二)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

来吧!带你玩转 Excel VBA

来吧!带你玩转 Excel VBA

罗刚君、杨嘉恺 / 电子工业出版社 / 2013-7 / 85.00元

本书旨在普及Excel VBA 基础理论,以及通过VBA 的高级应用扩展Excel 的功能,提升读者的制表效率,解决工作中的疑难,同时亦可借此开发商业插件。 本书主要分为操作自动化引言篇、入门篇、进阶篇和疑难解答篇,覆盖从入门到提高的所有内容,以满足不同层次的读者需求。其中操作自动化引言篇简述了操作自动化的需求与方式,借此引出VBA 入门篇。VBA 入门篇包含第2 章到第13 章,主要介绍了......一起来看看 《来吧!带你玩转 Excel VBA》 这本书的介绍吧!

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具