在Android开发中有时候会遇到需要对下载过程进行一些限制,比如只能同时下载一个文件或者允许同时下载N个文件的情况,但是不能让用户在下载完成一个或几个之后才能再次点击。因此需要对下载任务进行保存,在下载完一个后自动下载任务队列中的下一个。这种情况也是我在实际开发中遇到过的,因此分享出来,有些地方可能还不完善,但基本使用没有问题。
public class DownloadManager {
private static final String TAG = "DownloadManager";
public class DownLoadTask {
public String url;
public String localPath;
public IDownloadListener listener;
}
private static DownloadManager mInstance = null;
private Context mContext;
private List<DownLoadTask> mDownloadWaitings = new ArrayList<DownLoadTask>(); //等待下载的任务
private List<DownLoadTask> mDownLoadings = new ArrayList<DownLoadTask>(); //正在下载的任务
private static final int MAX_NUM = 1; //正在下载的最大下载任务数
private DownloadManager(Context context) {
this.mContext = context;
}
public synchronized static DownloadManager getInstance(Context context) {
if (null == mInstance) {
mInstance = new DownloadManager(context);
}
return mInstance;
}
private synchronized void pushList(DownLoadTask info) {
mDownloadWaitings.add(info);
}
private synchronized DownLoadTask popList() {
if (mDownloadWaitings.size() == 0) {
return null;
}
return mDownloadWaitings.remove(0);
}
public DownLoadTask findTask(String url){
for(int i = 0; i < mDownloadWaitings.size(); ++ i){
if(mDownloadWaitings.get(i).url.equals(url)){
return mDownloadWaitings.get(i);
}
}
for(int i = 0; i < mDownLoadings.size(); ++ i){
if(mDownLoadings.get(i).url.equals(url)){
return mDownLoadings.get(i);
}
}
return null;
}
public synchronized boolean isDownloadWaiting(String url){
if(mDownloadWaitings.size() == 0)return false;
for(int i = 0; i < mDownloadWaitings.size(); ++ i){
String murl = mDownloadWaitings.get(i).url;
if(murl.compareTo(url) == 0){
return true;
}
}
return false;
}
public synchronized boolean isDownloading(String url){
if(mDownLoadings.size() == 0)return false;
for(int i = 0; i < mDownLoadings.size(); ++ i){
String murl = mDownLoadings.get(i).url;
if(murl.compareTo(url) == 0){
return true;
}
}
return false;
}
public synchronized boolean isDownloadingOrWaiting(String url){
List<DownLoadTask> loadingOrWaitingList = new ArrayList<DownLoadTask>();
loadingOrWaitingList.addAll(mDownloadWaitings);
loadingOrWaitingList.addAll(mDownLoadings);
if(loadingOrWaitingList.size() == 0)return false;
for(int i = 0;i < loadingOrWaitingList.size(); ++ i){
String murl = loadingOrWaitingList.get(i).url;
if(murl.compareTo(url) == 0){
return true;
}
}
return false;
}
public synchronized boolean isDownloading(){
if(mDownLoadings.size() != 0)
return true;
return false;
}
/**
* 取消下载任务
*/
public void cancelTask(DownLoadTask task) {
if(task == null){
return;
}
if (mDownloadWaitings.contains(task)) {
Log.d(TAG, "cancelTask() waiting");
// 下载任务排队中,删除任务;
mDownloadWaitings.remove(task);
} else {
Log.d(TAG, "cancelTask() loading. task.url: " + task.url);
cancelDownloadingTask(task);
}
}
public void cancelAllTask(){
mDownloadWaitings.clear();
for(DownLoadTask task : mDownLoadings){
cancelDownloadingTask(task);
}
mDownLoadings.clear();
}
private void cancelDownloadingTask(DownLoadTask task){
HttpUtil.cancelByTag(task.url); //取消正在下载的任务
}
/**
* 添加下载任务
*
* @param downloadUrl 下载地址
* @param savePath 文件保存目录
* @param listener 下载监听器
* @return ThumbLoadTask 返回当前下载任务实例
*/
public DownLoadTask addDownloadTask(String downloadUrl, String savePath, IDownloadListener listener) {
synchronized (this) {
if (TextUtils.isEmpty(downloadUrl)) {
if (listener != null) {
listener.onFailure("下载地址不能为空");
}
return null;
}
if (TextUtils.isEmpty(savePath)) {
if (listener != null) {
listener.onFailure("文件保存目录不能为空");
}
return null;
}
if (listener != null) {
listener.onWaiting();
}
// Add it to list.
DownLoadTask info = new DownLoadTask();
info.url = downloadUrl;
info.localPath = savePath;
info.listener = listener;
pushList(info);
return info;
}
}
/**
* 开始下载
*/
public void startDownload() {
int currNum = mDownLoadings.size();
Log.d(TAG, "startDownload(). currNum: " + currNum);
if(currNum < MAX_NUM) {
DownLoadTask task = popList();
if (task != null) {
mDownLoadings.add(task);
download(mContext, task);
}
}
}
private void changeLoadingByFinish(DownLoadTask task){
mDownLoadings.remove(task);
}
private void download(Context context, final DownLoadTask task) {
String url = task.url;
String localFilePath = task.localPath;
final IDownloadListener listener = task.listener;
Log.d(TAG, "download() begin. url: " + url);
if (TextUtils.isEmpty(url) || TextUtils.isEmpty(localFilePath) || listener == null) {
Log.e(TAG, "download information is not complete.");
return;
}
final File file = new File(localFilePath);
if (file.exists()) {
Log.e(TAG, "File is already exist.");
listener.onSuccess(file);
return;
}
Object tag = url;
new HttpEngine().downloadFile(context, url, localFilePath, new IDownloadTagListener() {
int lastPercent = 0;
long lastTime = 0;
@Override
public void onStart() {
listener.onStart();
}
@Override
public void onProgress(long bytesWritten, long totalSize) {
int currentPercent = (int) ((bytesWritten * 100) / totalSize);
long currentTime = System.currentTimeMillis();
// 为防止UI刷新过快,当只有更新百分比大于百分之一 且 更新时间与上次更新时间相差大于500ms 时才进行回调
if((currentPercent - lastPercent) > 1 && (currentTime - lastTime) > 500) {
listener.onProgress(bytesWritten, totalSize);
lastPercent = currentPercent;
lastTime = currentTime;
}
}
@Override
public void onSuccess(File file) {
changeLoadingByFinish(task);
listener.onSuccess(file);
startDownload();
}
@Override
public void onFailure(File file, String error) {
changeLoadingByFinish(task);
listener.onFailure(error);
startDownload();
}
@Override
public void onCancel() {
changeLoadingByFinish(task);
listener.onCancel();
startDownload();
}
}, tag);
}
}
public interface IDownloadListener {
/**
* 传输等待
*/
public void onWaiting();
/**
* 传输开始
*/
public void onStart();
/**
* 传输进行中
*
* @param totalSize 数据总量
* @param currentSize 已下载数据量
*/
public void onProgress(long currentSize, long totalSize);
/**
* 传输完成
*
* @param file 文件对象
*/
public void onSuccess(File file);
/**
* 传输错误
*
* @param error 错误信息
*/
public void onFailure(String error);
/**
* 取消下载
*/
public void onCancel();
}
其中
new HttpEngine().downloadFile(context, url, localFilePath, new IDownloadTagListener(){}, tag);
方法是具体的下载方法,可以自己实现,也可以使用开源网络库进行实现,本文只讨论下载管理,对具体下载实现不做讨论。