继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

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

波斯汪
关注TA
已关注
手记 502
粉丝 67
获赞 430

上篇中我们介绍了基于MVP的Retrofit2+RXjava封装,还没有看的

上篇中我们介绍了基于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崩溃了,原来是加入日志监听器,会导致每次都把整个文件加载到内存,那我们就去掉这个

修改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);

这次又崩溃了,

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

于是,修改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下载文件暂时告一段落。

原文链接:http://www.apkbus.com/blog-625356-77325.html

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP