修复分块可能出现的问题

v3.6.6
laoyuyu 6 years ago
parent e6da5230e9
commit 9212aabd95
  1. 15
      Aria/src/main/java/com/arialyy/aria/core/AriaManager.java
  2. 82
      Aria/src/main/java/com/arialyy/aria/core/common/AbsFileer.java
  3. 70
      Aria/src/main/java/com/arialyy/aria/core/common/AbsThreadTask.java
  4. 3
      Aria/src/main/java/com/arialyy/aria/core/common/ThreadRecord.java
  5. 29
      Aria/src/main/java/com/arialyy/aria/core/common/http/HttpHeaderDelegate.java
  6. 1
      Aria/src/main/java/com/arialyy/aria/core/common/http/HttpTaskDelegate.java
  7. 2
      Aria/src/main/java/com/arialyy/aria/core/download/AbsGroupDelegate.java
  8. 2
      Aria/src/main/java/com/arialyy/aria/core/download/BaseDListener.java
  9. 3
      Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTarget.java
  10. 2
      Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java
  11. 4
      Aria/src/main/java/com/arialyy/aria/core/download/DownloadTarget.java
  12. 43
      Aria/src/main/java/com/arialyy/aria/core/download/HttpGroupDelegate.java
  13. 9
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/Downloader.java
  14. 3
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpThreadTask.java
  15. 6
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpFileInfoThread.java
  16. 15
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpThreadTask.java
  17. 1
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/SimpleDownloadUtil.java
  18. 48
      Aria/src/main/java/com/arialyy/aria/core/download/group/AbsGroupUtil.java
  19. 34
      Aria/src/main/java/com/arialyy/aria/core/download/group/DownloadGroupUtil.java
  20. 2
      Aria/src/main/java/com/arialyy/aria/core/download/group/FtpDirDownloadUtil.java
  21. 8
      Aria/src/main/java/com/arialyy/aria/core/download/group/ISubQueue.java
  22. 12
      Aria/src/main/java/com/arialyy/aria/core/download/group/SimpleSubQueue.java
  23. 4
      Aria/src/main/java/com/arialyy/aria/core/download/group/SubDownloadLoader.java
  24. 10
      Aria/src/main/java/com/arialyy/aria/core/inf/AbsHttpFileLenAdapter.java
  25. 4
      Aria/src/main/java/com/arialyy/aria/core/manager/ThreadTaskManager.java
  26. 2
      Aria/src/main/java/com/arialyy/aria/core/upload/BaseUListener.java
  27. 3
      Aria/src/main/java/com/arialyy/aria/core/upload/uploader/FtpThreadTask.java
  28. 2
      Aria/src/main/java/com/arialyy/aria/core/upload/uploader/HttpThreadTask.java
  29. 2
      Aria/src/main/java/com/arialyy/aria/orm/DBConfig.java
  30. 579
      Aria/src/main/java/com/arialyy/aria/orm/DbEntity.java
  31. 2
      Aria/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java
  32. 4
      Aria/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java
  33. 2
      Aria/src/main/java/com/arialyy/aria/orm/SqlHelper.java
  34. 84
      Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java
  35. 7
      Aria/src/main/java/com/arialyy/aria/util/DbDataHelper.java
  36. 10
      DEV_LOG.md
  37. 27
      app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java

