|
|
|
@ -19,16 +19,18 @@ import android.content.Context; |
|
|
|
|
import android.util.SparseArray; |
|
|
|
|
import com.arialyy.aria.core.AriaManager; |
|
|
|
|
import com.arialyy.aria.core.download.DownloadEntity; |
|
|
|
|
import com.arialyy.aria.core.download.DownloadTaskEntity; |
|
|
|
|
import com.arialyy.aria.core.inf.AbsNormalEntity; |
|
|
|
|
import com.arialyy.aria.core.inf.AbsTaskEntity; |
|
|
|
|
import com.arialyy.aria.core.inf.IDownloadListener; |
|
|
|
|
import com.arialyy.aria.core.inf.IEventListener; |
|
|
|
|
import com.arialyy.aria.core.upload.UploadEntity; |
|
|
|
|
import com.arialyy.aria.core.upload.UploadTaskEntity; |
|
|
|
|
import com.arialyy.aria.orm.DbEntity; |
|
|
|
|
import com.arialyy.aria.util.ALog; |
|
|
|
|
import com.arialyy.aria.util.CommonUtil; |
|
|
|
|
import com.arialyy.aria.util.DbHelper; |
|
|
|
|
import java.io.File; |
|
|
|
|
import java.util.ArrayList; |
|
|
|
|
import java.util.HashSet; |
|
|
|
|
import java.util.List; |
|
|
|
|
import java.util.Properties; |
|
|
|
|
import java.util.Set; |
|
|
|
|
import java.util.Timer; |
|
|
|
@ -50,10 +52,8 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte |
|
|
|
|
protected IEventListener mListener; |
|
|
|
|
protected TASK_ENTITY mTaskEntity; |
|
|
|
|
protected ENTITY mEntity; |
|
|
|
|
protected File mConfigFile;//信息配置文件
|
|
|
|
|
protected Context mContext; |
|
|
|
|
protected File mTempFile; //文件
|
|
|
|
|
protected boolean isNewTask = true; |
|
|
|
|
protected StateConstance mConstance; |
|
|
|
|
private ExecutorService mFixedThreadPool; |
|
|
|
|
//总线程数
|
|
|
|
@ -68,7 +68,13 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte |
|
|
|
|
* 小于1m的文件不启用多线程 |
|
|
|
|
*/ |
|
|
|
|
private Timer mTimer; |
|
|
|
|
@Deprecated |
|
|
|
|
private File mConfigFile; |
|
|
|
|
/** |
|
|
|
|
* 进度刷新间隔 |
|
|
|
|
*/ |
|
|
|
|
private long mUpdateInterval = 1000; |
|
|
|
|
protected TaskRecord mRecord; |
|
|
|
|
|
|
|
|
|
protected AbsFileer(IEventListener listener, TASK_ENTITY taskEntity) { |
|
|
|
|
mListener = listener; |
|
|
|
@ -104,19 +110,20 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte |
|
|
|
|
private void startFlow() { |
|
|
|
|
mConstance.resetState(); |
|
|
|
|
checkTask(); |
|
|
|
|
mConstance.TASK_RECORD = mRecord; |
|
|
|
|
if (mListener instanceof IDownloadListener) { |
|
|
|
|
((IDownloadListener) mListener).onPostPre(mEntity.getFileSize()); |
|
|
|
|
} |
|
|
|
|
if (!mTaskEntity.isSupportBP()) { |
|
|
|
|
mTotalThreadNum = 1; |
|
|
|
|
mStartThreadNum = 1; |
|
|
|
|
mConstance.THREAD_NUM = mTotalThreadNum; |
|
|
|
|
handleNoSupportBP(); |
|
|
|
|
} else { |
|
|
|
|
mTotalThreadNum = isNewTask ? (mStartThreadNum = setNewTaskThreadNum()) : mStartThreadNum; |
|
|
|
|
mConstance.THREAD_NUM = mTotalThreadNum; |
|
|
|
|
mTotalThreadNum = |
|
|
|
|
mTaskEntity.isNewTask() ? (mStartThreadNum = setNewTaskThreadNum()) : mTotalThreadNum; |
|
|
|
|
handleBreakpoint(); |
|
|
|
|
} |
|
|
|
|
mConstance.START_THREAD_NUM = mTotalThreadNum; |
|
|
|
|
startTimer(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -193,13 +200,6 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte |
|
|
|
|
task.cancel(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (mTaskEntity instanceof DownloadTaskEntity) { |
|
|
|
|
CommonUtil.delDownloadTaskConfig(mTaskEntity.isRemoveFile(), |
|
|
|
|
(DownloadEntity) mTaskEntity.getEntity()); |
|
|
|
|
} else if (mTaskEntity instanceof UploadTaskEntity) { |
|
|
|
|
CommonUtil.delUploadTaskConfig(mTaskEntity.isRemoveFile(), |
|
|
|
|
(UploadEntity) mTaskEntity.getEntity()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override public void stop() { |
|
|
|
@ -230,53 +230,131 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 检查任务是否是新任务,新任务条件: |
|
|
|
|
* 检查任务、检查线程数 |
|
|
|
|
* 新任务条件: |
|
|
|
|
* 1、文件不存在 |
|
|
|
|
* 2、下载记录文件不存在 |
|
|
|
|
* 3、下载记录文件缺失或不匹配 |
|
|
|
|
* 4、数据库记录不存在 |
|
|
|
|
* 5、不支持断点,则是新任务 |
|
|
|
|
* 2、下载记录文件缺失或不匹配 |
|
|
|
|
* 3、数据库记录不存在 |
|
|
|
|
* 4、不支持断点,则是新任务 |
|
|
|
|
*/ |
|
|
|
|
protected abstract void checkTask(); |
|
|
|
|
protected void checkTask() { |
|
|
|
|
mConfigFile = new File(CommonUtil.getFileConfigPath(false, mEntity.getFileName())); |
|
|
|
|
if (mConfigFile.exists()) { |
|
|
|
|
convertDb(); |
|
|
|
|
} else { |
|
|
|
|
mRecord = DbHelper.getTaskRecord(mTaskEntity.getKey()); |
|
|
|
|
if (mRecord == null) { |
|
|
|
|
initRecord(); |
|
|
|
|
mTaskEntity.setNewTask(true); |
|
|
|
|
} else { |
|
|
|
|
if (mRecord.threadRecords == null || mRecord.threadRecords.isEmpty()) { |
|
|
|
|
initRecord(); |
|
|
|
|
mTaskEntity.setNewTask(true); |
|
|
|
|
} else if (mTempFile.length() == 0) { |
|
|
|
|
mRecord.deleteData(); |
|
|
|
|
initRecord(); |
|
|
|
|
mTaskEntity.setNewTask(true); |
|
|
|
|
} else { |
|
|
|
|
for (ThreadRecord tr : mRecord.threadRecords) { |
|
|
|
|
if (tr.isComplete) { |
|
|
|
|
mCompleteThreadNum++; |
|
|
|
|
} else { |
|
|
|
|
mStartThreadNum++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
mTotalThreadNum = mRecord.threadRecords.size(); |
|
|
|
|
mTaskEntity.setNewTask(false); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 检查记录文件,如果是新任务返回{@code true},否则返回{@code false} |
|
|
|
|
* convertDb 为兼容性代码 |
|
|
|
|
* 从3.4.1开始,线程配置信息将存储在数据库中。 |
|
|
|
|
* 将配置文件的内容复制到数据库中,并将配置文件删除 |
|
|
|
|
*/ |
|
|
|
|
protected boolean checkConfigFile() { |
|
|
|
|
mStartThreadNum = 0; |
|
|
|
|
mCompleteThreadNum = 0; |
|
|
|
|
private void convertDb() { |
|
|
|
|
List<RecordWrapper> records = |
|
|
|
|
DbEntity.findRelationData(RecordWrapper.class, "TaskRecord.filePath=?", |
|
|
|
|
mTaskEntity.getKey()); |
|
|
|
|
if (records == null || records.size() == 0) { |
|
|
|
|
Properties pro = CommonUtil.loadConfig(mConfigFile); |
|
|
|
|
if (pro.isEmpty()) { |
|
|
|
|
return true; |
|
|
|
|
mTaskEntity.setNewTask(true); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
initRecord(); |
|
|
|
|
Set<Object> keys = pro.keySet(); |
|
|
|
|
int num = 0; |
|
|
|
|
// 老版本记录是5s存一次,但是5s中内,如果线程执行完成,record记录是没有的,只有state记录...
|
|
|
|
|
// 第一步应该是record 和 state去重取正确的线程数
|
|
|
|
|
Set<Integer> set = new HashSet<>(); |
|
|
|
|
for (Object key : keys) { |
|
|
|
|
String str = String.valueOf(key); |
|
|
|
|
Object state = pro.getProperty(str); |
|
|
|
|
if (str.contains(RECORD)) { |
|
|
|
|
num++; |
|
|
|
|
} else if (state != null && str.contains(STATE) && Integer.parseInt(state + "") == 1) { |
|
|
|
|
int i = Integer.parseInt(str.substring(str.length() - 1, str.length())); |
|
|
|
|
if (pro.getProperty(mTempFile.getName() + RECORD + i) != null) { |
|
|
|
|
mCompleteThreadNum++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
set.add(i); |
|
|
|
|
} |
|
|
|
|
if (num == 0) { |
|
|
|
|
return true; |
|
|
|
|
int threadNum = set.size(); |
|
|
|
|
if (threadNum == 0) { |
|
|
|
|
mTaskEntity.setNewTask(true); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
mStartThreadNum = num; |
|
|
|
|
for (int i = 0; i < mStartThreadNum; i++) { |
|
|
|
|
if (pro.getProperty(mTempFile.getName() + RECORD + i) == null) { |
|
|
|
|
mRecord.threadNum = threadNum; |
|
|
|
|
mTotalThreadNum = threadNum; |
|
|
|
|
|
|
|
|
|
for (int i = 0; i < threadNum; i++) { |
|
|
|
|
ThreadRecord tRecord = new ThreadRecord(); |
|
|
|
|
tRecord.key = mRecord.filePath; |
|
|
|
|
Object state = pro.getProperty(mTempFile.getName() + STATE + i); |
|
|
|
|
Object record = pro.getProperty(mTempFile.getName() + RECORD + i); |
|
|
|
|
if (state != null && Integer.parseInt(state + "") == 1) { |
|
|
|
|
mCompleteThreadNum++; |
|
|
|
|
tRecord.isComplete = true; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
|
mStartThreadNum++; |
|
|
|
|
if (record != null) { |
|
|
|
|
Long temp = Long.parseLong(record + ""); |
|
|
|
|
tRecord.startLocation = temp > 0 ? temp : 0; |
|
|
|
|
} else { |
|
|
|
|
tRecord.startLocation = 0; |
|
|
|
|
} |
|
|
|
|
mRecord.threadRecords.add(tRecord); |
|
|
|
|
} |
|
|
|
|
mConfigFile.delete(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 初始化记录 |
|
|
|
|
*/ |
|
|
|
|
private void initRecord() { |
|
|
|
|
mRecord = new TaskRecord(); |
|
|
|
|
mRecord.fileName = mEntity.getFileName(); |
|
|
|
|
mRecord.filePath = mTaskEntity.getKey(); |
|
|
|
|
mRecord.threadRecords = new ArrayList<>(); |
|
|
|
|
mRecord.isGroupRecord = mTaskEntity.getEntity().isGroupChild(); |
|
|
|
|
mRecord.isOpenDynamicFile = |
|
|
|
|
AriaManager.getInstance(AriaManager.APP).getDownloadConfig().isOpenDynamicFile(); |
|
|
|
|
if (mRecord.isGroupRecord) { |
|
|
|
|
if (mTaskEntity.getEntity() instanceof DownloadEntity) { |
|
|
|
|
mRecord.dGroupName = ((DownloadEntity) mTaskEntity.getEntity()).getGroupName(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 保存任务记录 |
|
|
|
|
*/ |
|
|
|
|
private void saveRecord() { |
|
|
|
|
mRecord.save(); |
|
|
|
|
for (ThreadRecord tr : mRecord.threadRecords) { |
|
|
|
|
tr.save(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public TaskRecord getRecord() { |
|
|
|
|
return mRecord; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -291,9 +369,7 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte |
|
|
|
|
mConstance.STOP_NUM++; |
|
|
|
|
mConstance.CANCEL_NUM++; |
|
|
|
|
if (mConstance.isComplete()) { |
|
|
|
|
if (mConfigFile.exists()) { |
|
|
|
|
mConfigFile.delete(); |
|
|
|
|
} |
|
|
|
|
mRecord.deleteData(); |
|
|
|
|
mListener.onComplete(); |
|
|
|
|
mConstance.isRunning = false; |
|
|
|
|
return true; |
|
|
|
@ -309,7 +385,8 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte |
|
|
|
|
* @param endL 该任务结束位置 |
|
|
|
|
* @param fileLength 该任务需要处理的文件长度 |
|
|
|
|
*/ |
|
|
|
|
private AbsThreadTask createSingThreadTask(int i, long startL, long endL, long fileLength) { |
|
|
|
|
private AbsThreadTask createSingThreadTask(int i, long startL, long endL, long fileLength, |
|
|
|
|
ThreadRecord record) { |
|
|
|
|
SubThreadConfig<TASK_ENTITY> config = new SubThreadConfig<>(); |
|
|
|
|
config.FILE_SIZE = fileLength; |
|
|
|
|
config.URL = mEntity.isRedirect() ? mEntity.getRedirectUrl() : mEntity.getUrl(); |
|
|
|
@ -317,66 +394,76 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte |
|
|
|
|
config.THREAD_ID = i; |
|
|
|
|
config.START_LOCATION = startL; |
|
|
|
|
config.END_LOCATION = endL; |
|
|
|
|
config.CONFIG_FILE_PATH = mConfigFile.getPath(); |
|
|
|
|
config.SUPPORT_BP = mTaskEntity.isSupportBP(); |
|
|
|
|
config.TASK_ENTITY = mTaskEntity; |
|
|
|
|
config.THREAD_RECORD = record; |
|
|
|
|
return selectThreadTask(config); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 处理断点 |
|
|
|
|
*/ |
|
|
|
|
private void handleBreakpoint() { |
|
|
|
|
long fileLength = mEntity.getFileSize(); |
|
|
|
|
Properties pro = CommonUtil.loadConfig(mConfigFile); |
|
|
|
|
long blockSize = fileLength / mTotalThreadNum; |
|
|
|
|
int[] recordL = new int[mTotalThreadNum]; |
|
|
|
|
int[] threadId = new int[mTotalThreadNum]; |
|
|
|
|
int rl = 0; |
|
|
|
|
|
|
|
|
|
mRecord.fileLength = fileLength; |
|
|
|
|
for (int i = 0; i < mTotalThreadNum; i++) { |
|
|
|
|
recordL[i] = -1; |
|
|
|
|
threadId[i] = -1; |
|
|
|
|
} |
|
|
|
|
int rl = 0; |
|
|
|
|
if (isNewTask && !handleNewTask()) { |
|
|
|
|
if (mTaskEntity.isNewTask() && !handleNewTask()) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
for (int i = 0; i < mTotalThreadNum; i++) { |
|
|
|
|
long startL = i * blockSize, endL = (i + 1) * blockSize; |
|
|
|
|
Object state = pro.getProperty(mTempFile.getName() + STATE + i); |
|
|
|
|
Object record = pro.getProperty(mTempFile.getName() + RECORD + i); |
|
|
|
|
if (state != null && Integer.parseInt(state + "") == 1 && record != null) { //该线程已经完成
|
|
|
|
|
ThreadRecord tr; |
|
|
|
|
boolean isNewTr = false; // 是否是新的线程记录
|
|
|
|
|
if (mTaskEntity.isNewTask()) { |
|
|
|
|
tr = new ThreadRecord(); |
|
|
|
|
tr.key = mRecord.filePath; |
|
|
|
|
tr.threadId = i; |
|
|
|
|
isNewTr = true; |
|
|
|
|
} else { |
|
|
|
|
tr = mRecord.threadRecords.get(i); |
|
|
|
|
} |
|
|
|
|
if (tr.isComplete) {//该线程已经完成
|
|
|
|
|
if (resumeRecordLocation(i, startL, endL)) return; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//如果有记录,则恢复下载
|
|
|
|
|
if (!isNewTask && record != null && Long.parseLong(record + "") >= 0) { |
|
|
|
|
Long r = Long.parseLong(record + ""); |
|
|
|
|
if (tr.startLocation >= 0) { |
|
|
|
|
Long r = tr.startLocation; |
|
|
|
|
//记录的位置需要在线程区间中
|
|
|
|
|
if (startL < r && r < (i == (mTotalThreadNum - 1) ? fileLength : endL)) { |
|
|
|
|
mConstance.CURRENT_LOCATION += r - startL; |
|
|
|
|
startL = r; |
|
|
|
|
} |
|
|
|
|
ALog.d(TAG, "任务【" + mEntity.getFileName() + "】线程__" + i + "__恢复下载"); |
|
|
|
|
recordL[rl] = i; |
|
|
|
|
rl++; |
|
|
|
|
} else { |
|
|
|
|
recordL[rl] = i; |
|
|
|
|
rl++; |
|
|
|
|
} |
|
|
|
|
if (i == (mTotalThreadNum - 1)) { |
|
|
|
|
//最后一个线程的结束位置即为文件的总长度
|
|
|
|
|
if (i == (mTotalThreadNum - 1)) { |
|
|
|
|
endL = fileLength; |
|
|
|
|
} |
|
|
|
|
AbsThreadTask task = createSingThreadTask(i, startL, endL, fileLength); |
|
|
|
|
// 更新记录
|
|
|
|
|
tr.startLocation = startL; |
|
|
|
|
tr.endLocation = endL; |
|
|
|
|
if (isNewTr) { |
|
|
|
|
mRecord.threadRecords.add(tr); |
|
|
|
|
} |
|
|
|
|
AbsThreadTask task = createSingThreadTask(i, startL, endL, fileLength, tr); |
|
|
|
|
if (task == null) return; |
|
|
|
|
mTask.put(i, task); |
|
|
|
|
threadId[rl] = i; |
|
|
|
|
rl++; |
|
|
|
|
} |
|
|
|
|
startSingleTask(recordL); |
|
|
|
|
saveRecord(); |
|
|
|
|
startThreadTask(threadId); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 启动单线程下载任务 |
|
|
|
|
*/ |
|
|
|
|
private void startSingleTask(int[] recordL) { |
|
|
|
|
private void startThreadTask(int[] recordL) { |
|
|
|
|
if (mConstance.CURRENT_LOCATION > 0) { |
|
|
|
|
mListener.onResume(mConstance.CURRENT_LOCATION); |
|
|
|
|
} else { |
|
|
|
@ -390,7 +477,6 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte |
|
|
|
|
mFixedThreadPool.execute(task); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
mTaskEntity.setNewTask(false); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -411,7 +497,6 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte |
|
|
|
|
config.THREAD_ID = 0; |
|
|
|
|
config.START_LOCATION = 0; |
|
|
|
|
config.END_LOCATION = config.FILE_SIZE; |
|
|
|
|
config.CONFIG_FILE_PATH = mConfigFile.getPath(); |
|
|
|
|
config.SUPPORT_BP = mTaskEntity.isSupportBP(); |
|
|
|
|
config.TASK_ENTITY = mTaskEntity; |
|
|
|
|
AbsThreadTask task = selectThreadTask(config); |
|
|
|
|