优化组合任务target

v3.6.6
laoyuyu 6 years ago
parent b3501dc3c3
commit 4b8eeca164
  1. 194
      Aria/src/main/java/com/arialyy/aria/core/download/AbsDGTarget.java
  2. 2
      Aria/src/main/java/com/arialyy/aria/core/download/AbsDTarget.java
  3. 159
      Aria/src/main/java/com/arialyy/aria/core/download/AbsGroupDelegate.java
  4. 8
      Aria/src/main/java/com/arialyy/aria/core/download/DNormalDelegate.java
  5. 247
      Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTarget.java
  6. 2
      Aria/src/main/java/com/arialyy/aria/core/download/DownloadTarget.java
  7. 91
      Aria/src/main/java/com/arialyy/aria/core/download/FtpDirDelegate.java
  8. 104
      Aria/src/main/java/com/arialyy/aria/core/download/FtpDirDownloadTarget.java
  9. 2
      Aria/src/main/java/com/arialyy/aria/core/download/FtpDownloadTarget.java
  10. 238
      Aria/src/main/java/com/arialyy/aria/core/download/HttpGroupDelegate.java
  11. 58
      Aria/src/main/java/com/arialyy/aria/core/inf/IGroupTarget.java
  12. 17
      Aria/src/main/java/com/arialyy/aria/core/inf/INormalTarget.java
  13. 8
      Aria/src/main/java/com/arialyy/aria/core/upload/UNormalDelegate.java
  14. 10
      Aria/src/main/java/com/arialyy/aria/orm/SqlHelper.java
  15. 14
      README.md
  16. BIN
      img/group_task.gif

@ -33,18 +33,18 @@ import java.util.List;
*/
abstract class AbsDGTarget<TARGET extends AbsDGTarget> extends AbsTarget<TARGET> {
/**
* 组任务名
*/
String mGroupHash;
/**
* 文件夹临时路径
*/
String mDirPathTemp;
/**
* 是否需要修改路径
*/
boolean needModifyPath = false;
///**
// * 组任务名
// */
//String mGroupHash;
///**
// * 文件夹临时路径
// */
//String mDirPathTemp;
///**
// * 是否需要修改路径
// */
//boolean needModifyPath = false;
private SubTaskManager mSubTaskManager;
@ -79,93 +79,93 @@ abstract class AbsDGTarget<TARGET extends AbsDGTarget> extends AbsTarget<TARGET>
return (TARGET) this;
}
@Override public boolean taskExists() {
return DbEntity.checkDataExist(DownloadGroupEntity.class, "groupHash=?", mGroupHash);
}
//@Override public boolean taskExists() {
// return DbEntity.checkDataExist(DownloadGroupEntity.class, "groupHash=?", mGroupHash);
//}
/**
* 设置任务组的文件夹路径在Aria中任务组的所有子任务都会下载到以任务组组名的文件夹中
* groupDirPath = "/mnt/sdcard/download/group_test"
* <pre>
* {@code
* + mnt
* + sdcard
* + download
* + group_test
* - task1.apk
* - task2.apk
* - task3.apk
* ....
*
* }
* </pre>
*
* @param dirPath 任务组保存文件夹路径
*/
@CheckResult
public TARGET setDirPath(String dirPath) {
mDirPathTemp = dirPath;
return (TARGET) this;
}
///**
// * 设置任务组的文件夹路径,在Aria中,任务组的所有子任务都会下载到以任务组组名的文件夹中。
// * 如:groupDirPath = "/mnt/sdcard/download/group_test"
// * <pre>
// * {@code
// * + mnt
// * + sdcard
// * + download
// * + group_test
// * - task1.apk
// * - task2.apk
// * - task3.apk
// * ....
// *
// * }
// * </pre>
// *
// * @param dirPath 任务组保存文件夹路径
// */
//@CheckResult
//public TARGET setDirPath(String dirPath) {
// mDirPathTemp = dirPath;
// return (TARGET) this;
//}
@Override public boolean isRunning() {
DownloadGroupTask task = DownloadGroupTaskQueue.getInstance().getTask(getEntity().getKey());
return task != null && task.isRunning();
}
//@Override public boolean isRunning() {
// DownloadGroupTask task = DownloadGroupTaskQueue.getInstance().getTask(getEntity().getKey());
// return task != null && task.isRunning();
//}
/**
* 改变任务组文件夹路径修改文件夹路径会将子任务所有路径更换
*
* @param newDirPath 新的文件夹路径
*/
void reChangeDirPath(String newDirPath) {
List<DTaskWrapper> subTasks = getTaskWrapper().getSubTaskWrapper();
if (subTasks != null && !subTasks.isEmpty()) {
List<DbEntity> des = new ArrayList<>();
for (DTaskWrapper dte : subTasks) {
DownloadEntity de = dte.getEntity();
String oldPath = de.getDownloadPath();
String newPath = newDirPath + "/" + de.getFileName();
File file = new File(oldPath);
if (file.exists()) {
file.renameTo(new File(newPath));
}
de.setDownloadPath(newPath);
des.add(de);
}
AbsEntity.saveAll(des);
}
}
/**
* 检查并设置文件夹路径
*
* @return {@code true} 合法
*/
boolean checkDirPath() {
if (TextUtils.isEmpty(mDirPathTemp)) {
ALog.e(TAG, "文件夹路径不能为null");
return false;
} else if (!mDirPathTemp.startsWith("/")) {
ALog.e(TAG, "文件夹路径【" + mDirPathTemp + "】错误");
return false;
}
File file = new File(mDirPathTemp);
if (file.isFile()) {
ALog.e(TAG, "路径【" + mDirPathTemp + "】是文件,请设置文件夹路径");
return false;
}
///**
// * 改变任务组文件夹路径,修改文件夹路径会将子任务所有路径更换
// *
// * @param newDirPath 新的文件夹路径
// */
//void reChangeDirPath(String newDirPath) {
// List<DTaskWrapper> subTasks = getTaskWrapper().getSubTaskWrapper();
// if (subTasks != null && !subTasks.isEmpty()) {
// List<DbEntity> des = new ArrayList<>();
// for (DTaskWrapper dte : subTasks) {
// DownloadEntity de = dte.getEntity();
// String oldPath = de.getDownloadPath();
// String newPath = newDirPath + "/" + de.getFileName();
// File file = new File(oldPath);
// if (file.exists()) {
// file.renameTo(new File(newPath));
// }
// de.setDownloadPath(newPath);
// des.add(de);
// }
// AbsEntity.saveAll(des);
// }
//}
if (TextUtils.isEmpty(getEntity().getDirPath()) || !getEntity().getDirPath()
.equals(mDirPathTemp)) {
if (!file.exists()) {
file.mkdirs();
}
needModifyPath = true;
getEntity().setDirPath(mDirPathTemp);
ALog.i(TAG, String.format("文件夹路径改变,将更新文件夹路径为:%s", mDirPathTemp));
}
return true;
}
///**
// * 检查并设置文件夹路径
// *
// * @return {@code true} 合法
// */
//boolean checkDirPath() {
// if (TextUtils.isEmpty(mDirPathTemp)) {
// ALog.e(TAG, "文件夹路径不能为null");
// return false;
// } else if (!mDirPathTemp.startsWith("/")) {
// ALog.e(TAG, "文件夹路径【" + mDirPathTemp + "】错误");
// return false;
// }
// File file = new File(mDirPathTemp);
// if (file.isFile()) {
// ALog.e(TAG, "路径【" + mDirPathTemp + "】是文件,请设置文件夹路径");
// return false;
// }
//
// if (TextUtils.isEmpty(getEntity().getDirPath()) || !getEntity().getDirPath()
// .equals(mDirPathTemp)) {
// if (!file.exists()) {
// file.mkdirs();
// }
// needModifyPath = true;
// getEntity().setDirPath(mDirPathTemp);
// ALog.i(TAG, String.format("文件夹路径改变,将更新文件夹路径为:%s", mDirPathTemp));
// }
//
// return true;
//}
}