@ -276,19 +276,20 @@ import org.xml.sax.SAXException;
/**
* 删除任务记录
*
* @param type 需要删除的任务类型1表示单任务下载2表示任务组下载3单任务上传
* @param key 下载为保存路径任务组为任务组名上传为上传文件路径
* @param type 需要删除的任务类型1普通下载任务2组合任务3普通上传任务
* @param key type为1时key为保存路径type为2时key为组合任务hashtype为3时key为文件上传路径
* @param removeFile {@code true} 不仅删除任务数据库记录还会删除已经删除完成的文件{@code false}如果任务已经完成只删除任务数据库记录
*/
public void delRecord(int type, String key) {
public void delRecord(int type, String key, boolean removeFile) {
switch (type) {
case 1:
DbEntity.deleteData(DownloadEntity.class, "url=? and isGroupChild='false'", key);
case 1: // 删除普通任务记录
CommonUtil.delTaskRecord(key, 1, removeFile, true);
break;
case 2:
DbEntity.deleteData(DownloadGroupEntity.class, "groupHash=?", key);
CommonUtil.delGroupTaskRecord(key, removeFile);
break;
case 3:
DbEntity.deleteData(UploadEntity.class, "filePath=?", key);
CommonUtil.delTaskRecord(key, 2);
break;
}
}

@ -212,6 +212,9 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER ext
|| mConstance.isCancel()
|| mConstance.isFail()
|| !isRunning()) {
if (mConstance.isComplete() || mConstance.isFail()) {
ThreadTaskManager.getInstance().removeTaskThread(mTaskWrapper.getKey());
}
closeTimer();
} else if (mConstance.CURRENT_LOCATION >= 0) {
mListener.onProgress(mConstance.CURRENT_LOCATION);
@ -251,9 +254,7 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER ext
}
public synchronized boolean isRunning() {
boolean b = ThreadTaskManager.getInstance().taskIsRunning(mTaskWrapper.getKey());
//ALog.d(TAG, "isRunning = " + b);
return b;
return ThreadTaskManager.getInstance().taskIsRunning(mTaskWrapper.getKey());
}
public synchronized void cancel() {
@ -271,7 +272,7 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER ext
task.cancel();
}
}
ThreadTaskManager.getInstance().stopTaskThread(mTaskWrapper.getKey());
ThreadTaskManager.getInstance().removeTaskThread(mTaskWrapper.getKey());
}
}).start();
}
@ -292,7 +293,7 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER ext
task.stop();
}
}
ThreadTaskManager.getInstance().stopTaskThread(mTaskWrapper.getKey());
ThreadTaskManager.getInstance().removeTaskThread(mTaskWrapper.getKey());
}
}).start();
}
@ -382,13 +383,14 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER ext
}
/**
* 处理分块任务的记录
* 处理分块任务的记录分块文件blockFileLen长度必须需要小于等于线程区间threadRectLen的长度
*/
private void handleBlockRecord() {
final int threadNum = mRecord.threadRecords.size();
final long blockLen = mEntity.getFileSize() / threadNum;
int i = 0;
// 默认线程分块长度
long normalRectLen = mEntity.getFileSize() / mRecord.threadRecords.size();
for (ThreadRecord tr : mRecord.threadRecords) {
long threadRect = tr.blockLen;
File temp = new File(String.format(SUB_PATH, mRecord.filePath, tr.threadId));
if (!temp.exists()) {
ALog.i(TAG, String.format("分块文件【%s】不存在,该分块将重新开始", temp.getPath()));
@ -398,33 +400,38 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER ext
if (tr.isComplete) {
mCompleteThreadNum++;
} else {
long realLocation = i * blockLen + temp.length();
ALog.i(TAG, String.format(
"startLocation = %s; endLocation = %s; block = %s; tempLen = %s; i = %s",
tr.startLocation, tr.endLocation, blockLen, temp.length(), i));
if (tr.endLocation == realLocation) {
"startLocation = %s; endLocation = %s; block = %s; tempLen = %s; threadId = %s",
tr.startLocation, tr.endLocation, threadRect, temp.length(), tr.threadId));
long blockFileLen = temp.length(); // 磁盘中的分块文件长度
/*
* 检查磁盘中的分块文件
*/
if (blockFileLen > threadRect) {
ALog.i(TAG, String.format("分块【%s】错误,分块长度【%s】 > 线程区间长度【%s】,将重新开始该分块",
tr.threadId, blockFileLen, threadRect));
temp.delete();
tr.startLocation = tr.threadId * threadRect;
continue;
}
long realLocation =
tr.threadId * normalRectLen + blockFileLen; //正常情况下,该线程的startLocation的位置
/*
* 检查记录文件
*/
if (blockFileLen == threadRect) {
ALog.i(TAG, String.format("分块【%s】已完成,更新记录", temp.getPath()));
tr.startLocation = realLocation;
tr.startLocation = blockFileLen;
tr.isComplete = true;
mCompleteThreadNum++;
} else {
tr.isComplete = false;
if (realLocation == tr.startLocation) {
i++;
continue;
}
if (realLocation > tr.startLocation) {
ALog.i(TAG, String.format("修正分块【%s】的进度记录为:%s", temp.getPath(), realLocation));
tr.startLocation = realLocation;
} else {
ALog.i(TAG, String.format("分块【%s】错误,将重新开始该分块", temp.getPath()));
temp.delete();
tr.startLocation = i * blockLen;
}
} else if (tr.startLocation != realLocation) { // 处理记录小于分块文件长度的情况
ALog.i(TAG, String.format("修正分块【%s】的进度记录为:%s", temp.getPath(), realLocation));
tr.startLocation = realLocation;
}
}
}
i++;
}
mTotalThreadNum = mRecord.threadRecords.size();
mTaskWrapper.setNewTask(false);
@ -517,13 +524,8 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER ext
private void saveRecord() {
mRecord.threadNum = mRecord.threadRecords.size();
mRecord.save();
for (ThreadRecord tr : mRecord.threadRecords) {
tr.save();
}
}
public TaskRecord getRecord() {
return mRecord;
ALog.d(TAG, String.format("保存记录,线程记录数:%s", mRecord.threadRecords.size()));
DbEntity.saveAll(mRecord.threadRecords);
}
/**
@ -573,10 +575,10 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER ext
long fileLength = mEntity.getFileSize();
long blockSize = fileLength / mTotalThreadNum;
Set<Integer> threads = new HashSet<>();
// 如果是新任务,检查下历史记录,如果有遗留的记录,删除并删除分块文件
if (mTaskWrapper.isNewTask()) {
CommonUtil.delTaskRecord(getFilePath(), 1, true);
}
//// 如果是新任务,检查下历史记录,如果有遗留的记录,删除并删除分块文件
//if (mTaskWrapper.isNewTask()) {
// CommonUtil.delTaskRecord(getFilePath(), 1, true);
//}
mRecord.fileLength = fileLength;
if (mTaskWrapper.isNewTask() && !handleNewTask()) {

@ -22,6 +22,7 @@ import com.arialyy.aria.core.config.BaseTaskConfig;
import com.arialyy.aria.core.config.DGroupConfig;
import com.arialyy.aria.core.config.DownloadConfig;
import com.arialyy.aria.core.config.UploadConfig;
import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.inf.AbsNormalEntity;
import com.arialyy.aria.core.inf.AbsTaskWrapper;
import com.arialyy.aria.core.inf.IEventListener;
@ -31,6 +32,7 @@ import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.exception.FileException;
import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.ErrorHelp;
import com.arialyy.aria.util.FileUtil;
import com.arialyy.aria.util.NetUtils;
@ -272,8 +274,10 @@ public abstract class AbsThreadTask<ENTITY extends AbsNormalEntity, TASK_WRAPPER
ThreadRecord tr = getThreadRecord();
File blockFile = getBockFile();
if (!blockFile.exists() || blockFile.length() != tr.blockLen) {
ALog.i(TAG, String.format("分块【%s】下载错误,即将重新下载该分块,开始位置:%s,结束位置:%s", blockFile.getName(),
tr.startLocation, tr.endLocation));
ALog.i(TAG,
String.format("分块【%s】错误,blockFileLen: %s, threadRect: %s; 即将重新下载该分块,开始位置:%s,结束位置:%s",
blockFile.getName(), blockFile.length(), tr.blockLen, tr.startLocation,
tr.endLocation));
if (blockFile.exists()) {
blockFile.delete();
ALog.i(TAG, String.format("删除分块【%s】成功", blockFile.getName()));
@ -405,14 +409,11 @@ public abstract class AbsThreadTask<ENTITY extends AbsNormalEntity, TASK_WRAPPER
*/
private void retryThis(boolean needRetry) {
if (!NetUtils.isConnected(AriaManager.APP) && !isNotNetRetry) {
ALog.w(TAG, String.format("任务【%s】thread__%s__重试失败,网络未连接", getConfig().TEMP_FILE.getName(),
getConfig().THREAD_ID));
ALog.w(TAG, String.format("任务【%s】重试失败,网络未连接", getConfig().TEMP_FILE.getName()));
}
if (mFailTimes < RETRY_NUM && needRetry && (NetUtils.isConnected(AriaManager.APP)
|| isNotNetRetry) && !isBreak()) {
ALog.w(TAG,
String.format("任务【%s】thread__%s__正在重试", getConfig().TEMP_FILE.getName(),
getConfig().THREAD_ID));
ALog.w(TAG, String.format("任务【%s】正在重试", getConfig().TEMP_FILE.getName()));
mFailTimes++;
handleRetryRecord();
ThreadTaskManager.getInstance().retryThread(AbsThreadTask.this);
@ -427,25 +428,40 @@ public abstract class AbsThreadTask<ENTITY extends AbsNormalEntity, TASK_WRAPPER
private void handleRetryRecord() {
if (getTaskRecord().isBlock) {
ThreadRecord tr = getThreadRecord();
long block = getEntity().getFileSize() / getTaskRecord().threadRecords.size();
// 默认线程分块长度
long normalRectLen = getEntity().getFileSize() / getTaskRecord().threadRecords.size();
File temp = getBockFile();
File blockFile = getBockFile();
if (blockFile.length() > tr.blockLen) {
ALog.i(TAG, String.format("分块【%s】错误,将重新下载该分块", blockFile.getPath()));
blockFile.delete();
tr.startLocation = block * tr.threadId;
tr.isComplete = false;
getConfig().START_LOCATION = tr.startLocation;
} else if (blockFile.length() < tr.blockLen) {
tr.startLocation = block * tr.threadId + blockFile.length();
long blockFileLen = temp.length(); // 磁盘中的分块文件长度
long threadRect = tr.blockLen; // 当前线程的区间
if (!temp.exists()) {
ALog.i(TAG, String.format("分块文件【%s】不存在,该分块将重新开始", temp.getName()));
tr.isComplete = false;
getConfig().START_LOCATION = tr.startLocation;
getState().CURRENT_LOCATION = getBlockRealTotalSize();
ALog.i(TAG, String.format("修正分块【%s】,开始位置:%s,当前进度:%s", blockFile.getPath(), tr.startLocation,
getState().CURRENT_LOCATION));
} else {
getState().COMPLETE_THREAD_NUM++;
tr.isComplete = true;
/*
* 检查磁盘中的分块文件
*/
if (blockFileLen > threadRect) {
ALog.i(TAG, String.format("分块【%s】错误,将重新下载该分块", temp.getName()));
temp.delete();
tr.startLocation = normalRectLen * tr.threadId;
tr.isComplete = false;
getConfig().START_LOCATION = tr.startLocation;
} else if (blockFileLen < tr.blockLen) {
tr.startLocation = normalRectLen * tr.threadId + blockFileLen;
tr.isComplete = false;
getConfig().START_LOCATION = tr.startLocation;
getState().CURRENT_LOCATION = getBlockRealTotalSize();
ALog.i(TAG,
String.format("修正分块【%s】,开始位置:%s,当前进度:%s", temp.getName(), tr.startLocation,
getState().CURRENT_LOCATION));
} else {
ALog.i(TAG, String.format("分块【%s】已完成,更新记录", temp.getName()));
getState().COMPLETE_THREAD_NUM++;
tr.isComplete = true;
}
}
tr.update();
} else {
@ -454,6 +470,16 @@ public abstract class AbsThreadTask<ENTITY extends AbsNormalEntity, TASK_WRAPPER
}
}
/**
* 发送任务完成的消息并删除任务记录
*/
protected synchronized void sendCompleteMsg() {
ALog.i(TAG, String.format("任务【%s】完成", mEntity.getFileName()));
CommonUtil.delTaskRecord(mEntity.getKey(), mEntity instanceof DownloadEntity ? 1 : 2, false,
false);
mListener.onComplete();
}
/**
* 获取分块文件
*

@ -24,6 +24,7 @@ import com.arialyy.aria.orm.annotation.Foreign;
* 任务的线程记录
*/
public class ThreadRecord extends DbEntity {
@Foreign(parent = TaskRecord.class, column = "filePath", onUpdate = ActionPolicy.CASCADE, onDelete = ActionPolicy.CASCADE)
public String key;
@ -46,7 +47,7 @@ public class ThreadRecord extends DbEntity {
/**
* 线程id
*/
public int threadId = -1;
public int threadId = 0;
/**
* 分块长度

@ -17,20 +17,11 @@ package com.arialyy.aria.core.common.http;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.core.download.DGTaskWrapper;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.inf.AbsHttpFileLenAdapter;
import com.arialyy.aria.core.inf.AbsTarget;
import com.arialyy.aria.core.inf.AbsTaskWrapper;
import com.arialyy.aria.core.inf.IHttpFileLenAdapter;
import com.arialyy.aria.core.inf.IHttpHeaderDelegate;
import com.arialyy.aria.core.upload.UTaskWrapper;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import java.io.File;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.net.Proxy;
import java.util.Collection;
import java.util.Map;
@ -90,27 +81,11 @@ public class HttpHeaderDelegate<TARGET extends AbsTarget>
return mTarget;
}
public TARGET setFileLenAdapter(AbsHttpFileLenAdapter adapter) {
public TARGET setFileLenAdapter(IHttpFileLenAdapter adapter) {
if (adapter == null) {
throw new IllegalArgumentException("adapter为空");
}
//try {
// adapter.clone();
mTarget.getTaskWrapper().asHttp().setFileLenAdapter((IHttpFileLenAdapter) adapter);
//} catch (CloneNotSupportedException e) {
// e.printStackTrace();
//}
//// 以下代码有问题,匿名内部类不能序列化
//String objPath = String.format("%s/obj_temp/%s", AriaManager.APP.getFilesDir().getPath(),
// adapter.hashCode());
//CommonUtil.writeObjToFile(objPath, adapter);
//IHttpFileLenAdapter newAdapter = (IHttpFileLenAdapter) CommonUtil.readObjFromFile(objPath);
//File temp = new File(objPath);
//if (temp.exists()) {
// temp.delete();
//}
//mTarget.getTaskWrapper().asHttp().setFileLenAdapter(newAdapter);
mTarget.getTaskWrapper().asHttp().setFileLenAdapter(adapter);
return mTarget;
}

@ -19,6 +19,7 @@ package com.arialyy.aria.core.common.http;
import com.arialyy.aria.core.common.RequestEnum;
import com.arialyy.aria.core.inf.IHttpFileLenAdapter;
import com.arialyy.aria.core.inf.ITargetHeadDelegate;
import java.lang.ref.WeakReference;
import java.net.CookieManager;
import java.net.Proxy;
import java.util.HashMap;

@ -76,6 +76,7 @@ abstract class AbsGroupDelegate<TARGET extends AbsDGTarget> implements IGroupTar
* @param newDirPath 新的文件夹路径
*/
void reChangeDirPath(String newDirPath) {
ALog.d(TAG, String.format("修改新路径为:%s", newDirPath));
List<DTaskWrapper> subTasks = mWrapper.getSubTaskWrapper();
if (subTasks != null && !subTasks.isEmpty()) {
List<DownloadEntity> des = new ArrayList<>();
@ -90,7 +91,6 @@ abstract class AbsGroupDelegate<TARGET extends AbsDGTarget> implements IGroupTar
de.setDownloadPath(newPath);
des.add(de);
}
AbsEntity.saveAll(des);
}
}

@ -55,7 +55,7 @@ public class BaseDListener extends BaseListener<DownloadEntity, DTaskWrapper, Do
mEntity.setState(state);
if (state == IEntity.STATE_CANCEL) {
CommonUtil.delTaskRecord(mEntity.getDownloadPath(), 1, mTaskWrapper.isRemoveFile());
CommonUtil.delTaskRecord(mEntity.getDownloadPath(), 1, mTaskWrapper.isRemoveFile(), true);
return;
} else if (state == IEntity.STATE_STOP) {
mEntity.setStopTime(System.currentTimeMillis());

@ -19,7 +19,6 @@ import android.support.annotation.CheckResult;
import android.support.annotation.NonNull;
import com.arialyy.aria.core.common.http.HttpHeaderDelegate;
import com.arialyy.aria.core.common.http.PostDelegate;
import com.arialyy.aria.core.inf.AbsHttpFileLenAdapter;
import com.arialyy.aria.core.inf.IHttpFileLenAdapter;
import com.arialyy.aria.core.inf.IHttpHeaderDelegate;
import com.arialyy.aria.core.manager.TaskWrapperManager;
@ -162,7 +161,7 @@ public class DownloadGroupTarget extends AbsDGTarget<DownloadGroupTarget> implem
/**
* 如果你需要使用header中特定的key来设置文件长度或有定制文件长度的需要那么你可以通过该方法自行处理文件长度
*/
public DownloadGroupTarget setFileLenAdapter(AbsHttpFileLenAdapter adapter) {
public DownloadGroupTarget setFileLenAdapter(IHttpFileLenAdapter adapter) {
return mHeaderDelegate.setFileLenAdapter(adapter);
}

@ -187,7 +187,7 @@ public class DownloadReceiver extends AbsReceiver {
* 取消注册如果是Activity或fragmentAria会界面销毁时自动调用该方法不需要你手动调用
* 注意事项
* 1如果在activity中一定要调用该方法那么请在{@code onDestroy()}中调用
* 2不要在activity的{@code onStop()}中调用改方法
* 2不要在activity的{@code onPreStop()}中调用改方法
* 3如果是Dialog或popupwindow需要你在撤销界面时调用该方法
* 4如果你是在Module非android组件类中注册了Aria那么你也需要在Module类中调用该方法而不是在组件类中
* 调用销毁详情见

@ -20,7 +20,6 @@ import android.support.annotation.NonNull;
import com.arialyy.aria.core.common.http.GetDelegate;
import com.arialyy.aria.core.common.http.HttpHeaderDelegate;
import com.arialyy.aria.core.common.http.PostDelegate;
import com.arialyy.aria.core.inf.AbsHttpFileLenAdapter;
import com.arialyy.aria.core.inf.IHttpFileLenAdapter;
import com.arialyy.aria.core.inf.IHttpHeaderDelegate;
import java.net.Proxy;
@ -103,9 +102,8 @@ public class DownloadTarget extends AbsDTarget<DownloadTarget>
/**
* 如果你需要使用header中特定的key来设置文件长度或有定制文件长度的需要那么你可以通过该方法自行处理文件长度
*/
public DownloadTarget setFileLenAdapter(AbsHttpFileLenAdapter adapter) {
public DownloadTarget setFileLenAdapter(IHttpFileLenAdapter adapter) {
return mHeaderDelegate.setFileLenAdapter(adapter);
//return this;
}
/**

@ -125,8 +125,6 @@ class HttpGroupDelegate extends AbsGroupDelegate<DownloadGroupTarget> {
}
}
saveEntity();
if (isNeedModifyPath()) {
reChangeDirPath(getDirPathTemp());
}
@ -134,10 +132,11 @@ class HttpGroupDelegate extends AbsGroupDelegate<DownloadGroupTarget> {
if (!mSubNameTemp.isEmpty()) {
updateSingleSubFileName();
}
saveEntity();
return true;
}
private void saveEntity(){
private void saveEntity() {
getEntity().save();
DbEntity.saveAll(getEntity().getSubEntities());
}
@ -148,10 +147,23 @@ class HttpGroupDelegate extends AbsGroupDelegate<DownloadGroupTarget> {
private void updateSingleSubFileName() {
List<DTaskWrapper> entities = getTaskWrapper().getSubTaskWrapper();
int i = 0;
for (DTaskWrapper entity : entities) {
for (DTaskWrapper taskWrapper : entities) {
if (i < mSubNameTemp.size()) {
String newName = mSubNameTemp.get(i);
updateSingleSubFileName(entity, newName);
DownloadEntity entity = taskWrapper.getEntity();
if (!newName.equals(entity.getFileName())) {
String oldPath = getEntity().getDirPath() + "/" + entity.getFileName();
String newPath = getEntity().getDirPath() + "/" + newName;
if (DbEntity.checkDataExist(DownloadEntity.class, "downloadPath=? or isComplete='true'",
newPath)) {
ALog.w(TAG, String.format("更新文件名失败,路径【%s】已存在或文件已下载", newPath));
return;
}
CommonUtil.modifyTaskRecord(oldPath, newPath);
entity.setDownloadPath(newPath);
entity.setFileName(newName);
}
}
i++;
}
@ -203,27 +215,6 @@ class HttpGroupDelegate extends AbsGroupDelegate<DownloadGroupTarget> {
return true;
}
/**
* 更新单个子任务文件名
*/
private void updateSingleSubFileName(DTaskWrapper taskEntity, String newName) {
DownloadEntity entity = taskEntity.getEntity();
if (!newName.equals(entity.getFileName())) {
String oldPath = getEntity().getDirPath() + "/" + entity.getFileName();
String newPath = getEntity().getDirPath() + "/" + newName;
if (DbEntity.checkDataExist(DownloadEntity.class, "downloadPath=? or isComplete='true'",
newPath)) {
ALog.w(TAG, String.format("更新文件名失败,路径【%s】已存在或文件已下载", newPath));
return;
}
CommonUtil.modifyTaskRecord(oldPath, newPath);
entity.setDownloadPath(newPath);
entity.setFileName(newName);
entity.update();
}
}
/**
* 如果用户设置了子任务文件名检查子任务文件名
*

@ -26,6 +26,7 @@ import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.BufferedRandomAccessFile;
import com.arialyy.aria.util.CommonUtil;
import java.io.File;
import java.io.IOException;
@ -59,6 +60,14 @@ public class Downloader extends AbsFileer<DownloadEntity, DTaskWrapper> {
mTempFile.delete();
}
//CommonUtil.createFile(mTempFile.getPath());
} else {
for (int i = 0; i < mTotalThreadNum; i++) {
File blockFile = new File(String.format(AbsFileer.SUB_PATH, mTempFile.getPath(), i));
if (blockFile.exists()) {
ALog.d(TAG, String.format("分块【%s】已经存在,将删除该分块", i));
blockFile.delete();
}
}
}
BufferedRandomAccessFile file = null;
try {

@ -147,8 +147,7 @@ class FtpThreadTask extends AbsFtpThreadTask<DownloadEntity, DTaskWrapper> {
return;
}
}
getState().TASK_RECORD.deleteData();
mListener.onComplete();
sendCompleteMsg();
}
if (getState().isFail()) {
mListener.onFail(false,

@ -89,6 +89,8 @@ public class HttpFileInfoThread implements Runnable {
if (conn != null) {
conn.disconnect();
}
// 销毁文件长度适配器
mTaskWrapper.asHttp().setFileLenAdapter(null);
}
}
@ -113,6 +115,8 @@ public class HttpFileInfoThread implements Runnable {
IHttpFileLenAdapter lenAdapter = mTaskWrapper.asHttp().getFileLenAdapter();
if (lenAdapter == null) {
lenAdapter = new FileLenAdapter();
} else {
ALog.d(TAG, "使用自定义adapter");
}
long len = lenAdapter.handleFileLen(conn.getHeaderFields());
@ -214,8 +218,6 @@ public class HttpFileInfoThread implements Runnable {
onFileInfoCallback.onComplete(mEntity.getUrl(), info);
}
mEntity.update();
// 销毁文件长度适配器
mTaskWrapper.asHttp().setFileLenAdapter(null);
}
}

@ -176,7 +176,8 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DTaskWrapper> {
handleComplete();
} catch (IOException e) {
fail(mChildCurrentLocation, new AriaIOException(TAG,
String.format("文件下载失败,savePath: %s, url: %s", getEntity().getDownloadPath(), getConfig().URL),
String.format("文件下载失败,savePath: %s, url: %s", getEntity().getDownloadPath(),
getConfig().URL),
e));
} finally {
if (fos != null) {
@ -228,7 +229,8 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DTaskWrapper> {
handleComplete();
} catch (IOException e) {
fail(mChildCurrentLocation, new AriaIOException(TAG,
String.format("文件下载失败,savePath: %s, url: %s", getEntity().getDownloadPath(), getConfig().URL),
String.format("文件下载失败,savePath: %s, url: %s", getEntity().getDownloadPath(),
getConfig().URL),
e));
} finally {
try {
@ -278,8 +280,7 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DTaskWrapper> {
return;
}
if (getTaskWrapper().asHttp().isChunked()) {
ALog.i(TAG, "任务下载完成");
mListener.onComplete();
sendCompleteMsg();
return;
}
@ -300,8 +301,7 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DTaskWrapper> {
return;
}
}
getState().TASK_RECORD.deleteData();
mListener.onComplete();
sendCompleteMsg();
}
if (getState().isFail()) {
mListener.onFail(false,
@ -310,8 +310,7 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DTaskWrapper> {
getEntity().getDownloadPath(), getEntity().getUrl())));
}
} else {
ALog.i(TAG, "任务下载完成");
mListener.onComplete();
sendCompleteMsg();
}
} else {
getState().FAIL_NUM++;

@ -23,7 +23,6 @@ import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.inf.AbsEntity;
import com.arialyy.aria.core.inf.AbsTaskWrapper;
import com.arialyy.aria.core.inf.IDownloadListener;
import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.exception.BaseException;
/**

@ -152,7 +152,7 @@ public abstract class AbsGroupUtil implements IUtil, Runnable {
private SubDownloadLoader getDownloader(String url) {
SubDownloadLoader d = mExeLoader.get(url);
if (d == null) {
return createSubLoader(mCache.get(url));
return createAndStartSubLoader(mCache.get(url));
}
return d;
}
@ -172,38 +172,34 @@ public abstract class AbsGroupUtil implements IUtil, Runnable {
@Override public void cancel() {
isCancel = true;
closeTimer();
onCancel();
Set<String> keys = mExeLoader.keySet();
mSubQueue.clear();
onPreCancel();
for (String key : keys) {
SubDownloadLoader loader = mExeLoader.get(key);
if (loader != null && loader.isRunning()) {
loader.cancel();
}
}
mSubQueue.removeAllTask();
mListener.onCancel();
}
public void onCancel() {
/**
* onCancel前的操作
*/
public void onPreCancel() {
}
@Override public void stop() {
isStop = true;
closeTimer();
if (onStop()) {
if (onPreStop()) {
return;
}
mSubQueue.stopAllTask();
}
/**
* 子类的钩子
* onStop前的操作
*
* @return 返回{@code true}直接回调{@link IDownloadGroupListener#onStop(long)}
*/
protected boolean onStop() {
protected boolean onPreStop() {
return false;
}
@ -286,8 +282,8 @@ public abstract class AbsGroupUtil implements IUtil, Runnable {
/**
* 创建并启动子任务下载器
*/
SubDownloadLoader createSubLoader(DTaskWrapper taskWrapper) {
return createSubLoader(taskWrapper, true);
SubDownloadLoader createAndStartSubLoader(DTaskWrapper taskWrapper) {
return createAndStartSubLoader(taskWrapper, true);
}
/**
@ -295,28 +291,10 @@ public abstract class AbsGroupUtil implements IUtil, Runnable {
*
* @param needGetFileInfo {@code true} 需要获取文件信息{@code false} 不需要获取文件信息
*/
SubDownloadLoader createSubLoader(DTaskWrapper taskWrapper, boolean needGetFileInfo) {
if (getTaskType() == HTTP_GROUP) {
cloneHeader(taskWrapper);
}
SubDownloadLoader createAndStartSubLoader(DTaskWrapper taskWrapper, boolean needGetFileInfo) {
SubDownloadLoader loader = new SubDownloadLoader(mScheduler, taskWrapper, needGetFileInfo);
mExeLoader.put(loader.getKey(), loader);
mSubQueue.startTask(loader);
return loader;
}
/**
* 子任务使用父包裹器的属性
*/
private void cloneHeader(DTaskWrapper taskWrapper) {
HttpTaskDelegate groupDelegate = mGTWrapper.asHttp();
HttpTaskDelegate subDelegate = taskWrapper.asHttp();
// 设置属性
subDelegate.setFileLenAdapter(groupDelegate.getFileLenAdapter());
subDelegate.setRequestEnum(groupDelegate.getRequestEnum());
subDelegate.setHeaders(groupDelegate.getHeaders());
subDelegate.setProxy(groupDelegate.getProxy());
subDelegate.setParams(groupDelegate.getParams());
}
}

@ -18,6 +18,7 @@ package com.arialyy.aria.core.download.group;
import com.arialyy.aria.core.common.CompleteInfo;
import com.arialyy.aria.core.common.IUtil;
import com.arialyy.aria.core.common.OnFileInfoCallback;
import com.arialyy.aria.core.common.http.HttpTaskDelegate;
import com.arialyy.aria.core.download.DGTaskWrapper;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.download.DownloadEntity;
@ -49,11 +50,11 @@ public class DownloadGroupUtil extends AbsGroupUtil implements IUtil {
return HTTP_GROUP;
}
@Override public void onCancel() {
super.onCancel();
@Override public void onPreCancel() {
super.onPreCancel();
}
@Override protected boolean onStop() {
@Override protected boolean onPreStop() {
// 如果是isUnknownSize()标志,并且获取大小没有完成,则直接回调onStop
if (mPool != null && !getLenComplete) {
ALog.d(TAG, "获取长度未完成的情况下,停止组合任务");
@ -83,7 +84,7 @@ public class DownloadGroupUtil extends AbsGroupUtil implements IUtil {
} else {
for (DTaskWrapper wrapper : mGTWrapper.getSubTaskWrapper()) {
if (wrapper.getState() != IEntity.STATE_COMPLETE) {
createSubLoader(wrapper);
createAndStartSubLoader(wrapper);
}
}
}
@ -99,10 +100,16 @@ public class DownloadGroupUtil extends AbsGroupUtil implements IUtil {
int failCount;
@Override public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (DTaskWrapper dTaskWrapper : mGTWrapper.getSubTaskWrapper()) {
cloneHeader(dTaskWrapper);
mPool.submit(new HttpFileInfoThread(dTaskWrapper, new OnFileInfoCallback() {
@Override public void onComplete(String url, CompleteInfo info) {
createSubLoader((DTaskWrapper) info.wrapper, false);
createAndStartSubLoader((DTaskWrapper) info.wrapper, false);
count++;
checkGetSizeComplete(count, failCount);
}
@ -133,15 +140,30 @@ public class DownloadGroupUtil extends AbsGroupUtil implements IUtil {
mGTWrapper.getEntity().setConvertFileSize(CommonUtil.formatFileSize(size));
mGTWrapper.getEntity().setFileSize(size);
mGTWrapper.getEntity().update();
mGTWrapper.asHttp().setFileLenAdapter(null);
getLenComplete = true;
ALog.d(TAG, String.format("获取组合任务长度完成,len:%s", size));
} else if (failCount == mGTWrapper.getSubTaskWrapper().size()) {
mListener.onFail(true, new AriaIOException(TAG, "获取子任务长度失败"));
}
mGTWrapper.asHttp().setFileLenAdapter(null);
synchronized (LOCK) {
LOCK.notify();
}
}
/**
* 子任务使用父包裹器的属性
*/
private void cloneHeader(DTaskWrapper taskWrapper) {
HttpTaskDelegate groupDelegate = mGTWrapper.asHttp();
HttpTaskDelegate subDelegate = taskWrapper.asHttp();
// 设置属性
subDelegate.setFileLenAdapter(groupDelegate.getFileLenAdapter());
subDelegate.setRequestEnum(groupDelegate.getRequestEnum());
subDelegate.setHeaders(groupDelegate.getHeaders());
subDelegate.setProxy(groupDelegate.getProxy());
subDelegate.setParams(groupDelegate.getParams());
}
}

@ -60,7 +60,7 @@ public class FtpDirDownloadUtil extends AbsGroupUtil {
private void startDownload() {
for (DTaskWrapper wrapper : mGTWrapper.getSubTaskWrapper()) {
if (wrapper.getState() != IEntity.STATE_COMPLETE) {
createSubLoader(wrapper);
createAndStartSubLoader(wrapper);
}
}
}

@ -63,10 +63,16 @@ interface ISubQueue<Fileer extends IUtil> {
void removeTaskFromExecQ(Fileer fileer);
/**
* 删除任务
* 删除任务如果缓存队列中有等待中的任务则启动等待中的任务
*/
void removeTask(Fileer fileer);
/**
* 停止全部任务停止所有正在执行的任务并清空所有等待中的端服务
*/
void removeAllTask();
/**
* 获取下一个任务
*/

@ -168,6 +168,18 @@ class SimpleSubQueue implements ISubQueue<SubDownloadLoader> {
mCache.remove(fileer.getKey());
}
@Override public void removeAllTask() {
ALog.d(TAG, "删除组合任务");
Set<String> keys = mExec.keySet();
for (String key : keys) {
SubDownloadLoader loader = mExec.get(key);
if (loader != null) {
ALog.d(TAG, String.format("停止子任务:%s", loader.getEntity().getFileName()));
loader.cancel();
}
}
}
@Override public SubDownloadLoader getNextTask() {
Iterator<String> keys = mCache.keySet().iterator();
if (keys.hasNext()) {

@ -87,8 +87,10 @@ class SubDownloadLoader implements IUtil {
}
@Override public void cancel() {
if (mDownloader != null) {
if (mDownloader != null && isRunning()) {
mDownloader.cancel();
} else {
mSchedulers.obtainMessage(ISchedulers.CANCEL, this).sendToTarget();
}
}

@ -1,10 +0,0 @@
package com.arialyy.aria.core.inf;
public abstract class AbsHttpFileLenAdapter implements IHttpFileLenAdapter {
@Override public Object clone() throws CloneNotSupportedException {
//AbsHttpFileLenAdapter newAdapter = super.clone();
return super.clone();
}
}

@ -29,7 +29,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* 线程管理器
* 线程任务管理器
*/
public class ThreadTaskManager {
private static volatile ThreadTaskManager INSTANCE = null;
@ -80,7 +80,7 @@ public class ThreadTaskManager {
*
* @param key 任务对应的key{@link AbsTaskWrapper#getKey()}
*/
public synchronized void stopTaskThread(String key) {
public synchronized void removeTaskThread(String key) {
if (mExePool.isShutdown()) {
ALog.e(TAG, "线程池已经关闭");
return;

@ -36,7 +36,7 @@ class BaseUListener extends BaseListener<UploadEntity, UTaskWrapper, UploadTask>
mTaskWrapper.setState(state);
mEntity.setState(state);
if (state == IEntity.STATE_CANCEL) {
CommonUtil.delTaskRecord(mEntity.getFilePath(), 2, mTaskWrapper.isRemoveFile());
CommonUtil.delTaskRecord(mEntity.getFilePath(), 2, mTaskWrapper.isRemoveFile(), true);
} else if (state == IEntity.STATE_STOP) {
mEntity.setStopTime(System.currentTimeMillis());
} else if (state == IEntity.STATE_COMPLETE) {

@ -96,8 +96,7 @@ class FtpThreadTask extends AbsFtpThreadTask<UploadEntity, UTaskWrapper> {
writeConfig(true, getConfig().END_LOCATION);
getState().COMPLETE_THREAD_NUM++;
if (getState().isComplete()) {
getState().TASK_RECORD.deleteData();
mListener.onComplete();
sendCompleteMsg();
}
if (getState().isFail()) {
mListener.onFail(false, new TaskException(TAG,

@ -101,7 +101,7 @@ class HttpThreadTask extends AbsThreadTask<UploadEntity, UTaskWrapper> {
}
uploadFile(writer, taskDelegate.getAttachment(), uploadFile);
getEntity().setResponseStr(finish(writer));
mListener.onComplete();
sendCompleteMsg();
} catch (Exception e) {
e.printStackTrace();
fail(new TaskException(TAG,

@ -36,7 +36,7 @@ class DBConfig {
static boolean DEBUG = false;
static Map<String, Class> mapping = new HashMap<>();
static String DB_NAME;
static int VERSION = 45;
static int VERSION = 46;
/**
* 是否将数据库保存在Sd卡{@code true}

@ -1,290 +1,291 @@
/*
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.arialyy.aria.orm;
import java.util.ArrayList;
import java.util.List;
/**
* Created by lyy on 2015/11/2. 所有数据库实体父类
*/
public abstract class DbEntity {
private static final Object LOCK = new Object();
protected long rowID = -1;
protected DbEntity() {
}
/**
* 查询关联数据
* <code>
* DbEntity.findRelationData(DGEntityWrapper.class, "downloadUrl=?", downloadUrl);
* </code>
*
* @param expression 查询条件
*/
public static <T extends AbsDbWrapper> List<T> findRelationData(Class<T> clazz,
String... expression) {
return DelegateWrapper.getInstance().findRelationData(clazz, expression);
}
/**
* 分页查询关联数据
*
* <code>
* DbEntity.findRelationData(DGEntityWrapper.class, 0, 10, "downloadUrl=?", downloadUrl);
* </code>
*
* @param expression 查询条件
* @param page 需要查询的页数从1开始如果page小于1 num 小于1返回null
* @param num 每页返回的数量
* @return 没有数据返回null如果页数大于总页数返回null
*/
public static <T extends AbsDbWrapper> List<T> findRelationData(Class<T> clazz, int page, int num,
String... expression) {
if (page < 1 || num < 1) {
return null;
}
return DelegateWrapper.getInstance().findRelationData(clazz, page, num, expression);
}
/**
* 检查某个字段的值是否存在
*
* @param expression 字段和值"downloadPath=?"
* @return {@code true}该字段的对应的value已存在
*/
public static boolean checkDataExist(Class clazz, String... expression) {
return DelegateWrapper.getInstance().checkDataExist(clazz, expression);
}
/**
* 清空表数据
*/
public static <T extends DbEntity> void clean(Class<T> clazz) {
DelegateWrapper.getInstance().clean(clazz);
}
/**
* 直接执行sql语句
*/
public static void exeSql(String sql) {
DelegateWrapper.getInstance().exeSql(sql);
}
/**
* 查询所有数据
*
* @return 没有数据返回null
*/
public static <T extends DbEntity> List<T> findAllData(Class<T> clazz) {
return DelegateWrapper.getInstance().findAllData(clazz);
}
/**
* 查询第一条数据
*/
public static <T extends DbEntity> T findFirst(Class<T> clazz) {
List<T> list = findAllData(clazz);
return (list == null || list.size() == 0) ? null : list.get(0);
}
/**
* 查询一组数据
* <code>
* DbEntity.findFirst(DownloadEntity.class, "downloadUrl=?", downloadUrl);
* </code>
*
* @return 没有数据返回null
*/
public static <T extends DbEntity> List<T> findDatas(Class<T> clazz, String... expression) {
return DelegateWrapper.getInstance().findData(clazz, expression);
}
/**
* 分页查询数据
* <code>
* DbEntity.findFirst(DownloadEntity.class, 0, 10, "downloadUrl=?", downloadUrl);
* </code>
*
* @param page 需要查询的页数从1开始如果page小于1 num 小于1返回null
* @param num 每页返回的数量
* @return 没有数据返回null如果页数大于总页数返回null
*/
public static <T extends DbEntity> List<T> findDatas(Class<T> clazz, int page, int num,
String... expression) {
if (page < 1 || num < 1) {
return null;
}
return DelegateWrapper.getInstance().findData(clazz, page, num, expression);
}
/**
* 模糊查询一组数据
* <code>
* DbEntity.findDataByFuzzy(DownloadEntity.class, "downloadUrl like http://");
* </code>
*
* @return 没有数据返回null
*/
public static <T extends DbEntity> List<T> findDataByFuzzy(Class<T> clazz, String conditions) {
return DelegateWrapper.getInstance().findDataByFuzzy(clazz, conditions);
}
/**
* 模糊查询一组数据
* <code>
* DbEntity.findDataByFuzzy(DownloadEntity.class, 0, 10, "downloadUrl like http://");
* </code>
*
* @param page 需要查询的页数从1开始如果page小于1 num 小于1返回null
* @param num 每页返回的数量
* @return 没有数据返回null如果页数大于总页数返回null
*/
public static <T extends DbEntity> List<T> findDataByFuzzy(Class<T> clazz, int page, int num,
String conditions) {
return DelegateWrapper.getInstance().findDataByFuzzy(clazz, page, num, conditions);
}
/**
* 查询一行数据
* <code>
* DbEntity.findFirst(DownloadEntity.class, "downloadUrl=?", downloadUrl);
* </code>
*
* @return 没有数据返回null
*/
public static <T extends DbEntity> T findFirst(Class<T> clazz, String... expression) {
DelegateWrapper util = DelegateWrapper.getInstance();
List<T> datas = util.findData(clazz, expression);
return datas == null ? null : datas.size() > 0 ? datas.get(0) : null;
}
/**
* 插入多条数据
*/
public static void insertManyData(List<DbEntity> entities) {
checkListData(entities);
DelegateWrapper.getInstance().insertManyData(entities);
}
/**
* 修改多条数据
*/
public static void updateManyData(List<DbEntity> entities) {
checkListData(entities);
DelegateWrapper.getInstance().updateManyData(entities);
}
/**
* 保存多条数据通过rowID来判断记录存在以否如果数据库已有记录则更新该记录如果数据库中没有记录则保存该记录
*/
public static <T extends DbEntity> void saveAll(List<T> entities) {
checkListData(entities);
List<DbEntity> insertD = new ArrayList<>();
List<DbEntity> updateD = new ArrayList<>();
DelegateWrapper wrapper = DelegateWrapper.getInstance();
for (DbEntity entity : entities) {
if (entity.rowID == -1) {
insertD.add(entity);
continue;
}
if (wrapper.isExist(entity.getClass(), entity.rowID)) {
updateD.add(entity);
} else {
insertD.add(entity);
}
}
if (!insertD.isEmpty()) {
wrapper.insertManyData(insertD);
} else {
wrapper.updateManyData(updateD);
}
}
/**
* 检查批量操作的列表数据如果数据为空抛出{@link NullPointerException}
*/
private static <T extends DbEntity> void checkListData(List<T> entities) {
if (entities == null || entities.isEmpty()) {
throw new NullPointerException("列表数据为空");
}
}
/**
* 删除当前数据
*/
public void deleteData() {
deleteData(getClass(), "rowid=?", rowID + "");
}
/**
* 根据条件删除数据
* <code>
* DbEntity.deleteData(DownloadEntity.class, "downloadUrl=?", downloadUrl);
* </code>
*/
public static <T extends DbEntity> void deleteData(Class<T> clazz, String... expression) {
DelegateWrapper util = DelegateWrapper.getInstance();
util.delData(clazz, expression);
}
/**
* 修改数据
*/
public void update() {
DelegateWrapper.getInstance().updateData(this);
}
/**
* 保存自身如果表中已经有数据则更新数据否则插入数据 只有 target中checkEntity成功后才能保存创建实体部分也不允许保存
*/
public void save() {
synchronized (LOCK) {
if (thisIsExist()) {
update();
} else {
insert();
}
}
}
/**
* 查找数据在表中是否存在
*/
private boolean thisIsExist() {
DelegateWrapper util = DelegateWrapper.getInstance();
return rowID != -1 && util.isExist(getClass(), rowID);
}
/**
* 表是否存在
*
* @return {@code true} 存在
*/
public static boolean tableExists(Class<DbEntity> clazz) {
return DelegateWrapper.getInstance().tableExists(clazz);
}
/**
* 插入数据只有 target中checkEntity成功后才能插入创建实体部分也不允许操作
*/
public void insert() {
DelegateWrapper.getInstance().insertData(this);
}
/*
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.arialyy.aria.orm;
import java.util.ArrayList;
import java.util.List;
/**
* Created by lyy on 2015/11/2. 所有数据库实体父类
*/
public abstract class DbEntity {
private static final Object LOCK = new Object();
protected long rowID = -1;
protected DbEntity() {
}
/**
* 查询关联数据
* <code>
* DbEntity.findRelationData(DGEntityWrapper.class, "downloadUrl=?", downloadUrl);
* </code>
*
* @param expression 查询条件
*/
public static <T extends AbsDbWrapper> List<T> findRelationData(Class<T> clazz,
String... expression) {
return DelegateWrapper.getInstance().findRelationData(clazz, expression);
}
/**
* 分页查询关联数据
*
* <code>
* DbEntity.findRelationData(DGEntityWrapper.class, 0, 10, "downloadUrl=?", downloadUrl);
* </code>
*
* @param expression 查询条件
* @param page 需要查询的页数从1开始如果page小于1 num 小于1返回null
* @param num 每页返回的数量
* @return 没有数据返回null如果页数大于总页数返回null
*/
public static <T extends AbsDbWrapper> List<T> findRelationData(Class<T> clazz, int page, int num,
String... expression) {
if (page < 1 || num < 1) {
return null;
}
return DelegateWrapper.getInstance().findRelationData(clazz, page, num, expression);
}
/**
* 检查某个字段的值是否存在
*
* @param expression 字段和值"downloadPath=?"
* @return {@code true}该字段的对应的value已存在
*/
public static boolean checkDataExist(Class clazz, String... expression) {
return DelegateWrapper.getInstance().checkDataExist(clazz, expression);
}
/**
* 清空表数据
*/
public static <T extends DbEntity> void clean(Class<T> clazz) {
DelegateWrapper.getInstance().clean(clazz);
}
/**
* 直接执行sql语句
*/
public static void exeSql(String sql) {
DelegateWrapper.getInstance().exeSql(sql);
}
/**
* 查询所有数据
*
* @return 没有数据返回null
*/
public static <T extends DbEntity> List<T> findAllData(Class<T> clazz) {
return DelegateWrapper.getInstance().findAllData(clazz);
}
/**
* 查询第一条数据
*/
public static <T extends DbEntity> T findFirst(Class<T> clazz) {
List<T> list = findAllData(clazz);
return (list == null || list.size() == 0) ? null : list.get(0);
}
/**
* 查询一组数据
* <code>
* DbEntity.findFirst(DownloadEntity.class, "downloadUrl=?", downloadUrl);
* </code>
*
* @return 没有数据返回null
*/
public static <T extends DbEntity> List<T> findDatas(Class<T> clazz, String... expression) {
return DelegateWrapper.getInstance().findData(clazz, expression);
}
/**
* 分页查询数据
* <code>
* DbEntity.findFirst(DownloadEntity.class, 0, 10, "downloadUrl=?", downloadUrl);
* </code>
*
* @param page 需要查询的页数从1开始如果page小于1 num 小于1返回null
* @param num 每页返回的数量
* @return 没有数据返回null如果页数大于总页数返回null
*/
public static <T extends DbEntity> List<T> findDatas(Class<T> clazz, int page, int num,
String... expression) {
if (page < 1 || num < 1) {
return null;
}
return DelegateWrapper.getInstance().findData(clazz, page, num, expression);
}
/**
* 模糊查询一组数据
* <code>
* DbEntity.findDataByFuzzy(DownloadEntity.class, "downloadUrl like http://");
* </code>
*
* @return 没有数据返回null
*/
public static <T extends DbEntity> List<T> findDataByFuzzy(Class<T> clazz, String conditions) {
return DelegateWrapper.getInstance().findDataByFuzzy(clazz, conditions);
}
/**
* 模糊查询一组数据
* <code>
* DbEntity.findDataByFuzzy(DownloadEntity.class, 0, 10, "downloadUrl like http://");
* </code>
*
* @param page 需要查询的页数从1开始如果page小于1 num 小于1返回null
* @param num 每页返回的数量
* @return 没有数据返回null如果页数大于总页数返回null
*/
public static <T extends DbEntity> List<T> findDataByFuzzy(Class<T> clazz, int page, int num,
String conditions) {
return DelegateWrapper.getInstance().findDataByFuzzy(clazz, page, num, conditions);
}
/**
* 查询一行数据
* <code>
* DbEntity.findFirst(DownloadEntity.class, "downloadUrl=?", downloadUrl);
* </code>
*
* @return 没有数据返回null
*/
public static <T extends DbEntity> T findFirst(Class<T> clazz, String... expression) {
DelegateWrapper util = DelegateWrapper.getInstance();
List<T> datas = util.findData(clazz, expression);
return datas == null ? null : datas.size() > 0 ? datas.get(0) : null;
}
/**
* 插入多条数据
*/
public static <T extends DbEntity> void insertManyData(List<T> entities) {
checkListData(entities);
DelegateWrapper.getInstance().insertManyData(entities);
}
/**
* 修改多条数据
*/
public static <T extends DbEntity> void updateManyData(List<T> entities) {
checkListData(entities);
DelegateWrapper.getInstance().updateManyData(entities);
}
/**
* 保存多条数据通过rowID来判断记录存在以否如果数据库已有记录则更新该记录如果数据库中没有记录则保存该记录
*/
public static <T extends DbEntity> void saveAll(List<T> entities) {
checkListData(entities);
List<T> insertD = new ArrayList<>();
List<T> updateD = new ArrayList<>();
DelegateWrapper wrapper = DelegateWrapper.getInstance();
for (T entity : entities) {
if (entity.rowID == -1) {
insertD.add(entity);
continue;
}
if (wrapper.isExist(entity.getClass(), entity.rowID)) {
updateD.add(entity);
} else {
insertD.add(entity);
}
}
if (!insertD.isEmpty()) {
wrapper.insertManyData(insertD);
}
if (!updateD.isEmpty()) {
wrapper.updateManyData(updateD);
}
}
/**
* 检查批量操作的列表数据如果数据为空抛出{@link NullPointerException}
*/
private static <T extends DbEntity> void checkListData(List<T> entities) {
if (entities == null || entities.isEmpty()) {
throw new NullPointerException("列表数据为空");
}
}
/**
* 删除当前数据
*/
public void deleteData() {
deleteData(getClass(), "rowid=?", rowID + "");
}
/**
* 根据条件删除数据
* <code>
* DbEntity.deleteData(DownloadEntity.class, "downloadUrl=?", downloadUrl);
* </code>
*/
public static <T extends DbEntity> void deleteData(Class<T> clazz, String... expression) {
DelegateWrapper util = DelegateWrapper.getInstance();
util.delData(clazz, expression);
}
/**
* 修改数据
*/
public void update() {
DelegateWrapper.getInstance().updateData(this);
}
/**
* 保存自身如果表中已经有数据则更新数据否则插入数据 只有 target中checkEntity成功后才能保存创建实体部分也不允许保存
*/
public void save() {
synchronized (LOCK) {
if (thisIsExist()) {
update();
} else {
insert();
}
}
}
/**
* 查找数据在表中是否存在
*/
private boolean thisIsExist() {
DelegateWrapper util = DelegateWrapper.getInstance();
return rowID != -1 && util.isExist(getClass(), rowID);
}
/**
* 表是否存在
*
* @return {@code true} 存在
*/
public static boolean tableExists(Class<DbEntity> clazz) {
return DelegateWrapper.getInstance().tableExists(clazz);
}
/**
* 插入数据只有 target中checkEntity成功后才能插入创建实体部分也不允许操作
*/
public void insert() {
DelegateWrapper.getInstance().insertData(this);
}
}

@ -69,7 +69,7 @@ class DelegateUpdate extends AbsDelegate {
/**
* 更新多条记录
*/
synchronized void updateManyData(SQLiteDatabase db, List<DbEntity> dbEntities) {
synchronized <T extends DbEntity> void updateManyData(SQLiteDatabase db, List<T> dbEntities) {
db = checkDb(db);
db.beginTransaction();
try {

@ -117,7 +117,7 @@ public class DelegateWrapper {
/**
* 更新多条数据
*/
void updateManyData(List<DbEntity> dbEntitys) {
<T extends DbEntity> void updateManyData(List<T> dbEntitys) {
mDManager.getDelegate(DelegateUpdate.class).updateManyData(mDb, dbEntitys);
}
@ -182,7 +182,7 @@ public class DelegateWrapper {
/**
* 插入多条数据
*/
void insertManyData(List<DbEntity> dbEntitys) {
<T extends DbEntity> void insertManyData(List<T> dbEntitys) {
mDManager.getDelegate(DelegateUpdate.class).insertManyData(mDb, dbEntitys);
}

@ -98,6 +98,8 @@ final class SqlHelper extends SQLiteOpenHelper {
handle314AriaUpdate(db);
} else if (oldVersion < 45) {
handle360AriaUpdate(db);
} else if (oldVersion < 46) {
db.execSQL("UPDATE ThreadRecord SET threadId=0 WHERE threadId=-1");
} else {
handleDbUpdate(db, null, null);
}

@ -32,6 +32,7 @@ import com.arialyy.aria.core.command.group.GroupCmdFactory;
import com.arialyy.aria.core.command.normal.AbsNormalCmd;
import com.arialyy.aria.core.command.normal.NormalCmdFactory;
import com.arialyy.aria.core.common.AbsFileer;
import com.arialyy.aria.core.common.RecordWrapper;
import com.arialyy.aria.core.common.TaskRecord;
import com.arialyy.aria.core.common.ThreadRecord;
import com.arialyy.aria.core.download.DownloadEntity;
@ -587,6 +588,21 @@ public class CommonUtil {
return "";
}
/**
* 删除任务组记录
*
* @param removeFile {@code true} 不仅删除任务数据库记录还会删除已经删除完成的文件 {@code false}如果任务已经完成只删除任务数据库记录
*/
public static void delGroupTaskRecord(String groupHash, boolean removeFile) {
if (TextUtils.isEmpty(groupHash)) {
ALog.e(TAG, "删除下载任务组记录失败,groupHash为null");
return;
}
DownloadGroupEntity groupEntity = DbDataHelper.getDGEntity(groupHash);
delGroupTaskRecord(groupEntity, removeFile);
}
/**
* 删除任务组记录
*
@ -597,23 +613,28 @@ public class CommonUtil {
ALog.e(TAG, "删除下载任务组记录失败,任务组实体为null");
return;
}
List<TaskRecord> records =
DbEntity.findDatas(TaskRecord.class, "dGroupHash=?", groupEntity.getGroupHash());
List<RecordWrapper> records =
DbEntity.findRelationData(RecordWrapper.class, "dGroupHash=?", groupEntity.getGroupHash());
if (records == null || records.isEmpty()) {
ALog.w(TAG, "组任务记录删除失败,记录为null");
} else {
for (TaskRecord record : records) {
for (RecordWrapper record : records) {
if (record == null || record.taskRecord == null) {
continue;
}
// 删除分块文件
if (record.isBlock) {
for (int i = 0, len = record.threadNum; i < len; i++) {
File partFile = new File(String.format(AbsFileer.SUB_PATH, record.filePath, i));
if (record.taskRecord.isBlock) {
for (int i = 0, len = record.taskRecord.threadNum; i < len; i++) {
File partFile =
new File(String.format(AbsFileer.SUB_PATH, record.taskRecord.filePath, i));
if (partFile.exists()) {
partFile.delete();
}
}
}
record.deleteData();
DbEntity.deleteData(ThreadRecord.class, "key=?", record.taskRecord.filePath);
record.taskRecord.deleteData();
}
}
@ -632,12 +653,13 @@ public class CommonUtil {
if (dir.exists() && (removeFile || !groupEntity.isComplete())) {
dir.delete();
}
groupEntity.deleteData();
}
DbEntity.deleteData(DownloadEntity.class, "groupHash=?", groupEntity.getGroupHash());
DbEntity.deleteData(DownloadGroupEntity.class, "groupHash=?", groupEntity.getGroupHash());
}
/**
* 删除任务记录
* 删除任务记录默认删除任务实体
*
* @param removeFile {@code true} 不仅删除任务数据库记录还会删除已经完成的文件 {@code false}如果任务已经完成只删除任务数据库记录
*/
@ -655,7 +677,7 @@ public class CommonUtil {
ALog.w(TAG, "删除记录失败,未知类型");
return;
}
delTaskRecord(filePath, type, removeFile);
delTaskRecord(filePath, type, removeFile, true);
}
/**
@ -664,21 +686,28 @@ public class CommonUtil {
* @param filePath 文件路径
* @param removeFile {@code true} 不仅删除任务数据库记录还会删除已经删除完成的文件 {@code false}如果任务已经完成只删除任务数据库记录
* @param type {@code 1}下载任务的记录{@code 2} 上传任务的记录 {@code false}如果任务已经完成只删除任务数据库记录
* @param removeEntity {@code true} 删除任务实体
*/
public static void delTaskRecord(String filePath, int type, boolean removeFile) {
public static void delTaskRecord(String filePath, int type, boolean removeFile,
boolean removeEntity) {
if (TextUtils.isEmpty(filePath)) {
throw new NullPointerException("删除记录失败,文件路径为空");
}
if (type != 1 && type != 2) {
throw new IllegalArgumentException("任务记录类型错误");
}
TaskRecord record = DbEntity.findFirst(TaskRecord.class, "filePath=?", filePath);
DbEntity.deleteData(ThreadRecord.class, "key=?", filePath);
List<RecordWrapper> recordWrapper =
DbEntity.findRelationData(RecordWrapper.class, "filePath=?", filePath);
DbEntity.deleteData(ThreadRecord.class, "key=?", filePath); // 必须先获取完成数据再删除线程记录
File file = new File(filePath);
if (record == null) {
if (recordWrapper == null
|| recordWrapper.isEmpty()
|| recordWrapper.get(0) == null
|| recordWrapper.get(0).taskRecord == null) {
ALog.w(TAG, "记录为空");
} else {
TaskRecord record = recordWrapper.get(0).taskRecord;
// 删除分块文件
if (record.isBlock) {
for (int i = 0, len = record.threadNum; i < len; i++) {
@ -688,28 +717,31 @@ public class CommonUtil {
}
}
}
// 删除任务记录
record.deleteData();
}
if (file.exists() && removeFile) {
file.delete();
}
//下载任务实体和下载实体为一对一关系,下载实体删除,任务实体自动删除
if (type == 1) {
DbEntity.deleteData(DownloadEntity.class, "downloadPath=?", filePath);
} else {
DbEntity.deleteData(UploadEntity.class, "filePath=?", filePath);
if (removeEntity) {
if (type == 1) {
DbEntity.deleteData(DownloadEntity.class, "downloadPath=?", filePath);
} else {
DbEntity.deleteData(UploadEntity.class, "filePath=?", filePath);
}
}
}
/**
* 删除任务记录默认删除文件
* 删除任务记录默认删除文件删除任务实体
*
* @param filePath 文件路径
* @param type {@code 1}下载任务的记录{@code 2} 上传任务的记录 {@code false}如果任务已经完成只删除任务数据库记录
*/
public static void delTaskRecord(String filePath, int type) {
delTaskRecord(filePath, type, false);
delTaskRecord(filePath, type, false, true);
}
/**
@ -925,9 +957,9 @@ public class CommonUtil {
* 获取当前类里面的所在字段
*/
public static Field[] getFields(Class clazz) {
Field[] fields = null;
Field[] fields;
fields = clazz.getDeclaredFields();
if (fields == null || fields.length == 0) {
if (fields.length == 0) {
Class superClazz = clazz.getSuperclass();
if (superClazz != null) {
fields = getFields(superClazz);
@ -1016,7 +1048,7 @@ public class CommonUtil {
* @return 对象名
*/
public static String getClassName(Object obj) {
String arrays[] = obj.getClass().getName().split("\\.");
String[] arrays = obj.getClass().getName().split("\\.");
return arrays[arrays.length - 1];
}
@ -1027,7 +1059,7 @@ public class CommonUtil {
* @return 对象名
*/
public static String getClassName(Class clazz) {
String arrays[] = clazz.getName().split("\\.");
String[] arrays = clazz.getName().split("\\.");
return arrays[arrays.length - 1];
}
@ -1165,7 +1197,7 @@ public class CommonUtil {
blockFile.renameTo(new File(String.format(AbsFileer.SUB_PATH, newPath, tr.threadId)));
}
}
DbEntity.saveAll(record.threadRecords);
DbEntity.updateManyData(record.threadRecords);
}
}

@ -17,6 +17,7 @@ package com.arialyy.aria.util;
import com.arialyy.aria.core.common.RecordWrapper;
import com.arialyy.aria.core.common.TaskRecord;
import com.arialyy.aria.core.common.ThreadRecord;
import com.arialyy.aria.core.download.DGEntityWrapper;
import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.download.DownloadGroupEntity;
@ -37,12 +38,12 @@ public class DbDataHelper {
* @return 没有记录返回null有记录则返回任务记录
*/
public static TaskRecord getTaskRecord(String filePath) {
List<RecordWrapper> records =
List<RecordWrapper> record =
DbEntity.findRelationData(RecordWrapper.class, "TaskRecord.filePath=?", filePath);
if (records == null || records.size() == 0) {
if (record == null || record.size() == 0) {
return null;
}
return records.get(0).taskRecord;
return record.get(0).taskRecord;
}
/**

@ -13,6 +13,16 @@
- 组合任务新增`unknownSize()`,用于处理组合任务大小未知的情况,https://github.com/AriaLyy/Aria/issues/380
- 优化`AbsThreadTask`代码
- 新增文件长度处理功能 https://github.com/AriaLyy/Aria/issues/393
```java
.setFileLenAdapter(new IHttpFileLenAdapter() {
@Override public long handleFileLen(Map<String, List<String>> headers) {
...
// 处理header中的文件长度
return fileLen;
}
})
```
- 修复组合任务多次回调`onStop`注解的问题
- 优化`isRunning()`的逻辑,任务是否在执行的判断将更加准确
- 修复多次重复快速点击`暂停、开始`时,任务有可能重复下载的问题

@ -24,8 +24,8 @@ import com.arialyy.aria.core.Aria;
import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.download.DownloadGroupEntity;
import com.arialyy.aria.core.download.DownloadGroupTask;
import com.arialyy.aria.core.inf.AbsHttpFileLenAdapter;
import com.arialyy.aria.core.inf.IHttpFileLenAdapter;
import com.arialyy.aria.util.ALog;
import com.arialyy.frame.util.show.L;
import com.arialyy.frame.util.show.T;
import com.arialyy.simple.R;
@ -59,6 +59,7 @@ public class DownloadGroupActivity extends BaseActivity<ActivityDownloadGroupBin
getBinding().setProgress(entity.isComplete() ? 100
: (int) (entity.getCurrentProgress() * 100 / entity.getFileSize()));
}
ALog.d(TAG, "size = " + entity.getSubEntities().size() + "; len = " + entity.getConvertFileSize());
}
mChildList.setOnItemClickListener(new SubStateLinearLayout.OnItemClickListener() {
@ -90,18 +91,18 @@ public class DownloadGroupActivity extends BaseActivity<ActivityDownloadGroupBin
//.setSubFileName(getModule(GroupModule.class).getSubName2())
.setSubFileName(getModule(GroupModule.class).getSubName())
.unknownSize()
//.setFileLenAdapter(new AbsHttpFileLenAdapter() {
// @Override public long handleFileLen(Map<String, List<String>> headers) {
//
// List<String> sLength = headers.get("Content-Length");
// if (sLength == null || sLength.isEmpty()) {
// return -1;
// }
// String temp = sLength.get(0);
//
// return Long.parseLong(temp);
// }
//})
.setFileLenAdapter(new IHttpFileLenAdapter() {
@Override public long handleFileLen(Map<String, List<String>> headers) {
List<String> sLength = headers.get("Content-Length");
if (sLength == null || sLength.isEmpty()) {
return -1;
}
String temp = sLength.get(0);
return Long.parseLong(temp);
}
})
//.setFileSize(114981416)
//.updateUrls(temp)
.start();

Loading…
Cancel
Save