android和java实现下载,Android mvp+retrofit2+rxjava+greendao 实现多文件断点下载

多文件断点下载(基于mvp模式)

ffec28c22a2b?from=timeline&isappinstalled=0

1528445893770.gif

介绍

和大多数应用商店一样,多个apk文件同时下载,点击暂停,停止下载,或者在下载中退出应用时,下次进入可以从断点处进行下载,配合rxjava十分方便,源码地址附在文末。

1.添加头部

在请求头部添加下载起始点 range ,range假如是 200,文件就从200byte开始下载,是0,代表从头下载

public interface RetrofitIntetface {

/**

* 断点下载

* @param start 起始位

* @param url 链接

* @return

*/

//多个baseurl使用,单个baseUrl无需配置在此配置

@Headers({"baseUrl:"+ AppConstant.BASE_HTTP})

//特别注意,防止加载到内存中

@Streaming

@GET

Observable download(@Header("RANGE") String start, @Url String url);

}

2.下载线程

用rxjava的好处就是不用考虑线程的切换,请求和读流写文件都放在io线程中操作

public void onDownload(String start,String url,BaseObserver observer){

intetface.download(start,url)

//在io线程中请求下载

.subscribeOn(Schedulers.io())

//在io线程中读流写文件

.observeOn(Schedulers.io())

.subscribe(observer);

}

2.presenter 层操作

在此 rxjava的回调方法中切不可操作UI(progressBar控件除外,它内部实现了线程切换),上面已知请求文件body和写流都在在io线程中,不能直接更新ui,若要更新ui可用handler或者eventbus。

public class MainPresenter extends BasePresenter {

private Context mContext;

private ConcurrentHashMap mMap;

public MainPresenter(Context mContext) {

super(mContext);

this.mContext=mContext;

this.mMap=new ConcurrentHashMap<>();

}

public void onDownloadStart(final String url,final String fileName){

boolean flag=mPermissions.isGranted(Manifest.permission_group.STORAGE);

if (!flag) {

mPermissions.requestEach(Manifest.permission.WRITE_EXTERNAL_STORAGE).subscribe(new Consumer() {

@Override

public void accept(Permission permission) throws Exception {

if (permission.granted) {

// 用户已经同意该权限

download(url,fileName);

} else if (permission.shouldShowRequestPermissionRationale) {

// 用户拒绝了该权限,没有选中『不再询问』

mView.onPermission(false);

} else {

mView.onPermission(false);

}

}

});

}else{

download(url,fileName);

}

}

private void download(String url,final String fileName){

final RetrofitDownLoadManager manager= new RetrofitDownLoadManager(mContext);

final long start=manager.getFileStart(mGreenDao,fileName);

//下载完成

if (start==-1)

return;

//异常

if (start==-2)

return;

mRetrofit.onDownload("bytes=" + start + "-", url, new BaseObserver(mContext) {

@Override

protected void onError(String error) {

}

@Override

protected void onSuccess(final ResponseBody result) {

if (manager.writeStream(start,result,this)){

onDownloadStop(fileName);

}

}

@Override

protected void onAddSubscribe(Disposable d) {

mMap.put(fileName,d);

}

@Override

protected void onProgress(String fileName, int progress) {

mView.onProgress(fileName,progress);

}

@Override

protected void onResult(DownResult result) {

mView.onDownloadResult(result);

}

});

}

public void onDownloadStop(String flieName){

Disposable disposable=mMap.get(flieName);

//取消订阅,是文件停止下载

if (disposable!=null&&!disposable.isDisposed())

disposable.dispose();

}

@Override

public void clearRequest() {

for (Disposable disposable: mMap.values()) {

if (!disposable.isDisposed())

disposable.dispose();

}

}

}

3.写流操作

进度更新和进度保存,用greendao数据库对下载进度进行保存,当用户暂停下载时,rxjava取消订阅即可,数据库保存当前下载进度。

/**

* @param mFileName

* @return

*/

public long getFileStart(DaoManagement mGreenDao, String mFileName) {

try {

this.mGreenDao = mGreenDao;

this.mFileName = mFileName;

mFileRange = mGreenDao.query(mFileName);

//检查待下载文件是否存在

String basePath = FileUtil.checkDirPath(Environment.getExternalStorageDirectory() + File.separator + mcontext.getPackageName());

mFile = new File(basePath + File.separator + mFileName);

if (!mFile.exists()) {

//文件不存在,从0开始下载

mFile.createNewFile();

if (mFileRange != null) {

mGreenDao.delete(mFileRange);

}

return 0;

} else {

if (mFileRange != null) {

//文件存在,已经下载完成

if (mFileRange.getIsFinish()) {

//完整性校验

//取代事务车结果回调,完成解耦

mObserver.onResultCallBack(new DownResult(true, mFileName));

return -1;

} else {

//文件下载未完成,从断点处下载

mFileLength = mFileRange.getLength();

return mFileRange.getStart();

}

} else {

mFile.delete();

mFile.createNewFile();

if (mFileRange != null) {

mGreenDao.delete(mFileRange);

}

return 0;

}

}

} catch (IOException e) {

return -2;

}

}

public boolean writeStream(long mStart, ResponseBody mBody, BaseObserver mObserver) {

this.mObserver = mObserver;

if (mFile == null) {

return false;

}

if (mStart > 0 && mFileLength > 0 && mStart == mFileLength) {

return true;

}

//从0开始下载,数据库插入数据

if (mStart == 0) {

mFileRange = new FileRange();

mFileRange.setFileName(mFileName);

mFileRange.setIsFinish(false);

mFileRange.setLength(mBody.contentLength());

mFileRange.setStart(mStart);

mFileRange.setTime(System.currentTimeMillis());

mGreenDao.update(mFileRange);

mFileLength = mBody.contentLength();

}

InputStream mInputStream = null;

OutputStream mOutputStream = null;

try {

byte[] mReader = new byte[1024 * 1024];

//边读边写

mInputStream = mBody.byteStream();

mOutputStream = new FileOutputStream(mFile, true);

while (true) {

int read = mInputStream.read(mReader);

if (read == -1) {

break;

}

mOutputStream.write(mReader, 0, read);

mStart += read;

//进度回调

int num = (int) (mStart * 100 / mFileLength);

mObserver.onSetProgress(mFileName, num);

Log.i("TAG", "进度回调" + ":" + mStart + " " + num);

}

mOutputStream.flush();

flag = true;

//更新数据库数据

mFileRange.setFileName(mFileName);

mFileRange.setIsFinish(true);

mFileRange.setStart(mStart);

mFileRange.setTime(System.currentTimeMillis());

mGreenDao.update(mFileRange);

//此处可以进行文件完整性校验(尤其针对APK文件,否则有可能无法安装)

Log.i("TAG", mFileName + ": 下载完成……");

//取代事务车结果回调,完成解耦

mObserver.onResultCallBack(new DownResult(true, mFileName));

return true;

} catch (IOException e) {

Log.i("TAG", mFileName + ": 下载失败……");

return false;

} finally {

//保存进度到本地数据库

if (!flag) {

mFileRange.setFileName(mFileName);

mFileRange.setStart(mStart);

mFileRange.setTime(System.currentTimeMillis());

mGreenDao.update(mFileRange);

}

try {

if (mInputStream != null) {

mInputStream.close();

}

if (mOutputStream != null) {

mOutputStream.close();

}

} catch (IOException e) {

e.printStackTrace();

}

}

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值