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

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

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

首先,我们先在ApiServer定义好调用的接口@GET

Observable downloadFile(@Url String fileUrl);

接着定义一个接口,下载成功后用来回调public interface FileView extends BaseView {    void onSuccess(File file);

}

接着是Observer,建议与处理普通接口的Observer区分处理public abstract class FileObsever extends BaseObserver {    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;

}

下来是FilePresenterpublic class FilePresenter extends BasePresenter {    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 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() {                    @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() {                    @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下载文件暂时告一段落。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值