@ -23,7 +23,7 @@ import com.arialyy.aria.util.CommonUtil;
/**
* Created by lyy on 2017/2/28.
*/
abstract class AbsDownloadTarget<TARGET extends AbsDownloadTarget> extends AbsTarget<TARGET> {
abstract class AbsDTarget<TARGET extends AbsDTarget> extends AbsTarget<TARGET> {
/**
* 更新下载url

@ -0,0 +1,159 @@
/*
* 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.core.download;
import android.support.annotation.CheckResult;
import android.text.TextUtils;
import com.arialyy.aria.core.inf.AbsEntity;
import com.arialyy.aria.core.inf.IGroupTarget;
import com.arialyy.aria.core.queue.DownloadGroupTaskQueue;
import com.arialyy.aria.orm.DbEntity;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* Created by lyy on 2019/4/9.
* 下载组合任务功能
*/
abstract class AbsGroupDelegate<TARGET extends AbsDGTarget> implements IGroupTarget {
protected String TAG;
private TARGET mTarget;
private DGTaskWrapper mWrapper;
/**
* 组任务名
*/
private String mGroupHash;
/**
* 文件夹临时路径
*/
private String mDirPathTemp;
/**
* 是否需要修改路径
*/
private boolean needModifyPath = false;
AbsGroupDelegate(TARGET target, DGTaskWrapper wrapper) {
TAG = CommonUtil.getClassName(getClass());
mTarget = target;
mWrapper = wrapper;
setGroupHash(wrapper.getKey());
mTarget.setTaskWrapper(wrapper);
if (getEntity() != null) {
mDirPathTemp = getEntity().getDirPath();
}
}
@Override public boolean isRunning() {
DownloadGroupTask task = DownloadGroupTaskQueue.getInstance().getTask(getEntity().getKey());
return task != null && task.isRunning();
}
@CheckResult
TARGET setDirPath(String dirPath) {
mDirPathTemp = dirPath;
return mTarget;
}
/**
* 改变任务组文件夹路径修改文件夹路径会将子任务所有路径更换
*
* @param newDirPath 新的文件夹路径
*/
void reChangeDirPath(String newDirPath) {
List<DTaskWrapper> subTasks = mWrapper.getSubTaskWrapper();
if (subTasks != null && !subTasks.isEmpty()) {
List<DbEntity> des = new ArrayList<>();
for (DTaskWrapper dte : subTasks) {
DownloadEntity de = dte.getEntity();
String oldPath = de.getDownloadPath();
String newPath = newDirPath + "/" + de.getFileName();
File file = new File(oldPath);
if (file.exists()) {
file.renameTo(new File(newPath));
}
de.setDownloadPath(newPath);
des.add(de);
}
AbsEntity.saveAll(des);
}
}
/**
* 检查并设置文件夹路径
*
* @return {@code true} 合法
*/
@Override public boolean checkDirPath() {
if (TextUtils.isEmpty(mDirPathTemp)) {
ALog.e(TAG, "文件夹路径不能为null");
return false;
} else if (!mDirPathTemp.startsWith("/")) {
ALog.e(TAG, "文件夹路径【" + mDirPathTemp + "】错误");
return false;
}
File file = new File(mDirPathTemp);
if (file.isFile()) {
ALog.e(TAG, "路径【" + mDirPathTemp + "】是文件,请设置文件夹路径");
return false;
}
if (TextUtils.isEmpty(getEntity().getDirPath()) || !getEntity().getDirPath()
.equals(mDirPathTemp)) {
if (!file.exists()) {
file.mkdirs();
}
needModifyPath = true;
getEntity().setDirPath(mDirPathTemp);
ALog.i(TAG, String.format("文件夹路径改变,将更新文件夹路径为:%s", mDirPathTemp));
}
return true;
}
@Override public DownloadGroupEntity getEntity() {
return mWrapper.getEntity();
}
@Override public boolean taskExists() {
return DbEntity.checkDataExist(DownloadGroupEntity.class, "groupHash=?", mWrapper.getKey());
}
DGTaskWrapper getTaskWrapper() {
return mWrapper;
}
boolean isNeedModifyPath() {
return needModifyPath;
}
String getDirPathTemp() {
return mDirPathTemp;
}
TARGET getTarget() {
return mTarget;
}
public String getGroupHash() {
return mGroupHash;
}
public void setGroupHash(String groupHash) {
this.mGroupHash = groupHash;
}
}

@ -17,7 +17,7 @@ package com.arialyy.aria.core.download;
import android.text.TextUtils;
import com.arialyy.aria.core.inf.ITargetHandler;
import com.arialyy.aria.core.inf.ITargetNormal;
import com.arialyy.aria.core.inf.INormalTarget;
import com.arialyy.aria.core.manager.TaskWrapperManager;
import com.arialyy.aria.core.queue.DownloadTaskQueue;
import com.arialyy.aria.orm.DbEntity;
@ -29,7 +29,7 @@ import java.io.File;
* Created by AriaL on 2019/4/5.
* 普通下载任务通用功能处理
*/
class DNormalDelegate<TARGET extends AbsDownloadTarget> implements ITargetNormal<TARGET> {
class DNormalDelegate<TARGET extends AbsDTarget> implements INormalTarget {
private final String TAG = "DNormalDelegate";
private DownloadEntity mEntity;
@ -54,7 +54,7 @@ class DNormalDelegate<TARGET extends AbsDownloadTarget> implements ITargetNormal
initTarget(url, targetName);
}
@Override public void initTarget(String url, String targetName) {
private void initTarget(String url, String targetName) {
DTaskWrapper taskWrapper =
TaskWrapperManager.getInstance().getHttpTaskWrapper(DTaskWrapper.class, url);
mEntity = taskWrapper.getEntity();
@ -67,7 +67,7 @@ class DNormalDelegate<TARGET extends AbsDownloadTarget> implements ITargetNormal
}
}
@Override public TARGET updateUrl(String newUrl) {
TARGET updateUrl(String newUrl) {
if (TextUtils.isEmpty(newUrl)) {
ALog.e(TAG, "url更新失败,newUrl为null");
return mTarget;

@ -17,21 +17,15 @@ package com.arialyy.aria.core.download;
import android.support.annotation.CheckResult;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import com.arialyy.aria.core.common.RequestEnum;
import com.arialyy.aria.core.common.http.HttpHeaderDelegate;
import com.arialyy.aria.core.common.http.PostDelegate;
import com.arialyy.aria.core.inf.IHttpHeaderDelegate;
import com.arialyy.aria.core.manager.TaskWrapperManager;
import com.arialyy.aria.orm.DbEntity;
import com.arialyy.aria.exception.ParamException;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import java.net.Proxy;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Created by AriaL on 2017/6/29.
@ -39,43 +33,33 @@ import java.util.Set;
*/
public class DownloadGroupTarget extends AbsDGTarget<DownloadGroupTarget> implements
IHttpHeaderDelegate<DownloadGroupTarget> {
private HttpHeaderDelegate<DownloadGroupTarget> mDelegate;
/**
* 子任务下载地址
*/
private List<String> mUrls = new ArrayList<>();
/**
* 子任务文件名
*/
private List<String> mSubNameTemp = new ArrayList<>();
private HttpHeaderDelegate<DownloadGroupTarget> mHeaderDelegate;
private HttpGroupDelegate mGroupDelegate;
public DownloadGroupTarget(DownloadGroupEntity groupEntity, String targetName) {
DownloadGroupTarget(DownloadGroupEntity groupEntity, String targetName) {
setTargetName(targetName);
if (groupEntity.getUrls() != null && !groupEntity.getUrls().isEmpty()) {
this.mUrls.addAll(groupEntity.getUrls());
init(groupEntity.getUrls());
} else {
throw new ParamException("组合任务只任务下载地址为空");
}
init();
}
DownloadGroupTarget(List<String> urls, String targetName) {
setTargetName(targetName);
this.mUrls = urls;
init();
init(urls);
}
private void init() {
mGroupHash = CommonUtil.getMd5Code(mUrls);
setTaskWrapper(TaskWrapperManager.getInstance().getDGTaskWrapper(DGTaskWrapper.class, mUrls));
if (getEntity() != null) {
mDirPathTemp = getEntity().getDirPath();
}
mDelegate = new HttpHeaderDelegate<>(this);
private void init(List<String> urls) {
mGroupDelegate = new HttpGroupDelegate(this,
TaskWrapperManager.getInstance().getDGTaskWrapper(DGTaskWrapper.class, urls));
mHeaderDelegate = new HttpHeaderDelegate<>(this);
}
/**
* Post处理
*/
@CheckResult
public PostDelegate asPost() {
return new PostDelegate<>(this);
}
@ -87,24 +71,7 @@ public class DownloadGroupTarget extends AbsDGTarget<DownloadGroupTarget> implem
*/
@CheckResult
public DownloadGroupTarget updateUrls(List<String> urls) {
if (urls == null || urls.isEmpty()) {
throw new NullPointerException("下载地址列表为空");
}
if (urls.size() != mUrls.size()) {
throw new IllegalArgumentException("新下载地址数量和旧下载地址数量不一致");
}
mUrls.clear();
mUrls.addAll(urls);
mGroupHash = CommonUtil.getMd5Code(urls);
getEntity().setGroupHash(mGroupHash);
getEntity().update();
if (getEntity().getSubEntities() != null && !getEntity().getSubEntities().isEmpty()) {
for (DownloadEntity de : getEntity().getSubEntities()) {
de.setGroupHash(mGroupHash);
de.update();
}
}
return this;
return mGroupDelegate.updateUrls(urls);
}
/**
@ -132,9 +99,7 @@ public class DownloadGroupTarget extends AbsDGTarget<DownloadGroupTarget> implem
*/
@CheckResult
public DownloadGroupTarget setGroupUrl(List<String> urls) {
mUrls.clear();
mUrls.addAll(urls);
return this;
return mGroupDelegate.setGroupUrl(urls);
}
/**
@ -147,22 +112,36 @@ public class DownloadGroupTarget extends AbsDGTarget<DownloadGroupTarget> implem
return setSubFileName(subTaskFileName);
}
/**
* 设置任务组的文件夹路径在Aria中任务组的所有子任务都会下载到以任务组组名的文件夹中
* groupDirPath = "/mnt/sdcard/download/group_test"
* <pre>
* {@code
* + mnt
* + sdcard
* + download
* + group_test
* - task1.apk
* - task2.apk
* - task3.apk
* ....
*
* }
* </pre>
*
* @param dirPath 任务组保存文件夹路径
*/
@CheckResult
public DownloadGroupTarget setDirPath(String dirPath) {
return mGroupDelegate.setDirPath(dirPath);
}
/**
* 设置子任务文件名该方法必须在{@link #setDirPath(String)}之后调用否则不生效
*/
@CheckResult
public DownloadGroupTarget setSubFileName(List<String> subTaskFileName) {
if (subTaskFileName == null || subTaskFileName.isEmpty()) {
ALog.e(TAG, "修改子任务的文件名失败:列表为null");
return this;
}
if (subTaskFileName.size() != getTaskWrapper().getSubTaskWrapper().size()) {
ALog.e(TAG, "修改子任务的文件名失败:子任务文件名列表数量和子任务的数量不匹配");
return this;
}
mSubNameTemp.clear();
mSubNameTemp.addAll(subTaskFileName);
return this;
return mGroupDelegate.setSubFileName(subTaskFileName);
}
@Override public int getTargetType() {
@ -170,161 +149,35 @@ public class DownloadGroupTarget extends AbsDGTarget<DownloadGroupTarget> implem
}
@Override protected boolean checkEntity() {
if (getTargetType() == GROUP_HTTP) {
if (!checkDirPath()) {
return false;
}
if (!checkSubName()) {
return false;
}
if (!checkUrls()) {
return false;
}
if (getTaskWrapper().getEntity().getFileSize() == 0) {
ALog.e(TAG, "组合任务必须设置文件文件大小");
return false;
}
if (getTaskWrapper().asHttp().getRequestEnum() == RequestEnum.POST) {
for (DTaskWrapper subTask : getTaskWrapper().getSubTaskWrapper()) {
subTask.asHttp().setRequestEnum(RequestEnum.POST);
}
}
getEntity().save();
if (needModifyPath) {
reChangeDirPath(mDirPathTemp);
}
if (!mSubNameTemp.isEmpty()) {
updateSingleSubFileName();
}
return true;
}
return false;
}
/**
* 更新所有改动的子任务文件名
*/
private void updateSingleSubFileName() {
List<DTaskWrapper> entities = getTaskWrapper().getSubTaskWrapper();
int i = 0;
for (DTaskWrapper entity : entities) {
if (i < mSubNameTemp.size()) {
String newName = mSubNameTemp.get(i);
updateSingleSubFileName(entity, newName);
}
i++;
}
}
/**
* 检查urls是否合法并删除不合法的子任务
*
* @return {@code true} 合法
*/
private boolean checkUrls() {
if (mUrls.isEmpty()) {
ALog.e(TAG, "下载失败,子任务下载列表为null");
return false;
}
Set<Integer> delItem = new HashSet<>();
int i = 0;
for (String url : mUrls) {
if (TextUtils.isEmpty(url)) {
ALog.e(TAG, "子任务url为null,即将删除该子任务。");
delItem.add(i);
continue;
} else if (!url.startsWith("http")) {
//} else if (!url.startsWith("http") && !url.startsWith("ftp")) {
ALog.e(TAG, "子任务url【" + url + "】错误,即将删除该子任务。");
delItem.add(i);
continue;
}
int index = url.indexOf("://");
if (index == -1) {
ALog.e(TAG, "子任务url【" + url + "】不合法,即将删除该子任务。");
delItem.add(i);
continue;
}
i++;
}
for (int index : delItem) {
mUrls.remove(index);
if (mSubNameTemp != null && !mSubNameTemp.isEmpty()) {
mSubNameTemp.remove(index);
}
}
getEntity().setGroupHash(CommonUtil.getMd5Code(mUrls));
return true;
return mGroupDelegate.checkEntity();
}
/**
* 更新单个子任务文件名
*/
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();
}
@Override public boolean isRunning() {
return mGroupDelegate.isRunning();
}
/**
* 如果用户设置了子任务文件名检查子任务文件名
*
* @return {@code true} 合法
*/
private boolean checkSubName() {
if (mSubNameTemp == null || mSubNameTemp.isEmpty()) {
return true;
}
if (mUrls.size() != mSubNameTemp.size()) {
ALog.e(TAG, "子任务文件名必须和子任务数量一致");
return false;
}
return true;
@Override public boolean taskExists() {
return mGroupDelegate.taskExists();
}
@CheckResult
@Override public DownloadGroupTarget addHeader(@NonNull String key, @NonNull String value) {
for (DTaskWrapper subTask : getTaskWrapper().getSubTaskWrapper()) {
mDelegate.addHeader(subTask, key, value);
mHeaderDelegate.addHeader(subTask, key, value);
}
return mDelegate.addHeader(key, value);
return mHeaderDelegate.addHeader(key, value);
}
@CheckResult
@Override public DownloadGroupTarget addHeaders(Map<String, String> headers) {
for (DTaskWrapper subTask : getTaskWrapper().getSubTaskWrapper()) {
mDelegate.addHeaders(subTask, headers);
mHeaderDelegate.addHeaders(subTask, headers);
}
return mDelegate.addHeaders(headers);
return mHeaderDelegate.addHeaders(headers);
}
@CheckResult
@Override public DownloadGroupTarget setUrlProxy(Proxy proxy) {
return mDelegate.setUrlProxy(proxy);
return mHeaderDelegate.setUrlProxy(proxy);
}
}

@ -28,7 +28,7 @@ import java.util.Map;
* Created by lyy on 2016/12/5.
* https://github.com/AriaLyy/Aria
*/
public class DownloadTarget extends AbsDownloadTarget<DownloadTarget>
public class DownloadTarget extends AbsDTarget<DownloadTarget>
implements IHttpHeaderDelegate<DownloadTarget> {
private HttpHeaderDelegate<DownloadTarget> mHeaderDelegate;
private DNormalDelegate<DownloadTarget> mNormalDelegate;

@ -0,0 +1,91 @@
/*
* 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.core.download;
import android.text.TextUtils;
import com.arialyy.aria.core.FtpUrlEntity;
import com.arialyy.aria.core.inf.AbsTaskWrapper;
import com.arialyy.aria.util.ALog;
/**
* Created by lyy on 2017/4/9.
* ftp文件夹下载功能代理
*/
class FtpDirDelegate extends AbsGroupDelegate<FtpDirDownloadTarget> {
FtpDirDelegate(FtpDirDownloadTarget target, DGTaskWrapper wrapper) {
super(target, wrapper);
wrapper.setRequestType(AbsTaskWrapper.D_FTP_DIR);
}
@Override public boolean checkEntity() {
boolean b = checkDirPath() && checkUrl();
if (b) {
getEntity().save();
if (getTaskWrapper().getSubTaskWrapper() != null) {
//初始化子项的登录信息
FtpUrlEntity tUrlEntity = getTaskWrapper().asFtp().getUrlEntity();
for (DTaskWrapper wrapper : getTaskWrapper().getSubTaskWrapper()) {
FtpUrlEntity urlEntity = wrapper.asFtp().getUrlEntity();
urlEntity.needLogin = tUrlEntity.needLogin;
urlEntity.account = tUrlEntity.account;
urlEntity.user = tUrlEntity.user;
urlEntity.password = tUrlEntity.password;
// 处理ftps详细
if (tUrlEntity.isFtps) {
urlEntity.isFtps = true;
urlEntity.protocol = tUrlEntity.protocol;
urlEntity.storePath = tUrlEntity.storePath;
urlEntity.storePass = tUrlEntity.storePass;
urlEntity.keyAlias = tUrlEntity.keyAlias;
}
}
}
}
if (getTaskWrapper().asFtp().getUrlEntity().isFtps) {
if (TextUtils.isEmpty(getTaskWrapper().asFtp().getUrlEntity().storePath)) {
ALog.e(TAG, "证书路径为空");
return false;
}
if (TextUtils.isEmpty(getTaskWrapper().asFtp().getUrlEntity().keyAlias)) {
ALog.e(TAG, "证书别名为空");
return false;
}
}
return b;
}
/**
* 检查普通任务的下载地址
*
* @return {@code true}地址合法
*/
private boolean checkUrl() {
final String url = getGroupHash();
if (TextUtils.isEmpty(url)) {
ALog.e(TAG, "下载失败,url为null");
return false;
} else if (!url.startsWith("ftp")) {
ALog.e(TAG, "下载失败,url【" + url + "】错误");
return false;
}
int index = url.indexOf("://");
if (index == -1) {
ALog.e(TAG, "下载失败,url【" + url + "】不合法");
return false;
}
return true;
}
}

@ -16,14 +16,10 @@
package com.arialyy.aria.core.download;
import android.support.annotation.CheckResult;
import android.text.TextUtils;
import com.arialyy.aria.core.FtpUrlEntity;
import com.arialyy.aria.core.common.ftp.FTPSDelegate;
import com.arialyy.aria.core.common.ftp.FtpDelegate;
import com.arialyy.aria.core.inf.AbsTaskWrapper;
import com.arialyy.aria.core.inf.IFtpTarget;
import com.arialyy.aria.core.manager.TaskWrapperManager;
import com.arialyy.aria.util.ALog;
import java.net.Proxy;
/**
@ -32,7 +28,8 @@ import java.net.Proxy;
*/
public class FtpDirDownloadTarget extends AbsDGTarget<FtpDirDownloadTarget>
implements IFtpTarget<FtpDirDownloadTarget> {
private FtpDelegate<FtpDirDownloadTarget> mDelegate;
private FtpDelegate<FtpDirDownloadTarget> mFtpDelegate;
private FtpDirDelegate mDirDelegate;
FtpDirDownloadTarget(String url, String targetName) {
setTargetName(targetName);
@ -40,13 +37,9 @@ public class FtpDirDownloadTarget extends AbsDGTarget<FtpDirDownloadTarget>
}
private void init(String key) {
mGroupHash = key;
setTaskWrapper(TaskWrapperManager.getInstance().getFtpTaskWrapper(DGTaskWrapper.class, key));
getTaskWrapper().setRequestType(AbsTaskWrapper.D_FTP_DIR);
if (getEntity() != null) {
mDirPathTemp = getEntity().getDirPath();
}
mDelegate = new FtpDelegate<>(this);
mDirDelegate = new FtpDirDelegate(this,
TaskWrapperManager.getInstance().getFtpTaskWrapper(DGTaskWrapper.class, key));
mFtpDelegate = new FtpDelegate<>(this);
}
@Override public int getTargetType() {
@ -54,62 +47,39 @@ public class FtpDirDownloadTarget extends AbsDGTarget<FtpDirDownloadTarget>
}
@Override protected boolean checkEntity() {
boolean b = getTargetType() == GROUP_FTP_DIR && checkDirPath() && checkUrl();
if (b) {
getEntity().save();
if (getTaskWrapper().getSubTaskWrapper() != null) {
//初始化子项的登录信息
FtpUrlEntity tUrlEntity = getTaskWrapper().asFtp().getUrlEntity();
for (DTaskWrapper wrapper : getTaskWrapper().getSubTaskWrapper()) {
FtpUrlEntity urlEntity = wrapper.asFtp().getUrlEntity();
urlEntity.needLogin = tUrlEntity.needLogin;
urlEntity.account = tUrlEntity.account;
urlEntity.user = tUrlEntity.user;
urlEntity.password = tUrlEntity.password;
// 处理ftps详细
if (tUrlEntity.isFtps) {
urlEntity.isFtps = true;
urlEntity.protocol = tUrlEntity.protocol;
urlEntity.storePath = tUrlEntity.storePath;
urlEntity.storePass = tUrlEntity.storePass;
urlEntity.keyAlias = tUrlEntity.keyAlias;
}
}
}
}
if (getTaskWrapper().asFtp().getUrlEntity().isFtps) {
if (TextUtils.isEmpty(getTaskWrapper().asFtp().getUrlEntity().storePath)) {
ALog.e(TAG, "证书路径为空");
return false;
}
if (TextUtils.isEmpty(getTaskWrapper().asFtp().getUrlEntity().keyAlias)) {
ALog.e(TAG, "证书别名为空");
return false;
}
}
return b;
return mDirDelegate.checkEntity();
}
@Override public boolean isRunning() {
return mDirDelegate.isRunning();
}
@Override public boolean taskExists() {
return mDirDelegate.taskExists();
}
/**
* 检查普通任务的下载地址
* 设置任务组的文件夹路径在Aria中任务组的所有子任务都会下载到以任务组组名的文件夹中
* groupDirPath = "/mnt/sdcard/download/group_test"
* <pre>
* {@code
* + mnt
* + sdcard
* + download
* + group_test
* - task1.apk
* - task2.apk
* - task3.apk
* ....
*
* }
* </pre>
*
* @return {@code true}地址合法
* @param dirPath 任务组保存文件夹路径
*/
private boolean checkUrl() {
final String url = mGroupHash;
if (TextUtils.isEmpty(url)) {
ALog.e(TAG, "下载失败,url为null");
return false;
} else if (!url.startsWith("ftp")) {
ALog.e(TAG, "下载失败,url【" + url + "】错误");
return false;
}
int index = url.indexOf("://");
if (index == -1) {
ALog.e(TAG, "下载失败,url【" + url + "】不合法");
return false;
}
return true;
@CheckResult
public FtpDirDownloadTarget setDirPath(String dirPath) {
return mDirDelegate.setDirPath(dirPath);
}
/**
@ -125,20 +95,20 @@ public class FtpDirDownloadTarget extends AbsDGTarget<FtpDirDownloadTarget>
@CheckResult
@Override public FtpDirDownloadTarget charSet(String charSet) {
return mDelegate.charSet(charSet);
return mFtpDelegate.charSet(charSet);
}
@CheckResult
@Override public FtpDirDownloadTarget login(String userName, String password) {
return mDelegate.login(userName, password);
return mFtpDelegate.login(userName, password);
}
@CheckResult
@Override public FtpDirDownloadTarget login(String userName, String password, String account) {
return mDelegate.login(userName, password, account);
return mFtpDelegate.login(userName, password, account);
}
@Override public FtpDirDownloadTarget setProxy(Proxy proxy) {
return mDelegate.setProxy(proxy);
return mFtpDelegate.setProxy(proxy);
}
}

@ -30,7 +30,7 @@ import java.net.Proxy;
* Created by lyy on 2016/12/5.
* https://github.com/AriaLyy/Aria
*/
public class FtpDownloadTarget extends AbsDownloadTarget<FtpDownloadTarget>
public class FtpDownloadTarget extends AbsDTarget<FtpDownloadTarget>
implements IFtpTarget<FtpDownloadTarget> {
private FtpDelegate<FtpDownloadTarget> mFtpDelegate;
private DNormalDelegate<FtpDownloadTarget> mNormalDelegate;

@ -0,0 +1,238 @@
/*
* 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.core.download;
import android.support.annotation.CheckResult;
import android.text.TextUtils;
import com.arialyy.aria.core.common.RequestEnum;
import com.arialyy.aria.orm.DbEntity;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Created by lyy on 2019/4/9.
*
* http组合任务功能代理
*/
class HttpGroupDelegate extends AbsGroupDelegate<DownloadGroupTarget> {
/**
* 子任务下载地址
*/
private List<String> mUrls = new ArrayList<>();
/**
* 子任务文件名
*/
private List<String> mSubNameTemp = new ArrayList<>();
HttpGroupDelegate(DownloadGroupTarget target, DGTaskWrapper wrapper) {
super(target, wrapper);
mUrls.addAll(wrapper.getEntity().getUrls());
}
@CheckResult
DownloadGroupTarget setGroupUrl(List<String> urls) {
mUrls.clear();
mUrls.addAll(urls);
return getTarget();
}
/**
* 设置子任务文件名该方法必须在{@link #setDirPath(String)}之后调用否则不生效
*/
@CheckResult
DownloadGroupTarget setSubFileName(List<String> subTaskFileName) {
if (subTaskFileName == null || subTaskFileName.isEmpty()) {
ALog.e(TAG, "修改子任务的文件名失败:列表为null");
return getTarget();
}
if (subTaskFileName.size() != getTaskWrapper().getSubTaskWrapper().size()) {
ALog.e(TAG, "修改子任务的文件名失败:子任务文件名列表数量和子任务的数量不匹配");
return getTarget();
}
mSubNameTemp.clear();
mSubNameTemp.addAll(subTaskFileName);
return getTarget();
}
/**
* 更新组合任务下载地址
*
* @param urls 新的组合任务下载地址列表
*/
@CheckResult
DownloadGroupTarget updateUrls(List<String> urls) {
if (urls == null || urls.isEmpty()) {
throw new NullPointerException("下载地址列表为空");
}
if (urls.size() != mUrls.size()) {
throw new IllegalArgumentException("新下载地址数量和旧下载地址数量不一致");
}
mUrls.clear();
mUrls.addAll(urls);
String newHash = CommonUtil.getMd5Code(urls);
setGroupHash(newHash);
getEntity().setGroupHash(newHash);
getEntity().update();
if (getEntity().getSubEntities() != null && !getEntity().getSubEntities().isEmpty()) {
for (DownloadEntity de : getEntity().getSubEntities()) {
de.setGroupHash(newHash);
de.update();
}
}
return getTarget();
}
@Override public boolean checkEntity() {
if (!checkDirPath()) {
return false;
}
if (!checkSubName()) {
return false;
}
if (!checkUrls()) {
return false;
}
if (getTaskWrapper().getEntity().getFileSize() == 0) {
ALog.e(TAG, "组合任务必须设置文件文件大小");
return false;
}
if (getTaskWrapper().asHttp().getRequestEnum() == RequestEnum.POST) {
for (DTaskWrapper subTask : getTaskWrapper().getSubTaskWrapper()) {
subTask.asHttp().setRequestEnum(RequestEnum.POST);
}
}
getEntity().save();
if (isNeedModifyPath()) {
reChangeDirPath(getDirPathTemp());
}
if (!mSubNameTemp.isEmpty()) {
updateSingleSubFileName();
}
return true;
}
/**
* 更新所有改动的子任务文件名
*/
private void updateSingleSubFileName() {
List<DTaskWrapper> entities = getTaskWrapper().getSubTaskWrapper();
int i = 0;
for (DTaskWrapper entity : entities) {
if (i < mSubNameTemp.size()) {
String newName = mSubNameTemp.get(i);
updateSingleSubFileName(entity, newName);
}
i++;
}
}
/**
* 检查urls是否合法并删除不合法的子任务
*
* @return {@code true} 合法
*/
private boolean checkUrls() {
if (mUrls.isEmpty()) {
ALog.e(TAG, "下载失败,子任务下载列表为null");
return false;
}
Set<Integer> delItem = new HashSet<>();
int i = 0;
for (String url : mUrls) {
if (TextUtils.isEmpty(url)) {
ALog.e(TAG, "子任务url为null,即将删除该子任务。");
delItem.add(i);
continue;
} else if (!url.startsWith("http")) {
//} else if (!url.startsWith("http") && !url.startsWith("ftp")) {
ALog.e(TAG, "子任务url【" + url + "】错误,即将删除该子任务。");
delItem.add(i);
continue;
}
int index = url.indexOf("://");
if (index == -1) {
ALog.e(TAG, "子任务url【" + url + "】不合法,即将删除该子任务。");
delItem.add(i);
continue;
}
i++;
}
for (int index : delItem) {
mUrls.remove(index);
if (mSubNameTemp != null && !mSubNameTemp.isEmpty()) {
mSubNameTemp.remove(index);
}
}
getEntity().setGroupHash(CommonUtil.getMd5Code(mUrls));
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();
}
}
/**
* 如果用户设置了子任务文件名检查子任务文件名
*
* @return {@code true} 合法
*/
private boolean checkSubName() {
if (mSubNameTemp == null || mSubNameTemp.isEmpty()) {
return true;
}
if (mUrls.size() != mSubNameTemp.size()) {
ALog.e(TAG, "子任务文件名必须和子任务数量一致");
return false;
}
return true;
}
}

@ -0,0 +1,58 @@
/*
* 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.core.inf;
/**
* Created by lyy on 2019/4/5.
* 组合任务接收器功能接口
*/
public interface IGroupTarget {
/**
* 获取实体
*/
AbsEntity getEntity();
/**
* 任务是否存在
*
* @return {@code true}任务存在{@code false} 任务不存在
*/
boolean taskExists();
/**
* 任务是否在执行
*
* @return {@code true} 任务正在执行{@code false} 任务没有执行
*/
boolean isRunning();
/**
* 检查实体是否合法
*
* @return {@code true}合法
*/
boolean checkEntity();
/**
* 检查文件夹路径
* 1文件夹路径不能为空
* 2文件夹路径不能是文件
*
* @return {@code true} 合法
*/
boolean checkDirPath();
}

@ -19,22 +19,7 @@ package com.arialyy.aria.core.inf;
* Created by lyy on 2019/4/5.
* 普通任务接收器功能接口
*/
public interface ITargetNormal<TARGET extends AbsTarget> {
/**
* 通过地址初始化target
*
* @param url 下载url上传url
* @param targetName 接收器名称
*/
void initTarget(String url, String targetName);
/**
* 更新下载url
*
* @param newUrl 新的下载url
*/
TARGET updateUrl(String newUrl);
public interface INormalTarget {
/**
* 获取实体

@ -17,7 +17,7 @@ package com.arialyy.aria.core.upload;
import android.text.TextUtils;
import com.arialyy.aria.core.inf.AbsEntity;
import com.arialyy.aria.core.inf.ITargetNormal;
import com.arialyy.aria.core.inf.INormalTarget;
import com.arialyy.aria.core.manager.TaskWrapperManager;
import com.arialyy.aria.core.queue.UploadTaskQueue;
import com.arialyy.aria.orm.DbEntity;
@ -29,7 +29,7 @@ import java.io.File;
* Created by Aria.Lao on 2019/4/5.
* 普通上传任务通用功能处理
*/
class UNormalDelegate<TARGET extends AbsUploadTarget> implements ITargetNormal<TARGET> {
class UNormalDelegate<TARGET extends AbsUploadTarget> implements INormalTarget {
private String TAG = "UNormalDelegate";
private UploadEntity mEntity;
private TARGET mTarget;
@ -43,7 +43,7 @@ class UNormalDelegate<TARGET extends AbsUploadTarget> implements ITargetNormal<T
initTarget(filePath, targetName);
}
@Override public void initTarget(String filePath, String targetName) {
private void initTarget(String filePath, String targetName) {
UTaskWrapper taskWrapper =
TaskWrapperManager.getInstance().getHttpTaskWrapper(UTaskWrapper.class, filePath);
mEntity = taskWrapper.getEntity();
@ -55,7 +55,7 @@ class UNormalDelegate<TARGET extends AbsUploadTarget> implements ITargetNormal<T
mTempUrl = mEntity.getUrl();
}
@Override public TARGET updateUrl(String newUrl) {
TARGET updateUrl(String newUrl) {
mTempUrl = newUrl;
return mTarget;
}

@ -20,13 +20,8 @@ import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.text.TextUtils;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.util.ALog;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -42,7 +37,7 @@ final class SqlHelper extends SQLiteOpenHelper {
private DelegateCommon mDelegate;
static SqlHelper init(Context context) {
synchronized static SqlHelper init(Context context) {
if (INSTANCE == null) {
synchronized (SqlHelper.class) {
DelegateCommon delegate = DelegateManager.getInstance().getDelegate(DelegateCommon.class);
@ -56,8 +51,7 @@ final class SqlHelper extends SQLiteOpenHelper {
db.execSQL("PRAGMA foreign_keys=ON;");
Set<String> tables = DBConfig.mapping.keySet();
for (String tableName : tables) {
Class clazz = null;
clazz = DBConfig.mapping.get(tableName);
Class clazz = DBConfig.mapping.get(tableName);
if (!delegate.tableExists(db, clazz)) {
delegate.createTable(db, clazz);

@ -7,8 +7,9 @@ Aria项目源于工作中遇到的一个文件下载管理的需求,当时被
Aria有以下特点:
+ 简单、方便
- 可以在Activity、Service、Fragment、Dialog、popupWindow、Notification等组件中使用
- 支持HTTP\FTP断点续传、多任务自动调度
- 支持HTTP任务组\FTP文件夹,断点续传下载
- 支持HTTP\FTP断点续传下载、多任务自动调度
- 支持多文件打包下载,多文件共享同一进度(如:视频 + 封面 + 字幕)
- 支持下载FTP文件夹
- 支持HTTP表单上传
- 支持文件FTP断点续传上传
- 支持FTPS/SFTP断点续传,[see](https://aria.laoyuyu.me/aria_doc/download/ftps.html)
@ -23,12 +24,17 @@ Aria有以下特点:
如果你觉得Aria对你有帮助,您的star和issues将是对我最大支持.`^_^`
## 示例
* 多任务下载
![多任务下载](https://github.com/AriaLyy/DownloadUtil/blob/master/img/download_img.gif)
* 速度限制
![网速下载限制](https://github.com/AriaLyy/DownloadUtil/blob/master/img/max_speed.gif)
![下载任务组](https://github.com/AriaLyy/DownloadUtil/blob/master/img/download_group.gif)
* 多文件打包下载
![多文件打包下载](https://github.com/AriaLyy/DownloadUtil/blob/master/img/group_task.gif)
## 下载
## 引入库
[![Core](https://api.bintray.com/packages/arialyy/maven/AriaApi/images/download.svg)](https://bintray.com/arialyy/maven/AriaApi/_latestVersion)
[![Compiler](https://api.bintray.com/packages/arialyy/maven/AriaCompiler/images/download.svg)](https://bintray.com/arialyy/maven/AriaCompiler/_latestVersion)

Binary file not shown.

After

Width:  |  Height:  |  Size: 809 KiB

Loading…
Cancel
Save