Retrofit+RxJava实现带进度条的文件下载

网友投稿 322 2023-01-03


Retrofit+RxJava实现带进度条的文件下载

项目中需要使用到更新版本,因此研究了一下Retrofit的下载文件,和进度条效果,其间也遇到了一些坑,写出来加深一下记忆,也为别的同学提供一下思路。

先说一下版本控制吧,通用做法基本上是通过接口获取服务器存储的app版本号,与应用的版本号进行比较,版本较低就去更新,先看一下如何获取应用版本号吧

PackageManager packageManager = mActivity.getPackageManager();

PackageInfo packageInfo = null;

try {

packageInfo = packageManager.getPackageInfo(mActivity.getPackageName(), 0);

} catch (PackageManager.NameNotFoundException e) {

e.printStackTrace();

}

String versionName = packageInfo.versionName;

可以看到使用的是Context中的getPackageManager方法来获取PackageManager 对象,该对象可用于获取版本的一些信息。

上面的属于附内容,接下来就是关于Retrofit+Rxjava实现进度条下载文件的功能,Retrofit本身不提供进度条显示的功能,但Retrofit默认使用Okhttp来进行网络请求,这里就可以自定义拦截器来进行拦截,实现进度。Okhttp的Demo中也为我们提供了一份代码,需要的可以去参考一下Progress.javar,可以看到拦截器的设置:

public class ProgressResponseBody extends ResponseBody {

private ResponseBody responseBody;

private ProgressListener progressListener;

private BufferedSource bufferedSource;

public ProgressResponseBody(ResponseBody responseBody,ProgressListener progressListener){

this.responseBody=responseBody;

this.progressListener=progressListener;

}

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

//增加当前读取的字节数,如果读取完成了bytesRead会返回-1

totalBytesRead += bytesRead != -1 ? bytesRead : 0;

//回调,如果contentLength()不知道长度,会返回http://-1

progressListener.onProgress(totalBytesRead,responseBody.contentLength(),bytesRead,bytesRead==-1);

return bytesRead;

}

};

}

}

ProgressListener 用来监听进度变化,回调到ProgressInterceptor中,ProgressInterceptor是一个自定义的拦截器,可以看一下代码

public class ProgressInterceptor implements Interceptor {

@Override

public Response intercept(Chain chain) throws IOException {

Response response=chain.proceed(chain.request());

return response.newBuilder().body(new ProgressResponseBody(response.body(),progressListener)).build();

}

static final ProgressListener progressListener=new ProgressListener() {

@Override

public void onProgress(long progress, long total, long speed, boolean done) {

Log.i("log","progress="+progress+"total="+total);

}

};

}

为了便于获取progress,可以通过OkHttpClient的addNetworkInterceptor方法直接添加一个自定义的拦截器,例如:

//为Okhttp设置拦截器

OkHttpClient client = new OkHttpClient.Builder()

.addNetworkInterceptor(new Interceptor() {

@Override public Response intercept(Chain chain) throws IOException {

Response originalResponse = chain.proceed(chain.request());

return originalResponse.newBuilder()

.body(new ProgressResponseBody(originalResponse.body(), progressListener))

.build();

}

})

.build();

//进度回调监听

ProgressListener progressListener=new ProgressListener() {

@Override

public void onProgress(long progress, long total, long speed, boolean done) {

Message message=new Message();

message.obj=new AmallLoadBean(progress,total);

progressHandler.sendMessage(message);

}

};

这里通过一个创建一个继承自Handler的ProgressHandler静态内部类用于在主线程中刷新进度,顺带提一下,使用static修饰ProgressHandler是因为静态内部类默认不持有外部类对象的引用,需要注意一下Handler的内存泄漏,使用一下写法:

//处理下载版本进度

public class ProgressHandler extends Handler{

private WeakRehttp://ference mActivityWeakReference;

public ProgressHandler(Activity activity){

mActivityWeakReference=new WeakReference(activity);

}

@Override

public void handleMessage(Message msg) {

if(mActivityWeakReference.get()!=null){

AmallLoadBean amallLoadBean= (AmallLoadBean) msg.obj;

long progress=amallLoadBean.getProgress();

long total=amallLoadBean.getTotal();

float cp=(float)progress/(float)total;

}

}

}

继续回到下载文件中,我才用的是Retrofit+RxJava的方法来实现,写之前也看了一下别人写的,好像不全,下满也遇到了一些小坑,讲一下吧:

observable.subscribeOn(Schedulers.io())

.observeOn(Schedulers.io())

.doOnNext(new Action1() {

@Override

public void call(ResponseBody responseBody) {

saveFiles(responseBody);

}

})

.observeOn(androidSchedulers.mainThread())

.subscribe(new Observer() {

@Override

public void onCompleted() {

installApk();

}

@Override

public void onError(Throwable e) {

ToastUtils.getInstance().showToast("请到应用市场下载最新版本");

}

@Override

public void onNext(ResponseBody responseBody) {

}

});

}

通过RxJava的doOnNext在subscribe方法之前存储文件,这里需要注意的是doOnNext方法需要在子线程中执行,调用.observeOn(Schedulers.io())方法,然后再切换到主线程,否则文件下载不下来。当文件下载完成时,在onCompleted方法中执行installApk()方法安装app。需要注意的是这里需要做权限的适配,因为我的是自己封装的因为就不拿出来了,挺简单就自己写吧。保存文件的代码给大家放出来了,通俗的语言:

/**

* 保存文件

*/

public void saveFiles(ResponseBody responseBody){

InputStream inputStream = null;

FileOutputStream fileOutputStream = null;

byte[] buffer=new byte[2048];

int len;

File file=new File(saveFileName);

if(!file.exists()){

file.mkdirs();

}

try {

inputStream=responseBody.byteStream();

fileOutputStream=new FileOutputStream(file);

while ((len=inputStream.read(buffer))!=-1){

fileOutputStream.write(buffer,0,len);

}

inputStream.close();

fileOutputStream.close();

} catch (Exception e) {

e.printStackTrace();

}

}

在安装文件的时候,需要注意7.0以后的适配,代码看看就好,和拍照适配的原理一直,都是Android对私密性文件的权限问题

/**

* 安装apk

*

*/

private void installApk() {

File apkfile = new File(saveFileName);

if (!apkfile.exists()) {

return;

}

//判断版本号

if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){

Uri apkUri = FileProvider.getUriForFile(activity, "******.fileprovider", apkfile);

Intent install = new Intent(Intent.ACTION_VIEW)nHvGZu;

install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

//添加这一句表示对目标应用临时授权该Uri所代表的文件

install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

install.setDataAndType(apkUri, "application/vnd.android.package-archive");

activity.startActivity(install);

}else{

Intent i = new Intent(Intent.ACTION_VIEW);

i.setDataAndType(Uri.parse("file://" + apkfile.toString()), "application/vnd.android.package-archive");

activity.startActivity(i);

}

}

基本上就这些,后续我会在此篇文章上继续补充。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:包含财付通接口测试工具的词条
下一篇:实现接口用的关键字(实现接口用的关键字是)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~