|
|
@ -39,8 +39,11 @@ final class DownloadUtil implements IDownloadUtil, Runnable { |
|
|
|
/** |
|
|
|
/** |
|
|
|
* 线程数 |
|
|
|
* 线程数 |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private final int THREAD_NUM; |
|
|
|
private int THREAD_NUM; |
|
|
|
private static final long SUB_LEN = 1024 * 100; |
|
|
|
/** |
|
|
|
|
|
|
|
* 小于1m的文件不启用多线程 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private static final long SUB_LEN = 1024 * 1024; |
|
|
|
//下载监听
|
|
|
|
//下载监听
|
|
|
|
private IDownloadListener mListener; |
|
|
|
private IDownloadListener mListener; |
|
|
|
private int mConnectTimeOut = 5000 * 4; //连接超时时间
|
|
|
|
private int mConnectTimeOut = 5000 * 4; //连接超时时间
|
|
|
@ -69,7 +72,7 @@ final class DownloadUtil implements IDownloadUtil, Runnable { |
|
|
|
mListener = downloadListener; |
|
|
|
mListener = downloadListener; |
|
|
|
THREAD_NUM = threadNum; |
|
|
|
THREAD_NUM = threadNum; |
|
|
|
mFixedThreadPool = Executors.newFixedThreadPool(Integer.MAX_VALUE); |
|
|
|
mFixedThreadPool = Executors.newFixedThreadPool(Integer.MAX_VALUE); |
|
|
|
mConstance = new DownloadStateConstance(THREAD_NUM); |
|
|
|
mConstance = new DownloadStateConstance(); |
|
|
|
init(); |
|
|
|
init(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -246,9 +249,61 @@ final class DownloadUtil implements IDownloadUtil, Runnable { |
|
|
|
* 处理断点 |
|
|
|
* 处理断点 |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private void handleBreakpoint(HttpURLConnection conn) throws IOException { |
|
|
|
private void handleBreakpoint(HttpURLConnection conn) throws IOException { |
|
|
|
|
|
|
|
|
|
|
|
//不支持断点只能单线程下载
|
|
|
|
//不支持断点只能单线程下载
|
|
|
|
if (!isSupportBreakpoint) { |
|
|
|
if (!isSupportBreakpoint) { |
|
|
|
|
|
|
|
handleNoSupportBreakpointDownload(conn); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
int fileLength = conn.getContentLength(); |
|
|
|
|
|
|
|
if (fileLength < SUB_LEN) { |
|
|
|
|
|
|
|
THREAD_NUM = 1; |
|
|
|
|
|
|
|
mConstance.THREAD_NUM = THREAD_NUM; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Properties pro = createConfigFile(fileLength); |
|
|
|
|
|
|
|
int blockSize = fileLength / THREAD_NUM; |
|
|
|
|
|
|
|
int[] recordL = new int[THREAD_NUM]; |
|
|
|
|
|
|
|
int rl = 0; |
|
|
|
|
|
|
|
for (int i = 0; i < THREAD_NUM; i++) { |
|
|
|
|
|
|
|
recordL[i] = -1; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
for (int i = 0; i < THREAD_NUM; i++) { |
|
|
|
|
|
|
|
long startL = i * blockSize, endL = (i + 1) * blockSize; |
|
|
|
|
|
|
|
Object state = pro.getProperty(mDownloadFile.getName() + "_state_" + i); |
|
|
|
|
|
|
|
if (state != null && Integer.parseInt(state + "") == 1) { //该线程已经完成
|
|
|
|
|
|
|
|
if (resumeRecordLocation(i, startL, endL)) return; |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
//分配下载位置
|
|
|
|
|
|
|
|
Object record = pro.getProperty(mDownloadFile.getName() + "_record_" + i); |
|
|
|
|
|
|
|
//如果有记录,则恢复下载
|
|
|
|
|
|
|
|
if (!isNewTask && record != null && Long.parseLong(record + "") > 0) { |
|
|
|
|
|
|
|
Long r = Long.parseLong(record + ""); |
|
|
|
|
|
|
|
mConstance.CURRENT_LOCATION += r - startL; |
|
|
|
|
|
|
|
Log.d(TAG, "++++++++++ 线程_" + i + "_恢复下载 ++++++++++"); |
|
|
|
|
|
|
|
mListener.onChildResume(r); |
|
|
|
|
|
|
|
startL = r; |
|
|
|
|
|
|
|
recordL[rl] = i; |
|
|
|
|
|
|
|
rl++; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
isNewTask = true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (isNewTask) { |
|
|
|
|
|
|
|
recordL[rl] = i; |
|
|
|
|
|
|
|
rl++; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (i == (THREAD_NUM - 1)) { |
|
|
|
|
|
|
|
//如果整个文件的大小不为线程个数的整数倍,则最后一个线程的结束位置即为文件的总长度
|
|
|
|
|
|
|
|
endL = fileLength; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
addSingleTask(i, startL, endL, fileLength); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
startSingleTask(recordL); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 处理不支持断点的下载 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private void handleNoSupportBreakpointDownload(HttpURLConnection conn){ |
|
|
|
ConfigEntity entity = new ConfigEntity(); |
|
|
|
ConfigEntity entity = new ConfigEntity(); |
|
|
|
long len = conn.getContentLength(); |
|
|
|
long len = conn.getContentLength(); |
|
|
|
entity.FILE_SIZE = len; |
|
|
|
entity.FILE_SIZE = len; |
|
|
@ -264,9 +319,13 @@ final class DownloadUtil implements IDownloadUtil, Runnable { |
|
|
|
mFixedThreadPool.execute(task); |
|
|
|
mFixedThreadPool.execute(task); |
|
|
|
mListener.onPostPre(len); |
|
|
|
mListener.onPostPre(len); |
|
|
|
mListener.onStart(0); |
|
|
|
mListener.onStart(0); |
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
int fileLength = conn.getContentLength(); |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 创建配置文件 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private Properties createConfigFile(long fileLength) throws IOException { |
|
|
|
|
|
|
|
Properties pro = null; |
|
|
|
//必须建一个文件
|
|
|
|
//必须建一个文件
|
|
|
|
CommonUtil.createFile(mDownloadFile.getPath()); |
|
|
|
CommonUtil.createFile(mDownloadFile.getPath()); |
|
|
|
BufferedRandomAccessFile file = |
|
|
|
BufferedRandomAccessFile file = |
|
|
@ -275,7 +334,6 @@ final class DownloadUtil implements IDownloadUtil, Runnable { |
|
|
|
file.setLength(fileLength); |
|
|
|
file.setLength(fileLength); |
|
|
|
mListener.onPostPre(fileLength); |
|
|
|
mListener.onPostPre(fileLength); |
|
|
|
//分配每条线程的下载区间
|
|
|
|
//分配每条线程的下载区间
|
|
|
|
Properties pro = null; |
|
|
|
|
|
|
|
pro = CommonUtil.loadConfig(mConfigFile); |
|
|
|
pro = CommonUtil.loadConfig(mConfigFile); |
|
|
|
if (pro.isEmpty()) { |
|
|
|
if (pro.isEmpty()) { |
|
|
|
isNewTask = true; |
|
|
|
isNewTask = true; |
|
|
@ -291,16 +349,15 @@ final class DownloadUtil implements IDownloadUtil, Runnable { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
int blockSize = fileLength / THREAD_NUM; |
|
|
|
return pro; |
|
|
|
int[] recordL = new int[THREAD_NUM]; |
|
|
|
|
|
|
|
int rl = 0; |
|
|
|
|
|
|
|
for (int i = 0; i < THREAD_NUM; i++) { |
|
|
|
|
|
|
|
recordL[i] = -1; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
for (int i = 0; i < THREAD_NUM; i++) { |
|
|
|
|
|
|
|
long startL = i * blockSize, endL = (i + 1) * blockSize; |
|
|
|
/** |
|
|
|
Object state = pro.getProperty(mDownloadFile.getName() + "_state_" + i); |
|
|
|
* 恢复记录地址 |
|
|
|
if (state != null && Integer.parseInt(state + "") == 1) { //该线程已经完成
|
|
|
|
* |
|
|
|
|
|
|
|
* @return true 表示下载完成 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private boolean resumeRecordLocation(int i, long startL, long endL) { |
|
|
|
mConstance.CURRENT_LOCATION += endL - startL; |
|
|
|
mConstance.CURRENT_LOCATION += endL - startL; |
|
|
|
Log.d(TAG, "++++++++++ 线程_" + i + "_已经下载完成 ++++++++++"); |
|
|
|
Log.d(TAG, "++++++++++ 线程_" + i + "_已经下载完成 ++++++++++"); |
|
|
|
mConstance.COMPLETE_THREAD_NUM++; |
|
|
|
mConstance.COMPLETE_THREAD_NUM++; |
|
|
@ -312,32 +369,15 @@ final class DownloadUtil implements IDownloadUtil, Runnable { |
|
|
|
} |
|
|
|
} |
|
|
|
mListener.onComplete(); |
|
|
|
mListener.onComplete(); |
|
|
|
mConstance.isDownloading = false; |
|
|
|
mConstance.isDownloading = false; |
|
|
|
return; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
continue; |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
//分配下载位置
|
|
|
|
|
|
|
|
Object record = pro.getProperty(mDownloadFile.getName() + "_record_" + i); |
|
|
|
|
|
|
|
//如果有记录,则恢复下载
|
|
|
|
|
|
|
|
if (!isNewTask && record != null && Long.parseLong(record + "") > 0) { |
|
|
|
|
|
|
|
Long r = Long.parseLong(record + ""); |
|
|
|
|
|
|
|
mConstance.CURRENT_LOCATION += r - startL; |
|
|
|
|
|
|
|
Log.d(TAG, "++++++++++ 线程_" + i + "_恢复下载 ++++++++++"); |
|
|
|
|
|
|
|
mListener.onChildResume(r); |
|
|
|
|
|
|
|
startL = r; |
|
|
|
|
|
|
|
recordL[rl] = i; |
|
|
|
|
|
|
|
rl++; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
isNewTask = true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (isNewTask) { |
|
|
|
|
|
|
|
recordL[rl] = i; |
|
|
|
|
|
|
|
rl++; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (i == (THREAD_NUM - 1)) { |
|
|
|
|
|
|
|
//如果整个文件的大小不为线程个数的整数倍,则最后一个线程的结束位置即为文件的总长度
|
|
|
|
|
|
|
|
endL = fileLength; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 创建单线程任务 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private void addSingleTask(int i, long startL, long endL, long fileLength) { |
|
|
|
ConfigEntity entity = new ConfigEntity(); |
|
|
|
ConfigEntity entity = new ConfigEntity(); |
|
|
|
entity.FILE_SIZE = fileLength; |
|
|
|
entity.FILE_SIZE = fileLength; |
|
|
|
entity.DOWNLOAD_URL = mDownloadEntity.getDownloadUrl(); |
|
|
|
entity.DOWNLOAD_URL = mDownloadEntity.getDownloadUrl(); |
|
|
@ -351,6 +391,11 @@ final class DownloadUtil implements IDownloadUtil, Runnable { |
|
|
|
SingleThreadTask task = new SingleThreadTask(mConstance, mListener, entity); |
|
|
|
SingleThreadTask task = new SingleThreadTask(mConstance, mListener, entity); |
|
|
|
mTask.put(i, task); |
|
|
|
mTask.put(i, task); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 启动单线程下载任务 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private void startSingleTask(int[] recordL) { |
|
|
|
if (mConstance.CURRENT_LOCATION > 0) { |
|
|
|
if (mConstance.CURRENT_LOCATION > 0) { |
|
|
|
mListener.onResume(mConstance.CURRENT_LOCATION); |
|
|
|
mListener.onResume(mConstance.CURRENT_LOCATION); |
|
|
|
} else { |
|
|
|
} else { |
|
|
@ -370,14 +415,14 @@ final class DownloadUtil implements IDownloadUtil, Runnable { |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
final static class ConfigEntity { |
|
|
|
final static class ConfigEntity { |
|
|
|
//文件大小
|
|
|
|
//文件大小
|
|
|
|
long FILE_SIZE; |
|
|
|
|
|
|
|
String DOWNLOAD_URL; |
|
|
|
|
|
|
|
int THREAD_ID; |
|
|
|
int THREAD_ID; |
|
|
|
|
|
|
|
long FILE_SIZE; |
|
|
|
long START_LOCATION; |
|
|
|
long START_LOCATION; |
|
|
|
long END_LOCATION; |
|
|
|
long END_LOCATION; |
|
|
|
File TEMP_FILE; |
|
|
|
File TEMP_FILE; |
|
|
|
boolean isSupportBreakpoint = true; |
|
|
|
String DOWNLOAD_URL; |
|
|
|
String CONFIG_FILE_PATH; |
|
|
|
String CONFIG_FILE_PATH; |
|
|
|
DownloadTaskEntity DOWNLOAD_TASK_ENTITY; |
|
|
|
DownloadTaskEntity DOWNLOAD_TASK_ENTITY; |
|
|
|
|
|
|
|
boolean isSupportBreakpoint = true; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |