【Android架构】基于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崩溃了,错误信息如下:
[图片上传失败...(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下载文件暂时告一段落。
你的认可,是我坚持更新博客的动力,如果觉得有用,就请点个赞,谢谢

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
JHipster生成微服务架构的应用栈(五)- 容器编排示例
本系列文章演示如何用JHipster生成一个微服务架构风格的应用栈。 环境需求:安装好JHipster开发环境的CentOS 7.4(参考这里) 应用栈名称:appstack 认证微服务: uaa 业务微服务:microservice1 网关微服务:gateway 实体名:role 主机IP:192.168.220.120 1 前提条件 1.1 已经生成微服务架构的应用栈 请参考这个系列的前4篇文章。 1.2 安装Docker Compose 推荐版本:1.21.2 完整安装说明,请参考这里 1.3 创建一个编排目录 在命令行,进入appstack目录,创建一个子目录docker-compose,现在整个应用栈的目录结构是这样的: -- appstack |-- uaa |-- microservice1 |-- gateway |-- docker-compose 1.4 公共镜像 预先下载openjdk:8-jre-alpine容器镜像,能提高后续工作的效率。 2 构建微服务的镜像 2.1 构建所有微服务的镜像 a, 构建uaa镜像 进入uaa目录,输入命令后回车: $ cd ua...
-
下一篇
【Android架构】基于MVP模式的Retrofit2+RXjava封装之文件上传(三)
最近手头事比较多,抽个空把之前系列也补充一下。 先回顾下之前的 【Android架构】基于MVP模式的Retrofit2+RXjava封装(一)【Android架构】基于MVP模式的Retrofit2+RXjava封装之文件下载(二) 今天要说的是文件上传 1.单图上传 首先ApiServer,要使用Multipart 注解 //上传图片(私有接口) @POST("index.php/PrivateApi/Goods/uploadPic") @Multipart Observable<BaseListModel<String>> upLoadImg(@Part MultipartBody.Part parts); 然后是Presenter public void upLoadImg(String path) { File file = new File(path); RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file); Multipa...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7设置SWAP分区,小内存服务器的救世主
- MySQL数据库在高并发下的优化方案
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS关闭SELinux安全模块
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2全家桶,快速入门学习开发网站教程
- Dcoker安装(在线仓库),最新的服务器搭配容器使用