前言
在RetroHttp
的下载功能使用过程中,出现了下载进度无法实时显示的问题,然后我做了些优化,下面就来讲讲吧。
更多关于RetroHttp
,大家可参考以下文章:
RetroHttp(一) — 下载使用介绍
RetroHttp(二) — 下载支持增量更新
RetroHttp(三) — kotlin版网络请求
RetroHttp(四) — 实现串联请求
RetroHttp(五) — 并发请求
今天涉及内容:
- RetroHttp 关于下载,优化前的调用方式
- 下载无法显示进度的原因分析
- 优化下载的调用方式
一. RetroHttp 关于下载,优化前的调用方式
在RetroHttp
的0.4.3
版本之前,我们要调用下载的话,是像下面这样调用的:
//是否增量更新的标志
private boolean mUpdate=false;
private void updateApk(){
String url=null;
//此处mUpdate你要先根据业务逻辑赋值为true或false
if(mUpdate){
//增量更新的url
url="https://xxxx/xxx/test.patch";//示例代码
}else{
//全量更新的url
url="https://xxxx/xxx/test_2.0.0.apk";//示例代码
}
LogUtil.i("=========mUpdate======"+mUpdate);
LogUtil.i("=========url======"+url);
int firstIndex=url.lastIndexOf("/")+1;
int lastIndex=url.length();
String fileName=url.substring(firstIndex,lastIndex);
LogUtil.i("=========fileName======"+fileName);
//启动下载
ProgressDialog progressDialog = DownLoadHelper.getInstance().showDownLoading(mContext);
DownLoadHelper.getInstance().setFileName(fileName)//设置下载文件名
.setIcon(R.mipmap.ic_launcher)//设置apk图标资源id
.setAuthorityTag("com.p.atestdemo")//设置要与清单文件的 provider 中配置的authorities值一致
.setIncrementUpdate(mUpdate)//是否开启增量更新(true:开启,false:关闭,此行代码不设置会默认全量更新)
.downLoadFile(url, MainActivity.this, new DownLoadHelper.DownloadListener() {
@Override
public void onStart() {
}
@Override
public void update(int progress, boolean done) {
progressDialog.setProgress((int) (progress * 1f));
}
@Override
public void onCompleted() {
progressDialog.dismiss();
}
@Override
public void onError(String err) {
progressDialog.dismiss();
LogUtil.i("=========下载失败====="+err);
if(DownLoadHelper.getInstance().isDeltaFileFailed(err)){
LogUtil.i("=========增量更新下载失败====="+err);
//增量更新下载失败的逻辑(一般发rx通知出来,然后改全量更新)
//......
}else{
LogUtil.i("=========全量更新下载失败====="+err);
//全量更新下载失败的逻辑(一般提示退出app)
//......
}
}
});
}
然而在使用过程中,我们会发现下载进度框在显示的时候,竟然不更新,下载进度一直显示是0,但实际上文件是在下载的。接着让我们来看看问题所在。
二.下载无法显示进度的原因分析
追查代码发现,下载目标文件的总大小contentLength
来自responseBody.contentLength()
,而responseBody.contentLength()
值一直是-1
。在http
通讯过程中,contentLength()
源自通讯字节头中的ContenLength
属性,ContenLength
中一般会加载要下载的文件总大小,但是由于okhttp3
基本采用gzip
压缩方式下载,导致通讯字节头中Transfer-Encoding: chunked
,无法获取准确ContenLength
值,于是网上提出一种解决办法,在发起下载通讯时,字节头中添加如下代码:
// 设置统一请求参数
Request.Builder newRequest = oldRequest.newBuilder()
.method(oldRequest.method(), oldRequest.body())
// 设置请求头
.addHeader("Accept-Encoding", "identity")//强迫服务器不走压缩,以得到文件总大小
使得在通讯时,申请服务器禁用压缩方式,这样就能获得具体的ContenLength
值。但是在实际使用过程中我们会发现并不是所有的下载通讯请求,在返回的数据中字节头都会含ContenLength
属性。让我们打开cmd
,输入curl -i 下载链接
看看返回信息的字节头:
curl -i https://xxxx/xxxx/xxxx/test.apk
HTTP/1.1 200 OK
Date: Tue, 06 Apr 2021 01:51:53 GMT
Content-Type: application/octet-stream
Transfer-Encoding: chunked
Connection: keep-alive
Set-Cookie: acw_tc=76b20f6916176739129186578e7f31209e1a24c1af4e3d16fbaecc60318433;path=/;HttpOnly;Max-Age=1800
Server: nginx
这里,你会发现,在返回的字节头中根本没有ContenLength
,所以最终解决办法是在下载之前的一个通讯中,让服务端返回要下载文件的总大小,然后你再去进行下载。
三. 优化下载的调用方式
最后,让我们来看看优化后RetroHttp
关于下载的代码调用吧:
/**更新apk**/
private void updateApk(String url){
String fileName= DownLoadHelper.getInstance().getDefaultFileName(url);
RetroLog.i("=========fileName===kk==="+fileName);
//启动下载
DownLoadHelper.getInstance().setFileName(fileName)//设置下载文件名
.setFileLength(10000)//设置下载文件大小
.setIcon(R.mipmap.ic_launcher)//设置apk图标资源id
.setAuthorityTag("xxxx")//设置要与清单文件的 provider 中配置的authorities值一致
.setIncrementUpdate(false)//是否开启增量更新(true:开启,false:关闭,此行代码不设置会默认全量更新)
.setUseDefaultDownDialog(true)//开启默认下载进度dialog
.downLoadFile(url, MainActivity.this, new DownLoadHelper.DownloadListener(){
@Override
public void onStart() {
}
@Override
public void update(int progress, boolean done) {
}
@Override
public void onCompleted() {
}
@Override
public void onError(String err) {
RetroLog.i("=========下载失败=====" + err);
if (DownLoadHelper.getInstance().isDeltaFileFailed(err)) {
RetroLog.i("=========增量更新下载失败=====" + err);
//增量更新下载失败的逻辑(一般发rx通知出来,然后改全量更新)
//......
//由于使用非增量更新,故此处不做处理
} else {
RetroLog.i("=========全量更新下载失败=====" + err);
//全量更新下载失败的逻辑(一般提示退出app)
}
}
});
}
这里需要注意的是:
-
setFileLength(10000)
:是设置下载文件总大小,当不设置此属性的时候,系统会默认采用ContenLength
来计算文件总大小 -
setIncrementUpdate(false)
:表示是否采用增量更新的方式下载,默认是不采用 -
setUseDefaultDownDialog(true)
:表示是否使用默认的下载进度框,true 表示使用,false 表示不使用。默认情况下为fasle,即只有我们声明为true时,才会加载默认下载进度框。当setUseDefaultDownDialog(fasle)
时,你可以自定义加载进度条,类似下面这样:
/**更新apk**/
private void updateApk(String url){
String fileName= DownLoadHelper.getInstance().getDefaultFileName(url);
RetroLog.i("=========fileName===kk==="+fileName);
//自定义加载进度框
MyProgressDialog dialog= new MyProgressDialog();
dialog.show();
//启动下载
DownLoadHelper.getInstance().setFileName(fileName)//设置下载文件名
//其他代码省略
//......
.setUseDefaultDownDialog(false)//禁用默认下载进度dialog
.downLoadFile(url, MainActivity.this, new DownLoadHelper.DownloadListener(){
@Override
public void onStart() {
}
@Override
public void update(int progress, boolean done) {
//更新进度条
dialog.setProgress(progress);
}
@Override
public void onCompleted() {
//进度条隐藏
dialog.dismiss();
}
@Override
public void onError(String err) {
//进度条隐藏
dialog.dismiss();
RetroLog.i("=========下载失败=====" + err);
if (DownLoadHelper.getInstance().isDeltaFileFailed(err)) {
RetroLog.i("=========增量更新下载失败=====" + err);
//增量更新下载失败的逻辑(一般发rx通知出来,然后改全量更新)
//......
//由于使用非增量更新,故此处不做处理
} else {
RetroLog.i("=========全量更新下载失败=====" + err);
//全量更新下载失败的逻辑(一般提示退出app)
}
}
});
}
ok,今天的内容就介绍到这里了,谢谢大家。