项目中经常会有文件下载、版本更新等功能。正好周末无事,用RxJava2 + Retrofit2 + Greendao造了一个文件下载的轮子。效果图如下:
方法名 | 作用 |
---|---|
getInstance() | 静态方法,以单列方式获取下载帮助类对象 |
setSavePath(String localPath) | 设置文件保存路径 |
setMaxTask(int count) | 设置最大下载进程数(同时可以下载的最大数量,默认三个) |
registerListener(DownloadListener listener) | 注册监听,回掉当前下载状态 |
unRegisterListener() | 解除监听,与registerListener配对使用,页面销毁时调用,防止内存泄漏 |
start(String url) | 开始单个文件下载 |
start(List list) | 开始多个下载任务 |
pause(String url) | 暂停单个下载任务 |
pauseAll() | 暂停所有进行中的任务 |
resume(String url) | 恢复下载任务(暂停状态恢复下载) |
delete(String url) | 删除下载任务 |
deleteAll() | 删除所有下载任务 |
getLocalFilePathFromUrl(String url) | 根据下载链接获取本地存储路径 |
首先引入RxJava、Retrfit、Greendao的相关库:
compile 'org.greenrobot:greendao:3.2.2' // add library
compile 'org.greenrobot:greendao-generator:3.2.2'
compile 'com.squareup.retrofit2:retrofit:2.5.0'
compile "io.reactivex.rxjava2:rxjava:2.2.4"
compile 'io.reactivex.rxjava2:rxandroid:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.5.0'
compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
至于Greendao的如何使用,大家可以去查询相关资料,这里不进行过多的阐述。
新建存储下载信息的类DownloadInfo,用于存储文件总大小、已下载大小、本地存储路径、下载链接等字段
@Entity
public class DownloadInfo {
@Id
private Long id;
// 本地存储路径
@Property(nameInDb = "local_path")
private String localPath;
// 文件总长度
@Property(nameInDb = "content_length")
private long contentLength;
// 已下载长度
@Property(nameInDb = "read_length")
private long readLength;
// 文件下载链接
@Property(nameInDb = "url")
private String url;
@Property(nameInDb = "is_complete")
private boolean isComplete;
// 绑定下载服务
@Transient
private DownLoadService service;
@Generated(hash = 1329093281)
public DownloadInfo(Long id, String localPath, long contentLength,
long readLength, String url, boolean isComplete) {
this.id = id;
this.localPath = localPath;
this.contentLength = contentLength;
this.readLength = readLength;
this.url = url;
this.isComplete = isComplete;
}
@Generated(hash = 327086747)
public DownloadInfo() {
}
public String getLocalPath() {
return localPath;
}
public void setLocalPath(String localPath) {
this.localPath = localPath;
}
public long getContentLength() {
return contentLength;
}
public void setContentLength(long contentLength) {
this.contentLength = contentLength;
}
public long getReadLength() {
return readLength;
}
public void setReadLength(long readLength) {
this.readLength = readLength;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public DownLoadService getService() {
return service;
}
public void setService(DownLoadService service) {
this.service = service;
}
@Override
public String toString() {
return "DownloadInfo{" +
"localPath='" + localPath + '\'' +
", contentLength=" + contentLength +
", readLength=" + readLength +
", url='" + url + '\'' +
", service=" + service +
'}';
}
public boolean getIsComplete() {
return this.isComplete;
}
public void setIsComplete(boolean isComplete) {
this.isComplete = isComplete;
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
}
新建进度回掉接口DownloadProgressListener
public interface DownloadProgressListener {
/**
* @param read 已下载长度
* @param contentLength 总长度
* @param done 是否下载完毕
*/
void progress(long read, long contentLength, boolean done);
}
自定义Retrofit的返回DownloadResponseBody
public class DownloadResponseBody extends ResponseBody{
protected ResponseBody responseBody;
protected DownloadProgressListener listener;
protected BufferedSource bufferedSource;
public DownloadResponseBody(ResponseBody responseBody, DownloadProgressListener listener)
{
this.responseBody = responseBody;
this.listener = listener;
}
@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);
// read() returns the number of bytes read, or -1 if this source is exhausted.
totalBytesRead += bytesRead != -1 ? bytesRead : 0;
if (null != listener) {
listener.progress(totalBytesRead, responseBody.contentLength(), bytesRead == -1);
}
return bytesRead;
}
};
}
}
建立Retrofit的请求接口DownLoadService
public interface DownLoadService {
/**
* @param start 从某个字节开始下载数据
* @param url 文件下载的url
* @return Observable
* @Streaming 这个注解必须添加,否则文件全部写入内存,文件过大会造成内存溢出
*/
@Streaming
@GET
Observable download(@Header("RANGE") String start, @Url String url);
}
因为Retrofit下载过程中是没有直接的进度回掉的,我们需要写一个进度拦截器
DownloadInterceptor
public class DownloadInterceptor implements Interceptor{
protected DownloadProgressListener listener;
public DownloadInterceptor(DownloadProgressListener listener)
{
this.listener = listener;
}
@Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
return response.newBuilder().body(new DownloadResponseBody(response.body(), listener)).build();
}
}
构建我们的下载管理类DownloadManager:
public class DownloadManager implements DownloadProgressListener{
private static final String TAG = "DownloadManager";
protected DownloadListener progressObserver;
protected DownloadInfo info;
protected DownLoadService service;
// 下载任务的网络请求操作对象
private Disposable disposable;
// 轮询操作的对象
private Disposable mResearchProgress;
// 当前状态
private int mDownStatus = DownloadConfig.STATUS_DEFAULT;
protected int mSpeedRefreshUiTime = DownloadConfig.SPEED_REFRESH_UI_TIME ;
// 文件存储路径
protected String mSaveFilePath = Environment.getExternalStorageDirectory() +
File.separator +"mickydown"+File.separator;
private DownloadManager()
{
// delayProgress();
}
/**
* 获取实例
* @return
*/
public static DownloadManager getInstance()
{
return new DownloadManager();
}
@Override
public void progress(long read, long contentLength, final boolean done) {
// Log.d(TAG, "progress : " + "read = " + read + "contentLength = " + contentLength);
// 该方法仍然是在子线程,如果想要调用进度回调,需要切换到主线程,否则的话,会在子线程更新UI,直接错误
// 如果断点续传,重新请求的文件大小是从断点处到最后的大小,不是整个文件的大小,info中的存储的总长度是
// 整个文件的大小,所以某一时刻总文件的大小可能会大于从某个断点处请求的文件的总大小。此时read的大小为
// 之前读取的加上现在读取的
if (info.getContentLength() > contentLength) {
// 继续上次断点续传
read = read + (info.getContentLength() - contentLength);
} else {
// 重新开始一个下载
info.setContentLength(contentLength);
}
// 设置已经下载的大小
info.setReadLength(read);
// 设置是否完成
info.setIsComplete(done);
// 设置状态下载中
mDownStatus = DownloadConfig.STATUS_DOWNNING;
// 通过RxJava的方法,将回掉结果转接到主线程,防止刷新UI时崩溃
/*Observable.just(1).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer() {
@Override
public void accept(Integer integer) throws Exception {
if (progressObserver != null)
{
progressObserver.progress(info.getReadLength(), info.getContentLength(), done, info.getUrl(),speed);
}
}
});*/
}
/**
* 设置保存的位置
* @param path 本地路径
*/
public void setSavePath(String path)
{
if (!TextUtils.isEmpty(path))
{
this.mSaveFilePath = path;
}
}
/**
* 开始下载
* @param url
*/
public void start(String url)
{
// 判断链接是否已经在下载
if (mDownStatus == DownloadConfig.STATUS_DOWNNING )
{
// 正在下载中
Log.d(TAG,"正在下载中,请勿重复添加");
return;
}
// 首先查找此链接数据库中是否已经存在对应的下载
info = FileDown.getFileDown().getDaoSession().getDownloadInfoDao().queryBuilder().where(DownloadInfoDao.Properties.Url.eq(url)).unique();
if (info == null)
{
info = new DownloadInfo();
}
// 判断是否已经下载完成
boolean isComplete = info.getIsComplete();
if (isComplete)
{
mDownStatus = DownloadConfig.STATUS_COMPLETE;
// 回掉下载完成 不在继续下载
if (progressObserver != null)
{
progressObserver.complete(url, new File(mSaveFilePath+getFileName(url)));
}
return;
}
// 设置本地保存路径
info.setLocalPath(mSaveFilePath+getFileName(url));
// 设置下载路径
info.setUrl(url);
// 拦截器
DownloadInterceptor interceptor = new DownloadInterceptor(this);
OkHttpClient.Builder builder = new OkHttpClient.Builder();
// 设置连接超时时间为8s
builder.connectTimeout(8 , TimeUnit.SECONDS);
// 添加拦截器
builder.addInterceptor(interceptor);
Retrofit retrofit = new Retrofit.Builder()
.client(builder.build())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl(UrlUtils.getBasUrl(url))
.build();
if (service == null)
{
service = retrofit.create(DownLoadService.class);
info.setService(service);
}else
{
// 保存的实例
info.setService(service);
}
// 开启轮询
delayProgress();
// 开始下载
download();
}
/**
* 开始下载
*/
protected void download()
{
Log.d(TAG,"info:"+info);
service.download("bytes=" + info.getReadLength() + "-",info.getUrl())
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.retryWhen(new RetryWhenNetworkException())
.map(new Function() {
@Override
public DownloadInfo apply(@NonNull ResponseBody responseBody) throws Exception {
//写入文件
FileUtil.writeCache(responseBody, new File(info.getLocalPath()), info);
return info;
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new Observer() {
@Override
public void onSubscribe(@NonNull Disposable d) {
disposable = d;
mDownStatus = DownloadConfig.STATUS_START;
// 初始化开始下载的时间和已经下载的数据,计算速度
lastTimeStamp = System.currentTimeMillis();
lastRead = info.getReadLength();
progressObserver.start(info.getUrl());
}
@Override
public void onNext(@NonNull DownloadInfo downloadInfo) {
Log.d(TAG,"onNext:"+downloadInfo);
}
@Override
public void onError(@NonNull Throwable e) {
Log.d(TAG,"onError:"+e.getMessage());
// 下载错误,保存当前的info
saveInfoToDb();
mDownStatus = DownloadConfig.STATUS_ERROR;
progressObserver.error(info.getUrl(),e.getMessage());
}
@Override
public void onComplete() {
Log.d(TAG,"onComplete");
// 下载完成,设置标识为已完成状态
mDownStatus= DownloadConfig.STATUS_COMPLETE;
if (progressObserver != null)
{
progressObserver.complete(info.getUrl(),new File(info.getLocalPath()));
}
info.setIsComplete(true);
saveInfoToDb();
}
});
}
/**
* 暂停下载
*/
public void pause()
{
if (mDownStatus == DownloadConfig.STATUS_PAUSE)
{
return;
}
if (disposable != null)
{
disposable.dispose();
// 暂停时需要保持相关信息
saveInfoToDb();
mDownStatus = DownloadConfig.STATUS_PAUSE;
progressObserver.pause(info.getUrl());
}
}
/**
* 恢复下载
*/
public void resume()
{
if (mDownStatus != DownloadConfig.STATUS_PAUSE)
{
return;
}
// 开启轮询
delayProgress();
download();
//mDownStatus = DownloadConfig.STATUS_RESUME;
}
public void delete()
{
if (mDownStatus == DownloadConfig.STATUS_DELETE)
{
return;
}
// 首先暂停下载
pause();
// 删除下载信息
deleteDownInfo();
// 删除已经下载的文件
FileUtil.deleteFile(info.getLocalPath());
mDownStatus = DownloadConfig.STATUS_DELETE;
progressObserver.delete(info.getUrl());
}
/**
* 获取文件名
* @param pathandname
* @return
*/
public String getFileName(String pathandname){
int start=pathandname.lastIndexOf("/");
if(start!=-1){
return pathandname.substring(start+1,pathandname.length());
}else{
return null;
}
}
/**
* 保存当前信息到数据库
*/
protected void saveInfoToDb()
{
FileDown.getFileDown().getDaoSession().getDownloadInfoDao().save(info);
}
/**
* 删除下载信息
*/
protected void deleteDownInfo()
{
FileDown.getFileDown().getDaoSession().getDownloadInfoDao().delete(info);
}
/**
* 清楚所有的下载数据数据
*/
public void cleanDataBase()
{
FileDown.getFileDown().getDaoSession().getDownloadInfoDao().deleteAll();
// Log.d(TAG,"DataBase:"+FileDown.getInstances().getDaoSession().getDownloadInfoDao().)
}
public String getDiskCacheDir(Context context) {
String cachePath = null;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
|| !Environment.isExternalStorageRemovable()) {
cachePath = context.getExternalCacheDir().getPath();
} else {
cachePath = context.getCacheDir().getPath();
}
return cachePath;
}
/**
* 延迟回掉进度
*/
protected void delayProgress()
{
mResearchProgress = Observable.interval(mSpeedRefreshUiTime,TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer() {
@Override
public void accept(Long aLong) throws Exception {
if (mDownStatus == DownloadConfig.STATUS_DOWNNING)
{
// 如果是正在下载的话,回掉进度:
if (progressObserver != null)
{
// 计算下载速度
getSpeed();
progressObserver.progress(info.getReadLength(), info.getContentLength(), info.getIsComplete(), info.getUrl(),speed);
}
}else
{
// 其他情况关闭轮询
if (mResearchProgress != null)
{
mResearchProgress.dispose();
}
}
}
});
}
// 最近一次计算时间
long lastTimeStamp;
// 上次读写的数据
long lastRead;
// 下载速度
long speed;
/**
* long类型的下载速度
* @return
*/
protected long getSpeed()
{
long nowTimeStamp = System.currentTimeMillis();
if (nowTimeStamp - lastTimeStamp < mSpeedRefreshUiTime) return speed;
speed = ((info.getReadLength() - lastRead) / ((nowTimeStamp - lastTimeStamp)/1000));
Log.d(TAG,"瞬时下载量:"+(info.getReadLength() - lastRead)+";speed:"+speed);
lastTimeStamp = nowTimeStamp;
lastRead = info.getReadLength();
return speed;
}
public void setProgressListener(DownloadListener progressObserver)
{
this.progressObserver = progressObserver;
}
}
支持后台多任务下载,我们自定义下载服务类DownloadManngerService:
public class DownloadManngerService extends Service{
private static final String TAG = "DownloadManngerService";
// 同时下载的最大线程数量
protected int maxThreadCount = DownloadConfig.MAX_DOWN_THREAD_COUNT;
// 当前正在下载的线程数量
protected int mCurrentThreadCount = 0;
// 文件保存位置
protected String mSavePath;
// 存放处理进行中的任务的map集合,url做为键
protected Map mapThread;
// 存放代下载任务的url集合
protected Map mWaitThread;
@Override
public void onCreate() {
super.onCreate();
// 执行初始化操作
init();
Log.d(TAG,"onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent == null)
{
return super.onStartCommand(intent, flags, startId);
}
// 获取相关动作
int action = intent.getIntExtra(DownloadConfig.ACTION,DownloadConfig.ACTION_DEFAULT);
// 获取相关连接
String url = intent.getStringExtra(DownloadConfig.URL);
// 获取下载链接的集合
List list = intent.getStringArrayListExtra(DownloadConfig.URL_ARRAY);
// 线程最大数量
maxThreadCount = intent.getIntExtra(DownloadConfig.MAX_THREAD_COUNT,DownloadConfig.MAX_DOWN_THREAD_COUNT);
// 文件保存位置
mSavePath = intent.getStringExtra(DownloadConfig.LOCAL_PATH);
// 处理相关响应
respondAction(action,url,list);
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
/**
* 初始化操作
*/
protected void init()
{
// 初始化话map
mapThread = new ConcurrentHashMap<>(maxThreadCount);
mWaitThread = new ConcurrentHashMap<>();
}
/**
* 处理相关操作:开始下载、暂停下载、恢复下载、重新下载、删除下载
* @param action 执行动作
* @param url 下载连接
* @param list 下载链接集合
*/
protected void respondAction(int action, String url, List list)
{
switch (action)
{
case DownloadConfig.ACTION_START: // 开始下载
start(url);
break;
case DownloadConfig.ACTION_PAUSE: // 暂停下载
pause(url);
break;
case DownloadConfig.ACTION_RESUME: // 恢复下载
resume(url);
break;
case DownloadConfig.ACTION_RESTART:// 重新下载
break;
case DownloadConfig.ACTION_DELETE: // 删除下载
delete(url);
break;
case DownloadConfig.ACTION_START_ALL: // 开始全部任务
start(list);
break;
case DownloadConfig.ACTION_PAUSE_ALL: // 暂停下载任务
pauseAllTash();
break;
case DownloadConfig.ACTION_DELETE_ALL: // 删除所有任务
deleteAllTask();
break;
default:
break;
}
}
/**
* 开始下载
* @param url 下载连接
*/
protected void start(String url)
{
mCurrentThreadCount = mapThread.size();
if (mCurrentThreadCount >= maxThreadCount)
{
// 当前进行的线程数量 大于 最大允许进行的最大线程
wait(url);
return;
}
DownloadManager downloadManager = null;
// 实例化DownloadManager对象
if (mapThread.containsKey(url))
{
downloadManager = mapThread.get(url);
}else
{
downloadManager = DownloadManager.getInstance();
// 加入到下载队列
mapThread.put(url,downloadManager);
}
// 开始下载
downloadManager.setSavePath(mSavePath);
downloadManager.setProgressListener(listener);
downloadManager.start(url);
// 当前任务数+1
mCurrentThreadCount ++;
}
/**
* 暂停任务
* @param url
*/
protected void pause(String url)
{
// 获取当前任务的下载管理类
DownloadManager downloadManager = mapThread.get(url);
if (downloadManager != null)
{
downloadManager.pause();
}else
{
// 查询是否在等待队列中
if (mWaitThread.containsKey(url))
{
// 从等待队列中移除链接,回掉暂停下载的监听
mWaitThread.remove(url);
listener.pause(url);
}
}
}
/**
* 恢复下载
* @param url 下载链接
*/
protected void resume(String url)
{
// 获取当前任务的下载管理类
DownloadManager downloadManager = mapThread.get(url);
if (downloadManager != null)
{
downloadManager.resume();
}else
{
// 等待下载
wait(url);
}
}
/**
* 删除下载
* @param url 下载链接
*/
public void delete(String url)
{
// 获取当前任务的下载管理类
DownloadManager downloadManager = mapThread.get(url);
if (downloadManager != null)
{
// 执行删除操作
downloadManager.delete();
// 正在进行中任务列表移除url
mapThread.remove(url);
return;
}
// 等待任务列表
if (mWaitThread.containsKey(url))
{
mWaitThread.remove(url);
listener.delete(url);
return;
}
// 删除已经下载完成或者下载失败的
DownloadInfo info = FileDown.getFileDown().getDaoSession().getDownloadInfoDao().queryBuilder().where(DownloadInfoDao.Properties.Url.eq(url)).unique();
if (info == null) return;
listener.delete(info.getUrl());
// 删除已下载下载的文件和数据库中的文件
FileUtil.deleteFile(info.getLocalPath());
FileDown.getFileDown().getDaoSession().getDownloadInfoDao().delete(info);
}
/**
* 等待下载
* @param url
*/
protected void wait(String url)
{
// 当前进行的线程数量 大于 最大允许进行的最大线程
// 下载任务需要等待
listener.wait(url);
// 加入等待队列
mWaitThread.put(url,url);
}
/**
* 批量开始任务:开启多任务下载
* @param list
*/
protected void start(List list)
{
// 判断任务列表是否为空
if (list.isEmpty()) return;
for (String url:list)
{
// 开始任务
if (mapThread.containsKey(url))
{
// 之前已经添加过的直接恢复下载
resume(url);
}else
{
// 没有添加过的,开始第一次下载
start(url);
}
}
}
/**
* 批量暂停任务
* @param list
*/
protected void pause(List list)
{
// 判断暂停任务
if (list.isEmpty()) return;
for (String url : list)
{
pause(url);
}
}
/**
* 删除所有的任务
* {只能删除进行和等待下载的任务,如果任务完成或者是下载失败,则需要用户自行删除}
*/
protected void deleteAllTask()
{
// 首先删除正在进行的
for (String url: mapThread.keySet())
{
delete(url);
}
// 然后删除等待中的
for (String url:mWaitThread.keySet())
{
delete(url);
}
// 清空本地数据存储库
List list = FileDown.getFileDown().getDaoSession().getDownloadInfoDao().queryBuilder().list();
if (list == null) return;
for (DownloadInfo info : list)
{
delete(info.getUrl());
}
}
/**
* 暂停所有的进行中的任务
*/
protected void pauseAllTash()
{
// 首先暂停正在进行的
for (String url: mapThread.keySet())
{
pause(url);
}
// 然后暂停等待中的
for (String url:mWaitThread.keySet())
{
pause(url);
}
}
DownloadListener listener = new DownloadListener() {
@Override
public void start(String url) {
// 开始下载
sendMessage(DownloadConfig.STATUS_START,url,0,0,false,"","",0);
}
@Override
public void pause(String url) {
// 暂停下载
nextTask(url,DownloadConfig.STATUS_PAUSE);
sendMessage(DownloadConfig.STATUS_PAUSE,url,0,0,false,"","",0);
}
@Override
public void progress(long read, long contentLength, boolean done, String url, long speed) {
// 回掉进度
// Log.d(TAG,"已下载:"+read+";总大小:"+contentLength);
sendMessage(DownloadConfig.STATUS_DOWNNING,url,read,contentLength,false,"","",speed);
}
@Override
public void wait(String url) {
// 等待下载
sendMessage(DownloadConfig.STATUS_WAIT,url,0,0,false,"","",0);
}
@Override
public void complete(String url, File file) {
// 下载完成
nextTask(url,DownloadConfig.STATUS_COMPLETE);
sendMessage(DownloadConfig.STATUS_COMPLETE,url,0,0,true,file.getAbsolutePath(),"",0);
}
@Override
public void error(String url, String msg) {
// 下载错误
nextTask(url,DownloadConfig.STATUS_ERROR);
sendMessage(DownloadConfig.STATUS_ERROR,url,0,0,false,"",msg,0);
}
@Override
public void delete(String url) {
nextTask(url,DownloadConfig.STATUS_DELETE);
sendMessage(DownloadConfig.STATUS_DELETE,url,0,0,false,"","",0);
}
};
/**
* 发送消息
* @param downStatus 下载状态
* @param url 下载连接
* @param read 已下载
* @param contentLength 总大小
* @param done 是否下载完成
* @param localPath
* @param errMsg 错误消息
* @param speed 下载速度 kb/s
*/
protected void sendMessage(int downStatus, String url,long read, long contentLength, boolean done, String localPath, String errMsg ,
long speed)
{
Intent intent = new Intent();
intent.putExtra(DownloadConfig.URL,url);
switch (downStatus)
{
case DownloadConfig.STATUS_START: // 开始下载
intent.setAction(DownloadConfig.RECEIVER_START);
break;
case DownloadConfig.STATUS_PAUSE: // 暂停下载
intent.setAction(DownloadConfig.RECEIVER_PASUE);
break;
case DownloadConfig.STATUS_RESUME: // 恢复下载
intent.setAction(DownloadConfig.RECEIVER_RESUME);
break;
case DownloadConfig.STATUS_WAIT: // 等待下载
intent.setAction(DownloadConfig.RECEIVER_WAIT);
break;
case DownloadConfig.STATUS_DOWNNING: // 下载中
intent.setAction(DownloadConfig.RECEIVER_DOWNNING);
// 传递已下载的进度
intent.putExtra(DownloadConfig.READ,read);
// 传递总大小
intent.putExtra(DownloadConfig.TOTAL,contentLength);
// 传输速度
intent.putExtra(DownloadConfig.SPEED,speed);
// 是否下载完成
intent.putExtra(DownloadConfig.IS_COMPLETE,done);
break;
case DownloadConfig.STATUS_COMPLETE: // 下载完成
intent.setAction(DownloadConfig.RECEIVER_COMPLETE);
intent.putExtra(DownloadConfig.LOCAL_PATH,localPath);
break;
case DownloadConfig.STATUS_ERROR: // 下载错误
intent.setAction(DownloadConfig.RECEIVER_ERROR);
intent.putExtra(DownloadConfig.ERROR_MESSAGE,errMsg);
break;
case DownloadConfig.STATUS_DELETE:
intent.setAction(DownloadConfig.RECEIVER_DELETE);
break;
default:
break;
}
// 发送本地广播
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
// sendBroadcast(intent);
}
/**
* 执行下一个任务
* @param url
* @param downStatus 下载状态
*/
protected void nextTask(String url, int downStatus)
{
// 如果是下载完成或者是下载错误,从已下载集合中移除暂停或者下载完成的任务
if (downStatus == DownloadConfig.STATUS_COMPLETE || downStatus == DownloadConfig.STATUS_ERROR
|| downStatus == DownloadConfig.STATUS_DELETE)
{
mapThread.remove(url);
}
// 处理排队的任务
String nextUrl = getKeyOrNull(mWaitThread);
if (TextUtils.isEmpty(nextUrl))
{
// 未取出第一个元素
// 全部任务完成,或只有三个任务
// TODO
return;
}
// 如果是暂停状态,且后面有排队的任务,从进行中任务移除暂停的任务
if (downStatus == DownloadConfig.STATUS_PAUSE)
{
mapThread.remove(url);
}
// 从待下载集合中移除
mWaitThread.remove(nextUrl);
// 开始执行下一个任务
start(nextUrl);
}
/**
* 获取第一个待下载的请求
* @param map
* @return
*/
private String getKeyOrNull(Map map) {
String obj = null;
for (Map.Entry entry : map.entrySet()) {
obj = entry.getKey();
if (obj != null) {
break;
}
}
return obj;
}
}
最后建立我们的单例帮助类DownloadHelper:
public class DownloadHelper {
// 帮助类对象
private static DownloadHelper mInstance;
// 监听Listener
protected DownloadListener progressListener;
// 本地保存路径
protected String mSavePath;
// 允许同时下载的最大线程数
protected int maxThreadTaskCount = DownloadConfig.MAX_DOWN_THREAD_COUNT;
private DownloadHelper()
{
}
public static DownloadHelper getInstance()
{
if (mInstance == null)
{
synchronized (DownloadHelper.class)
{
mInstance = new DownloadHelper();
}
}
return mInstance;
}
/**
*
* @param localPath 设置本地保存路径
*/
public void setSavePath(String localPath)
{
this.mSavePath = localPath;
}
/**
* 设置同时下载的最大线程数量,默认三个
* @param count
*/
public void setMaxTask(int count)
{
this.maxThreadTaskCount = count;
}
/**
* 注册监听
* @param listener 回掉监听
*/
public void registerListener(DownloadListener listener)
{
this.progressListener = listener;
// 注册广播
IntentFilter filter = new IntentFilter();
filter.addAction(DownloadConfig.RECEIVER_START);
filter.addAction(DownloadConfig.RECEIVER_PASUE);
filter.addAction(DownloadConfig.RECEIVER_RESUME);
filter.addAction(DownloadConfig.RECEIVER_WAIT);
filter.addAction(DownloadConfig.RECEIVER_COMPLETE);
filter.addAction(DownloadConfig.RECEIVER_DOWNNING);
filter.addAction(DownloadConfig.RECEIVER_ERROR);
filter.addAction(DownloadConfig.RECEIVER_DELETE);
LocalBroadcastManager.getInstance(FileDown.getInstances()).registerReceiver(receiver,filter);
}
/**
* 解除监听,页面销毁时一定要调用
*/
public void unRegisterListener()
{
this.progressListener = null;
// 解除广播
LocalBroadcastManager.getInstance(FileDown.getInstances()).unregisterReceiver(receiver);
}
/**
* 下载的广播接收器
*/
BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// 获取action,根据不同的状态处理相关操作
String action = intent.getAction();
// 获取下载链接
String url = intent.getStringExtra(DownloadConfig.URL);
if (action.equals(DownloadConfig.RECEIVER_START))
{
// 下载开始
progressListener.start(url);
}else if (action.equals(DownloadConfig.RECEIVER_PASUE))
{
// 下载暂停
progressListener.pause(url);
}else if (action.equals(DownloadConfig.RECEIVER_RESUME))
{
// 恢复下载
}else if (action.equals(DownloadConfig.RECEIVER_DOWNNING))
{
// 下载中
long read = intent.getLongExtra(DownloadConfig.READ,0);
long total = intent.getLongExtra(DownloadConfig.TOTAL,0);
long speed = intent.getLongExtra(DownloadConfig.SPEED,0);
boolean isComplete = intent.getBooleanExtra(DownloadConfig.IS_COMPLETE,false);
progressListener.progress(read,total,isComplete,url,speed);
}else if (action.equals(DownloadConfig.RECEIVER_COMPLETE))
{
// 下载完成
String localPath = intent.getStringExtra(DownloadConfig.LOCAL_PATH);
File file;
if (TextUtils.isEmpty(localPath))
{
file = null;
}else
{
file = new File(localPath);
}
progressListener.complete(url,file);
}else if (action.equals(DownloadConfig.RECEIVER_WAIT))
{
// 下载等待
progressListener.wait(url);
}else if (action.equals(DownloadConfig.RECEIVER_ERROR))
{
// 下载错误
String errorMsg = intent.getStringExtra(DownloadConfig.ERROR_MESSAGE);
progressListener.error(url,errorMsg);
}else if (action.equals(DownloadConfig.RECEIVER_DELETE))
{
// 删除下载
progressListener.delete(url);
}
}
};
/**
* 开始下载
* @param url
*/
public void start(String url)
{
startService(url,DownloadConfig.ACTION_START,null);
}
/**
* 开始多个任务下载
* @param list 任务列表
*/
public void start(List list)
{
startService("",DownloadConfig.ACTION_START_ALL, list);
}
/**
* 暂停下载
* @param url 请求连接
*/
public void pause(String url)
{
startService(url,DownloadConfig.ACTION_PAUSE,null);
}
/**
* 暂停所有进行中的任务
*/
public void pauseAll()
{
startService("",DownloadConfig.ACTION_PAUSE_ALL,null);
}
/**
* 恢复下载
* @param url
*/
public void resume(String url)
{
startService(url,DownloadConfig.ACTION_RESUME,null);
}
/**
* 重新下载 {暂未实现,可直接调用start方法}
* @param url
*/
private void restart(String url)
{
startService(url,DownloadConfig.ACTION_RESTART,null);
}
/**
* 删除下载
* @param url
*/
public void delete(String url)
{
startService(url,DownloadConfig.ACTION_DELETE,null);
}
/**
* 删除所有进行中的任务
*/
public void deleteAll()
{
startService("",DownloadConfig.ACTION_DELETE_ALL,null);
}
/**
* 根据下载链接查询本地存储文件的绝对路径
* @param url 下载链接
* @return
*/
public String getLocalFilePathFromUrl(String url)
{
String localPath;
DownloadInfo info = FileDown.getFileDown().getDaoSession().getDownloadInfoDao().queryBuilder().where(DownloadInfoDao.Properties.Url.eq(url)).unique();
if (info == null)
{
localPath = null;
} else
{
localPath = info.getLocalPath();
}
return localPath;
}
/**
* 启动服务
* @param url
* @param action
*/
protected void startService(String url,int action,List list)
{
Intent intent = new Intent(FileDown.getInstances(),DownloadManngerService.class);
intent.putExtra(DownloadConfig.ACTION,action);
intent.putExtra(DownloadConfig.URL,url);
// 线程数量和保存位置
intent.putExtra(DownloadConfig.LOCAL_PATH,mSavePath);
intent.putExtra(DownloadConfig.MAX_THREAD_COUNT,maxThreadTaskCount);
if (list != null)
{
intent.putStringArrayListExtra(DownloadConfig.URL_ARRAY, (ArrayList) list);
}
FileDown.getInstances().startService(intent);
}
}
完整代码和使用方法请移步github主页,欢迎start。https://github.com/zlwmzh/FileDown