Android 檔案下載(斷點、多工並行下載)
以下程式碼是基於百度雲網盤:http://pan.baidu.com/s/1dD1Xo8T 中的demo進行優化及功能新增。
以下程式碼實現功能有:多執行緒下載、多工並行下載以及下載進度和下載速度的顯示等功能。
實現思路:根據執行緒數分割待下載檔案;利用HttpURLConnection實現各部分檔案的下載;利用RandomAccessFile實現下載內容的儲存;各執行緒下載任務資訊儲存在資料庫,以便暫停和恢復下載。
demo已上傳到github:https://github.com/shichaohui/FileDownloadDemo.git 歡迎下載。
效果圖:
主要程式碼
DownLoadHelper.java:
package com.example.test;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
/**
* 利用資料庫來記錄下載資訊
*
* @author [email protected]
*/
public class DownLoadHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "download.db" ;
private static final String TB_NAME = "download_info";
private static final int DOWNLOAD_VERSION = 1;
public DownLoadHelper(Context context) {
super(context, DB_NAME, null, DOWNLOAD_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table "
+ TB_NAME
+ "(_id integer PRIMARY KEY AUTOINCREMENT, thread_id integer, "
+ "start_pos integer, end_pos integer, compelete_size integer,url char)");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
這個類沒什麼好說的,就是建立資料庫和資料表。
DownlaodSqlTool.java
package com.example.test;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
/**
* 資料庫操作工具類
*
* @author [email protected]
*/
public class DownlaodSqlTool {
private static DownlaodSqlTool instance = null;
private DownLoadHelper dbHelper = null;
private DownlaodSqlTool(Context context) {
dbHelper = new DownLoadHelper(context);
}
private static synchronized void syncInit(Context context) {
if (instance == null) {
instance = new DownlaodSqlTool(context);
}
}
public static DownlaodSqlTool getInstance(Context context) {
if (instance == null) {
syncInit(context);
}
return instance;
}
/** 將下載的進度等資訊儲存到資料庫 */
public void insertInfos(List<DownloadInfo> infos) {
SQLiteDatabase database = dbHelper.getWritableDatabase();
for (DownloadInfo info : infos) {
String sql = "insert into download_info(thread_id,start_pos, end_pos,compelete_size,url) values (?,?,?,?,?)";
Object[] bindArgs = { info.getThreadId(), info.getStartPos(),
info.getEndPos(), info.getCompeleteSize(), info.getUrl() };
database.execSQL(sql, bindArgs);
}
}
/** 獲取下載的進度等資訊 */
public List<DownloadInfo> getInfos(String urlstr) {
List<DownloadInfo> list = new ArrayList<DownloadInfo>();
SQLiteDatabase database = dbHelper.getWritableDatabase();
String sql = "select thread_id, start_pos, end_pos,compelete_size,url from download_info where url=?";
Cursor cursor = database.rawQuery(sql, new String[] { urlstr });
while (cursor.moveToNext()) {
DownloadInfo info = new DownloadInfo(cursor.getInt(0),
cursor.getInt(1), cursor.getInt(2), cursor.getInt(3),
cursor.getString(4));
list.add(info);
}
cursor.close();
return list;
}
/** 更新資料庫中的下載資訊 */
public void updataInfos(int threadId, int compeleteSize, String urlstr) {
SQLiteDatabase database = dbHelper.getWritableDatabase();
String sql = "update download_info set compelete_size=? where thread_id=? and url=?";
Object[] bindArgs = { compeleteSize, threadId, urlstr };
database.execSQL(sql, bindArgs);
}
/** 關閉資料庫 */
public void closeDb() {
dbHelper.close();
}
/** 刪除資料庫中的資料 */
public void delete(String url) {
SQLiteDatabase database = dbHelper.getWritableDatabase();
database.delete("download_info", "url=?", new String[] { url });
}
}
單例模式的資料庫操作類,主要實現資料的增刪改查等操作。
DownloadInfo.java
package com.example.test;
/**
* 儲存每個下載執行緒下載資訊類
*
* @author [email protected]
*/
public class DownloadInfo {
private int threadId; // 下載執行緒的id
private int startPos; // 開始點
private int endPos; // 結束點
private int compeleteSize; // 完成度
private String url; // 下載檔案的URL地址
/**
*
* @param threadId
* 下載執行緒的id
* @param startPos
* 開始點
* @param endPos
* 結束點
* @param compeleteSize
* // 已下載的大小
* @param url
* 下載地址
*/
public DownloadInfo(int threadId, int startPos, int endPos,
int compeleteSize, String url) {
this.threadId = threadId;
this.startPos = startPos;
this.endPos = endPos;
this.compeleteSize = compeleteSize;
this.url = url;
}
public DownloadInfo() {
}
/** 獲取下載地址 */
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
/** 獲取下載執行緒的Id */
public int getThreadId() {
return threadId;
}
public void setThreadId(int threadId) {
this.threadId = threadId;
}
/** 獲取下載的開始位置 */
public int getStartPos() {
return startPos;
}
public void setStartPos(int startPos) {
this.startPos = startPos;
}
/** 獲取下載的結束位置 */
public int getEndPos() {
return endPos;
}
public void setEndPos(int endPos) {
this.endPos = endPos;
}
/** 獲取已下載的大小 */
public int getCompeleteSize() {
return compeleteSize;
}
public void setCompeleteSize(int compeleteSize) {
this.compeleteSize = compeleteSize;
}
@Override
public String toString() {
return "DownloadInfo [threadId=" + threadId + ", startPos=" + startPos
+ ", endPos=" + endPos + ", compeleteSize=" + compeleteSize
+ "]";
}
}
下載實體類,針對於單個下載執行緒,儲存下載執行緒對應的檔案相關資訊,比如當前下載執行緒負責下載的部分是從檔案的哪個點開始的(startPos)、哪個點結束的(endPos)以及當前已經下載了多少(compeleteSize)等資訊。
DownloadingInfo.java
package com.example.test;
/**
* 某一任務正在下載時的資訊
*
* @author [email protected]
*
*/
public class DownloadingInfo {
private String kbps = "0"; // 每秒下載速度
private int secondSize = 0; // 一秒鐘累計下載量
private int fileSize = 0; // 檔案大小
public String getKbps() {
return kbps;
}
public void setKbps(String kbps) {
this.kbps = kbps;
}
public int getSecondSize() {
return secondSize;
}
public void setSecondSize(int secondSize) {
this.secondSize = secondSize;
}
public int getFileSize() {
return fileSize;
}
public void setFileSize(int fileSize) {
this.fileSize = fileSize;
}
@Override
public String toString() {
return "DownloadingInfo [kbps=" + kbps + ", secondSize=" + secondSize
+ ", fileSize=" + fileSize + "]";
}
}
這也是一個下載相關的實體類,針對於一個下載任務(包括多個下載執行緒)。儲存下載任務的下載進度、速度等用於客戶端顯示的資料。
DownloadHttpTool.java
package com.example.test;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
/**
* 利用Http協議進行多執行緒下載具體實現類
*
* @author [email protected]
*/
public class DownloadHttpTool {
private final int THREAD_COUNT = 2; // 執行緒數量
private String urlstr = ""; // URL地址
private Context mContext = null;
private List<DownloadInfo> downloadInfos = null; // 儲存下載資訊的類
/** 下載檔案儲存路徑 */
public static String filePath = ""; // 目錄
private String fileName = ""; // 檔名
private String fileNameTmp = ""; // 臨時檔名
/** 臨時檔名字尾 */
public static final String FILE_TMP_SUFFIX = ".tmp";
private int fileSize = 0; // 檔案大小
private DownlaodSqlTool sqlTool = null; // 檔案資訊儲存的資料庫操作類
private DownloadComplated downloadComplated = null;
private int totalCompelete = 0;// 所有執行緒已下載的總數
private List<DownloadThread> threads = null; // 下載執行緒
private Handler handler = null;
// 利用列舉表示下載的幾種狀態
private enum Download_State {
Downloading, Pause, Ready, Compeleted, Exception;
}
private Download_State state = Download_State.Ready; // 當前下載狀態
/**
* @param context
* 上下文物件
* @param downloadComplated
*/
public DownloadHttpTool(Context context, Handler handler,
DownloadComplated downloadComplated) {
super();
this.mContext = context;
this.handler = handler;
this.downloadComplated = downloadComplated;
sqlTool = DownlaodSqlTool.getInstance(mContext);
if ("".equals(filePath)) {
// TODO 根據有無sdcard設定路徑
filePath = Environment.getExternalStorageDirectory()
.getAbsolutePath() + "/meiriq-download";
}
threads = new ArrayList<DownloadThread>();
}
/**
* 開始下載
*
* @param url
* 下載地址
*/
public void start(String urlstr) {
this.urlstr = urlstr;
String[] ss = urlstr.split("/");
fileName = ss[ss.length - 1];
fileNameTmp = fileName + FILE_TMP_SUFFIX;
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... arg0) {
// 下載之前首先非同步執行緒呼叫ready方法做下載的準備工作
ready();
Message msg = new Message();
msg.what = 1;
msg.arg1 = fileSize;
msg.obj = DownloadHttpTool.this.urlstr;
handler.sendMessage(msg);
return null;
}
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
// 開始下載
startDownload();
}
}.execute();
}
/** 在開始下載之前需要呼叫ready方法進行配置 */
private void ready() {
if (new File(filePath + "/" + fileName).exists()) {
downloadComplated.onComplated(urlstr);
return;
}
totalCompelete = 0;
downloadInfos = sqlTool.getInfos(urlstr);
if (downloadInfos.size() == 0) { // 資料庫中沒有相關資訊
initFirst();
} else {
File file = new File(filePath + "/" + fileNameTmp);
if (!file.exists()) {
sqlTool.delete(urlstr);
initFirst();
} else {
fileSize = downloadInfos.get(downloadInfos.size() - 1)
.getEndPos();
for (DownloadInfo info : downloadInfos) {
totalCompelete += info.getCompeleteSize();
}
}
}
}
/** 開始下載 */
private void startDownload() {
if (downloadInfos != null) {
if (state == Download_State.Downloading) {
return;
}
state = Download_State.Downloading;
for (DownloadInfo info : downloadInfos) { // 開啟執行緒下載
DownloadThread thread = new DownloadThread(info.getThreadId(),
info.getStartPos(), info.getEndPos(),
info.getCompeleteSize(), info.getUrl());
thread.start();
threads.add(thread);
}
}
}
/** 暫停當前下載任務 */
public void pause() {
state = Download_State.Pause;
}
/** 刪除當前下載任務 */
public void delete() {
compeleted();
File file = new File(filePath + "/" + fileNameTmp);
file.delete();
}
/** 完成下載 */
private void compeleted() {
state = Download_State.Compeleted;
sqlTool.delete(urlstr);
downloadComplated.onComplated(urlstr);
}
/** 獲取目標檔案大小 */
public int getFileSize() {
return fileSize;
}
/** 獲取當前下載的大小 */
public int getTotalCompeleteSize() {
return totalCompelete;
}
/** 第一次下載時進行的初始化 */
private void initFirst() {
URL url = null;
RandomAccessFile accessFile = null;
HttpURLConnection connection = null;
try {
url = new URL(urlstr);
connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(5000);
connection.setRequestMethod("GET");
fileSize = connection.getContentLength();
if (fileSize < 0) {
return;
}
File fileParent = new File(filePath);
if (!fileParent.exists()) {
fileParent.mkdir();
}
File file = new File(fileParent, fileNameTmp);
if (!file.exists()) {
file.createNewFile();
}
// 隨機訪問檔案
accessFile = new RandomAccessFile(file, "rwd");
accessFile.setLength(fileSize);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (accessFile != null) {
try {
accessFile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (connection != null) {
connection.disconnect();
}
}
// 計算每個執行緒需要下載的大小
int range = fileSize / THREAD_COUNT;
// 儲存每個執行緒的下載資訊
downloadInfos = new ArrayList<DownloadInfo>();
for (int i = 0; i < THREAD_COUNT - 1; i++) {
DownloadInfo info = new DownloadInfo(i, i * range, (i + 1) * range
- 1, 0, urlstr);
downloadInfos.add(info);
}
// 最後一個執行緒和前面的處理有點不一樣
DownloadInfo info = new DownloadInfo(THREAD_COUNT - 1,
(THREAD_COUNT - 1) * range, fileSize - 1, 0, urlstr);
downloadInfos.add(info);
// 插入到資料庫
sqlTool.insertInfos(downloadInfos);
}
interface DownloadComplated {
/**
* 下載完成回撥
*
* @param urlString
*/
void onComplated(String urlString);
}
/** 自定義下載執行緒 */
private class DownloadThread extends Thread {
private int threadId = 0; // 執行緒Id
private int startPos = 0; // 在檔案中的開始的位置
private int endPos = 0; // 在檔案中的結束的位置
private int compeleteSize = 0; // 已完成下載的大小
private String urlstr = ""; // 下載地址
/**
*
* @param threadId
* 執行緒Id
* @param startPos
* 在檔案中的開始的位置
* @param endPos
* 在檔案中的結束的位置
* @param compeleteSize
* 已完成下載的大小
* @param urlstr
* 下載地址
*/
public DownloadThread(int threadId, int startPos, int endPos,
int compeleteSize, String urlstr) {
this.threadId = threadId;
this.startPos = startPos;
this.endPos = endPos;
this.urlstr = urlstr;
this.compeleteSize = compeleteSize;
}
@Override
public void run() {
HttpURLConnection connection = null;
RandomAccessFile randomAccessFile = null;
InputStream is = null;
try {
randomAccessFile = new RandomAccessFile(filePath + "/"
+ fileNameTmp, "rwd");
randomAccessFile.seek(startPos + compeleteSize);
URL url = new URL(urlstr);
connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(5000);
connection.setRequestMethod("GET");
// 設定請求的資料的範圍
connection.setRequestProperty("Range", "bytes="
+ (startPos + compeleteSize) + "-" + endPos);
is = connection.getInputStream();
byte[] buffer = new byte[6 * 1024]; // 6K的快取
int length = -1;
while ((length = is.read(buffer)) != -1) {
randomAccessFile.write(buffer, 0, length); // 寫快取資料到檔案
compeleteSize += length;
synchronized (this) { // 加鎖保證已下載的正確性
totalCompelete += length;
Message msg = new Message();
msg.what = 0;
msg.arg1 = length;
msg.arg2 = totalCompelete;
msg.obj = urlstr;
handler.sendMessage(msg);
}
// 非正在下載狀態時跳出迴圈
if (state != Download_State.Downloading) {
break;
}
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("異常退出____" + urlstr);
state = Download_State.Exception;
} finally {
// 不管發生了什麼事,都要儲存下載資訊到資料庫
sqlTool.updataInfos(threadId, compeleteSize, urlstr);
if (threads.size() == 1) { // 當前執行緒是此url對應下載任務唯一一個正在執行的執行緒
try {
if (is != null) {
is.close();
}
if (randomAccessFile != null) {
randomAccessFile.close();
}
if (connection != null) {
connection.disconnect();
}
} catch (Exception e) {
e.printStackTrace();
}
if (state == Download_State.Downloading) { // 此時此執行緒的下載任務正常完成(沒有被人為或異常中斷)
File file = new File(filePath + "/" + fileNameTmp);
file.renameTo(new File(filePath + "/" + fileName));
}
if (state != Download_State.Pause) {
compeleted();
}
}
threads.remove(this);
}
}
}
}
一個DownloadHttpTool的例項表示一個下載任務,一個下載任務中可以有多個下載執行緒,可以通過修改常量THREAD_COUNT
的方式修改一個下載任務的下載執行緒數。檔案的儲存路徑是在sdcard中的meiriq-download資料夾,也可以修改到其他路徑。
此類中在下載開始的時候首先會執行ready()
方法獲取檔案相關的資訊,之後執行startDownload()
開啟下載執行緒執行下載。
下載時使用HttpURLConnection類的setRequestProperty
方法指定請求頭欄位實現檔案的隨機下載(下載從某一個點開始到某一個點結束之際的內容),使用RandomAccessFile實現檔案的隨機訪問(可以從某一個點開始寫入資料)。
為了保證下載速度,寫入資料庫的操作並不是每次寫入檔案之後都執行,而是在下載出現異常或者暫停等操作之後才寫入資料庫。所有下載任務全部結束後執行關閉資料流等操作。
DownloadUtil.java
package com.example.test;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import com.example.test.DownloadHttpTool.DownloadComplated;
/**
* 將下載方法封裝在此類 提供開始、暫停、刪除以及重置的方法。<br>
* 通過修改常量{@link DownloadUtil#MAX_COUNT}可改變最大並行下載任務量
*
* @author [email protected]
*/
public class DownloadUtil {
private static DownloadUtil instance = null;
private Context context = null;
private List<String> downloadList = null;
private Map<String, DownloadHttpTool> downloadMap = null;
private int currentUrlIndex = -1;
private final int MAX_COUNT = 2; // 最大並行下載量
private int currentCount = 0; // 當前並行下載量
private final String FLAG_FREE = "free"; // 標記downloadMap中空閒的DownloadHttpTool例項
private OnDownloadListener onDownloadListener = null;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
String url = msg.obj.toString();
if (msg.what == 0) {
if (onDownloadListener != null) {
onDownloadListener
.downloadProgress(url, msg.arg2, msg.arg1);
}
} else if (msg.what == 1) {
if (onDownloadListener != null) {
onDownloadListener.downloadStart(url, msg.arg1);
}
} else if (msg.what == 2) {
onDownloadListener.downloadEnd(url);
}
}
};
private DownloadUtil(Context context) {
this.context = context;
downloadList = new ArrayList<String>();
downloadMap = new HashMap<String, DownloadHttpTool>();
}
private static synchronized void syncInit(Context context) {
if (instance == null) {
instance = new DownloadUtil(context);
}
}
public static DownloadUtil getInstance(Context context) {
if (instance == null) {
syncInit(context);
}
return instance;
}
/**
* 下載之前的準備工作,並自動開始下載
*
* @param context
*/
public void prepare(String urlString) {
downloadList.add(urlString);
if (currentCount < MAX_COUNT) {
start();
} else {
System.out.println("等待下載____" + urlString);
}
}
/**
* 開始下載
*/
private synchronized void start() {
if (++currentUrlIndex >= downloadList.size()) {
currentUrlIndex--;
return;
}
currentCount++;
String urlString = downloadList.get(currentUrlIndex);
System.out.println("開始下載____" + urlString);
DownloadHttpTool downloadHttpTool = null;
if (downloadMap.size() < MAX_COUNT) { // 保證downloadMap.size() <= 2
downloadHttpTool = new DownloadHttpTool(context, mHandler,
downloadComplated);
if (downloadMap.containsKey(urlString)) {
downloadMap.remove(urlString);
}
downloadMap.put(urlString, downloadHttpTool);
} else {
downloadHttpTool = downloadMap.get(FLAG_FREE);
downloadMap.remove(FLAG_FREE);
downloadMap.put(urlString, downloadHttpTool);
}
downloadHttpTool.start(urlString);
}
/** 暫停當前下載任務 */
public void pause(String urlString) {
paused(urlString, new Paused() {
@Override
public void onPaused(DownloadHttpTool downloadHttpTool) {
downloadHttpTool.pause();
}
});
}
/** 暫停所有的下載任務 */
public void pauseAll() {
// 如果需要邊遍歷集合邊刪除資料,需要從後向前遍歷,否則會出異常(Caused by:
// java.util.ConcurrentModificationException)
String[] keys = new String[downloadMap.size()];
downloadMap.keySet().toArray(keys);
for (int i = keys.length - 1; i >= 0; i--) {
pause(keys[i]);
}
instance = null;
}
/**
* 恢復當前下載任務
*
* @param urlString
* 要恢復下載的檔案的地址
*/
public void resume(String urlString) {
prepare(urlString);
}
/** 恢復所有的下載任務 */
public void resumeAll() {
for (Entry<String, DownloadHttpTool> entity : downloadMap.entrySet()) {
prepare(entity.getKey());
}
}
/** 刪除當前下載任務 */
public void delete(String urlString) {
boolean bool = paused(urlString, new Paused() {
@Override
public void onPaused(DownloadHttpTool downloadHttpTool) {
downloadHttpTool.pause();
downloadHttpTool.delete();
}
});
if (!bool) { // 下載任務不存在,直接刪除臨時檔案
File file = new File(DownloadHttpTool.filePath + "/"
+ urlString.split("/")[urlString.split("/").length - 1]
+ DownloadHttpTool.FILE_TMP_SUFFIX);
System.out.println(file.delete());
}
}
interface Paused {
void onPaused(DownloadHttpTool downloadHttpTool);
}
/**
* 暫停
*
* @param urlString
* @param paused
* @return 下載任務是否存在的標識
*/
private boolean paused(String urlString, Paused paused) {
if (downloadMap.containsKey(urlString)) {
currentCount--;
DownloadHttpTool downloadHttpTool = downloadMap.get(urlString);
paused.onPaused(downloadHttpTool);
if (!downloadMap.containsKey(FLAG_FREE)) { // 保證key == FLAG_FREE的數量
// = 1
downloadMap.put(FLAG_FREE, downloadHttpTool);
}
downloadMap.remove(urlString);
start();
return true;
}
return false;
}
DownloadComplated downloadComplated = new DownloadComplated() {
@Override
public void onComplated(String urlString) {
System.out.println("下載完成____" + urlString);
Message msg = new Message();
msg.what = 2;
msg.obj = urlString;
mHandler.sendMessage(msg);
pause(urlString);
// 滿足此條件說明全部下載結束
if (downloadMap.size() == 1 && downloadMap.containsKey(FLAG_FREE)) {
System.out.println("全部下載結束");
}
}
};
/** 設定下載監聽 */
public void setOnDownloadListener(OnDownloadListener onDownloadListener) {
this.onDownloadListener = onDownloadListener;
}
/** 下載回撥介面 */
public interface OnDownloadListener {
/**
* 下載開始回撥介面
*
* @param url
* @param fileSize
* 目標檔案大小
*/
public void downloadStart(String url, int fileSize);
/**
* 下載進度回撥介面
*
* @param
* @param downloadedSize
* 已下載大小
* @param lenth
* 本次下載大小
*/
public void downloadProgress(String url, int downloadedSize, int length);
/**
* 下載完成回撥
*
* @param url
*/
public void downloadEnd(String url);
}
}
一個單例的類封了”開始“、”暫停“、”繼續“、”刪除“等下載任務相關操作方法,管理所有下載任務;利用Handler實現下載進度等資訊的更新;常量FLAG_FREE
標識空閒下載任務;可通過修改常量MAX_COUNT
的值的方式修改最大並行下載任務數。
該類管理下載任務的方式:獲取該類例項後呼叫prepare(String urlString)
方法新增下載任務,如果沒有達到最大並行下載數,則會執行start()
開始下載,否則等待其他下載任務下載完成後下載;當一個任務被暫停、刪除或者下載完成後執行start()
開始新的下載。集合downloadMap
儲存所有的下載任務,最多MAX_COUNT
個。當一個下載任務完成後downloadMap
中對應的下載任務變為FLAG_FREE
以便後來的任務重複使用,如果FLAG_FREE
的任務已存在則直接刪除此任務。
MainActivity.java
package com.example.test;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Timer;
import java.util.TimerTask;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.example.test.DownloadUtil.OnDownloadListener;
public class MainActivity extends FragmentActivity implements OnClickListener {
private ListView listView = null;
private List<String> urls = null;
private DownloadUtil downloadUtil = null;
private final String TAG_PROGRESS = "_progress";
private final String TAG_TOTAL = "_total";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView) findViewById(R.id.listview);
urls = new ArrayList<String>();
urls.add("http://pc1.gamedog.cn/big/game/dongzuo/102631/shenmiaotw2_yxdog.apk");
urls.add("http://pc1.gamedog.cn/big/game/yizhi/67450/baoweiluobo_an_yxdog.apk");
urls.add("http://pc1.gamedog.cn/big/game/yizhi/161623/zhiwudzjs2gqb_an.apk");
listView.setAdapter(myAdapter);
downloadUtil = DownloadUtil.getInstance(this);
downloadUtil.setOnDownloadListener(new OnDownloadListener() {
String text = "已下載%sM / 共%sM \n佔比%s \n下載速度%skb/s";
DecimalFormat decimalFormat = new DecimalFormat("#.##"); // 小數格式化
Timer timer = null;
Map<String, DownloadingInfo> downloadingInfos = new HashMap<String, DownloadingInfo>();
@Override
public void downloadStart(String url, int fileSize) {
DownloadingInfo info = new DownloadingInfo();
info.setFileSize(fileSize);
downloadingInfos.put(url, info);
((ProgressBar) listView.findViewWithTag(url + TAG_PROGRESS))
.setMax(fileSize);
}
@Override
public synchronized void downloadProgress(String url,
int downloadedSize, int length) {
DownloadingInfo info = downloadingInfos.get(url);
if (info != null) {
((ProgressBar) listView.findViewWithTag(url + TAG_PROGRESS))
.setProgress(downloadedSize);
((TextView) listView.findViewWithTag(url + TAG_TOTAL)).setText(String.format(
text,
decimalFormat
.format(downloadedSize / 1024.0 / 1024.0),
decimalFormat.format(info.getFileSize() / 1024.0 / 1024.0),
(int) (((float) downloadedSize / (float) info
.getFileSize()) * 100) + "%", info