移除`getDownloadTask(String url)`、`getGroupTask(List<String>urls)`、`getFtpDirTask(String path)`等获取任务的api
修复使用`Content-Disposition`的文件名时,第一次下载无法重命名文件的问题
修复使用`Content-Disposition`的文件名时,多次重命名文件的问题
v3.6.6
laoyuyu 6 years ago
parent 0a447787ea
commit 8c0ef0f879
  1. 11
      Aria/src/main/java/com/arialyy/aria/core/common/AbsFileer.java
  2. 2
      Aria/src/main/java/com/arialyy/aria/core/common/AbsThreadTask.java
  3. 4
      Aria/src/main/java/com/arialyy/aria/core/download/DNormalDelegate.java
  4. 931
      Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java
  5. 101
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/ChunkedInputStream.java
  6. 42
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/ConnectionHelp.java
  7. 15
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/Downloader.java
  8. 58
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpFileInfoThread.java
  9. 69
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpThreadTask.java
  10. 2
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/SimpleDownloadUtil.java
  11. 4
      Aria/src/main/java/com/arialyy/aria/core/manager/DGTaskWrapperFactory.java
  12. 33
      Aria/src/main/java/com/arialyy/aria/core/manager/DTaskWrapperFactory.java
  13. 2
      Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java
  14. 61
      Aria/src/main/java/com/arialyy/aria/orm/AbsDelegate.java
  15. 1
      Aria/src/main/java/com/arialyy/aria/orm/DBConfig.java
  16. 8
      Aria/src/main/java/com/arialyy/aria/orm/DelegateCommon.java
  17. 1271
      Aria/src/main/java/com/arialyy/aria/orm/DelegateFind.java
  18. 4
      Aria/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java
  19. 2
      Aria/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java
  20. 557
      Aria/src/main/java/com/arialyy/aria/orm/SqlHelper.java
  21. 521
      Aria/src/main/java/com/arialyy/aria/util/CheckUtil.java
  22. 18
      Aria/src/main/java/com/arialyy/aria/util/DbDataHelper.java
  23. 443
      DEV_LOG.md
  24. 344
      README.md
  25. 320
      app/src/main/assets/aria_config.xml
  26. 37
      app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java
  27. 18
      app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java
  28. 17
      app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java
  29. 2
      app/src/main/java/com/arialyy/simple/core/test/AnyRunActivity.java
  30. 116
      build.gradle

@ -141,10 +141,11 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER ext
} }
resetState(); resetState();
mConstance.resetState(); mConstance.resetState();
ALog.d(TAG, "path: " + getFilePath());
checkRecord(); checkRecord();
mConstance.isRunning = true; mConstance.isRunning = true;
mConstance.TASK_RECORD = mRecord; mConstance.TASK_RECORD = mRecord;
if (!mTaskWrapper.isSupportBP()) { if (!mTaskWrapper.isSupportBP() || mTaskWrapper.asHttp().isChunked()) {
mTotalThreadNum = 1; mTotalThreadNum = 1;
} else { } else {
mTotalThreadNum = mTotalThreadNum =
@ -348,6 +349,7 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER ext
if (mRecord.isOpenDynamicFile) { if (mRecord.isOpenDynamicFile) {
ThreadRecord tr = mRecord.threadRecords.get(0); ThreadRecord tr = mRecord.threadRecords.get(0);
if (tr == null) { if (tr == null) {
ALog.d(TAG, "线程记录为空,任务为新任务");
mTaskWrapper.setNewTask(true); mTaskWrapper.setNewTask(true);
mTotalThreadNum = 1; mTotalThreadNum = 1;
return; return;
@ -438,6 +440,7 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER ext
if (records == null || records.size() == 0) { if (records == null || records.size() == 0) {
Properties pro = CommonUtil.loadConfig(mConfigFile); Properties pro = CommonUtil.loadConfig(mConfigFile);
if (pro.isEmpty()) { if (pro.isEmpty()) {
ALog.d(TAG, "老版本的线程记录为空,任务为新任务");
mTaskWrapper.setNewTask(true); mTaskWrapper.setNewTask(true);
return; return;
} }
@ -453,6 +456,7 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER ext
} }
int threadNum = set.size(); int threadNum = set.size();
if (threadNum == 0) { if (threadNum == 0) {
ALog.d(TAG, "线程数为空,任务为新任务");
mTaskWrapper.setNewTask(true); mTaskWrapper.setNewTask(true);
return; return;
} }
@ -495,6 +499,7 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER ext
mRecord.dGroupHash = ((DownloadEntity) mTaskWrapper.getEntity()).getGroupHash(); mRecord.dGroupHash = ((DownloadEntity) mTaskWrapper.getEntity()).getGroupHash();
} }
} }
ALog.d(TAG, String.format("初始化记录,任务为%s任务", isNewTask ? "新" : "旧"));
mTaskWrapper.setNewTask(isNewTask); mTaskWrapper.setNewTask(isNewTask);
} }
@ -569,6 +574,10 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER ext
long fileLength = mEntity.getFileSize(); long fileLength = mEntity.getFileSize();
long blockSize = fileLength / mTotalThreadNum; long blockSize = fileLength / mTotalThreadNum;
Set<Integer> threads = new HashSet<>(); Set<Integer> threads = new HashSet<>();
// 如果是新任务,检查下历史记录,如果有遗留的记录,删除并删除分块文件
if (mTaskWrapper.isNewTask()) {
CommonUtil.delTaskRecord(getFilePath(), 1, true);
}
mRecord.fileLength = fileLength; mRecord.fileLength = fileLength;
if (mTaskWrapper.isNewTask() && !handleNewTask()) { if (mTaskWrapper.isNewTask() && !handleNewTask()) {

@ -316,7 +316,7 @@ public abstract class AbsThreadTask<ENTITY extends AbsNormalEntity, TASK_WRAPPER
*/ */
protected void progress(long len) { protected void progress(long len) {
synchronized (AriaManager.LOCK) { synchronized (AriaManager.LOCK) {
if (STATE.CURRENT_LOCATION > mEntity.getFileSize()) { if (STATE.CURRENT_LOCATION > mEntity.getFileSize() && !mTaskWrapper.asHttp().isChunked()) {
String errorMsg = String errorMsg =
String.format("下载失败,下载长度超出文件真实长度;currentLocation=%s, fileSize=%s", String.format("下载失败,下载长度超出文件真实长度;currentLocation=%s, fileSize=%s",
STATE.CURRENT_LOCATION, STATE.CURRENT_LOCATION,

@ -145,6 +145,10 @@ class DNormalDelegate<TARGET extends AbsDTarget> implements INormalTarget {
File newFile = new File(filePath); File newFile = new File(filePath);
mEntity.setDownloadPath(filePath); mEntity.setDownloadPath(filePath);
mEntity.setFileName(newFile.getName()); mEntity.setFileName(newFile.getName());
// 如过使用Content-Disposition中的文件名,将不会执行重命名工作
if (mTarget.getTaskWrapper().asHttp().isUseServerFileName()) {
return true;
}
if (oldFile.exists()) { if (oldFile.exists()) {
// 处理普通任务的重命名 // 处理普通任务的重命名
CommonUtil.modifyTaskRecord(oldFile.getPath(), newFile.getPath()); CommonUtil.modifyTaskRecord(oldFile.getPath(), newFile.getPath());

@ -1,475 +1,458 @@
/* /*
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.arialyy.aria.core.download; package com.arialyy.aria.core.download;
import android.support.annotation.CheckResult; import android.support.annotation.CheckResult;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.text.TextUtils; import android.text.TextUtils;
import com.arialyy.aria.core.AriaManager; import com.arialyy.annotations.DownloadGroup;
import com.arialyy.aria.core.command.ICmd; import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.core.command.normal.CancelAllCmd; import com.arialyy.aria.core.command.ICmd;
import com.arialyy.aria.core.command.normal.NormalCmdFactory; import com.arialyy.aria.core.command.normal.CancelAllCmd;
import com.arialyy.aria.core.common.ProxyHelper; import com.arialyy.aria.core.command.normal.NormalCmdFactory;
import com.arialyy.aria.core.inf.AbsEntity; import com.arialyy.aria.core.common.ProxyHelper;
import com.arialyy.aria.core.inf.AbsReceiver; import com.arialyy.aria.core.inf.AbsEntity;
import com.arialyy.aria.core.inf.ReceiverType; import com.arialyy.aria.core.inf.AbsReceiver;
import com.arialyy.aria.core.manager.TaskWrapperManager; import com.arialyy.aria.core.inf.ReceiverType;
import com.arialyy.aria.core.scheduler.DownloadGroupSchedulers; import com.arialyy.aria.core.manager.TaskWrapperManager;
import com.arialyy.aria.core.scheduler.DownloadSchedulers; import com.arialyy.aria.core.scheduler.DownloadGroupSchedulers;
import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.core.scheduler.DownloadSchedulers;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.orm.DbEntity;
import com.arialyy.aria.util.CheckUtil; import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.CheckUtil;
import java.util.ArrayList; import com.arialyy.aria.util.CommonUtil;
import java.util.List; import com.arialyy.aria.util.DbDataHelper;
import java.util.Set; import java.util.ArrayList;
import java.util.List;
/** import java.util.Set;
* Created by lyy on 2016/12/5.
* 下载功能接收器 /**
*/ * Created by lyy on 2016/12/5.
public class DownloadReceiver extends AbsReceiver { * 下载功能接收器
private final String TAG = "DownloadReceiver"; */
public class DownloadReceiver extends AbsReceiver {
/** private final String TAG = "DownloadReceiver";
* 设置最大下载速度单位kb
* /**
* @param maxSpeed 为0表示不限速 * 设置最大下载速度单位kb
* @deprecated {@code Aria.get(Context).getDownloadConfig().setMaxSpeed(int)} *
*/ * @param maxSpeed 为0表示不限速
@Deprecated * @deprecated {@code Aria.get(Context).getDownloadConfig().setMaxSpeed(int)}
public DownloadReceiver setMaxSpeed(int maxSpeed) { */
AriaManager.getInstance(AriaManager.APP).getDownloadConfig().setMaxSpeed(maxSpeed); @Deprecated
return this; public DownloadReceiver setMaxSpeed(int maxSpeed) {
} AriaManager.getInstance(AriaManager.APP).getDownloadConfig().setMaxSpeed(maxSpeed);
return this;
/** }
* 使用下载实体执行下载操作
* /**
* @param entity 下载实体 * 使用下载实体执行下载操作
*/ *
@CheckResult * @param entity 下载实体
public DownloadTarget load(DownloadEntity entity) { */
CheckUtil.checkUrlInvalidThrow(entity.getUrl()); @CheckResult
return new DownloadTarget(entity, targetName); public DownloadTarget load(DownloadEntity entity) {
} CheckUtil.checkUrlInvalidThrow(entity.getUrl());
return new DownloadTarget(entity, targetName);
/** }
* 加载Httphttps单任务下载地址
* /**
* @param url 下载地址 * 加载Httphttps单任务下载地址
*/ *
@CheckResult * @param url 下载地址
public DownloadTarget load(@NonNull String url) { */
CheckUtil.checkUrlInvalidThrow(url); @CheckResult
return new DownloadTarget(url, targetName); public DownloadTarget load(@NonNull String url) {
} CheckUtil.checkUrlInvalidThrow(url);
return new DownloadTarget(url, targetName);
/** }
* 加载下载地址如果任务组的中的下载地址改变了则任务从新的一个任务组
* /**
* @param urls 任务组子任务下载地址列表 * 加载下载地址如果任务组的中的下载地址改变了则任务从新的一个任务组
* @deprecated {@link #loadGroup(DownloadGroupEntity)} *
*/ * @param urls 任务组子任务下载地址列表
@Deprecated * @deprecated {@link #loadGroup(DownloadGroupEntity)}
@CheckResult */
public DownloadGroupTarget load(List<String> urls) { @Deprecated
return loadGroup(urls); @CheckResult
} public DownloadGroupTarget load(List<String> urls) {
return loadGroup(urls);
/** }
* 加载下载地址如果任务组的中的下载地址改变了则任务从新的一个任务组
*/ /**
@CheckResult * 加载下载地址如果任务组的中的下载地址改变了则任务从新的一个任务组
public DownloadGroupTarget loadGroup(List<String> urls) { */
CheckUtil.checkDownloadUrls(urls); @CheckResult
return new DownloadGroupTarget(urls, targetName); public DownloadGroupTarget loadGroup(List<String> urls) {
} CheckUtil.checkDownloadUrls(urls);
return new DownloadGroupTarget(urls, targetName);
/** }
* 使用下载实体执行FTP下载操作
* /**
* @param entity 下载实体 * 使用下载实体执行FTP下载操作
*/ *
@CheckResult * @param entity 下载实体
public FtpDownloadTarget loadFtp(DownloadEntity entity) { */
CheckUtil.checkUrlInvalidThrow(entity.getUrl()); @CheckResult
return new FtpDownloadTarget(entity, targetName); public FtpDownloadTarget loadFtp(DownloadEntity entity) {
} CheckUtil.checkUrlInvalidThrow(entity.getUrl());
return new FtpDownloadTarget(entity, targetName);
/** }
* 加载ftp单任务下载地址
*/ /**
@CheckResult * 加载ftp单任务下载地址
public FtpDownloadTarget loadFtp(@NonNull String url) { */
CheckUtil.checkUrlInvalidThrow(url); @CheckResult
return new FtpDownloadTarget(url, targetName); public FtpDownloadTarget loadFtp(@NonNull String url) {
} CheckUtil.checkUrlInvalidThrow(url);
return new FtpDownloadTarget(url, targetName);
/** }
* 使用任务组实体执行任务组的实体执行任务组的下载操作后续版本会删除该api
* /**
* @param groupEntity 如果加载的任务实体没有子项的下载地址 * 使用任务组实体执行任务组的实体执行任务组的下载操作后续版本会删除该api
* 那么你需要使用{@link DownloadGroupTarget#setGroupUrl(List)}设置子项的下载地址 *
* @deprecated 请使用 {@link #loadGroup(DownloadGroupEntity)} * @param groupEntity 如果加载的任务实体没有子项的下载地址
*/ * 那么你需要使用{@link DownloadGroupTarget#setGroupUrl(List)}设置子项的下载地址
@Deprecated * @deprecated 请使用 {@link #loadGroup(DownloadGroupEntity)}
@CheckResult */
public DownloadGroupTarget load(DownloadGroupEntity groupEntity) { @Deprecated
CheckUtil.checkDownloadUrls(groupEntity.getUrls()); @CheckResult
return loadGroup(groupEntity); public DownloadGroupTarget load(DownloadGroupEntity groupEntity) {
} CheckUtil.checkDownloadUrls(groupEntity.getUrls());
return loadGroup(groupEntity);
/** }
* 使用任务组实体执行任务组的实体执行任务组的下载操作
* /**
* @param groupEntity 如果加载的任务实体没有子项的下载地址 * 使用任务组实体执行任务组的实体执行任务组的下载操作
* 那么你需要使用{@link DownloadGroupTarget#setGroupUrl(List)}设置子项的下载地址 *
*/ * @param groupEntity 如果加载的任务实体没有子项的下载地址
@CheckResult * 那么你需要使用{@link DownloadGroupTarget#setGroupUrl(List)}设置子项的下载地址
public DownloadGroupTarget loadGroup(DownloadGroupEntity groupEntity) { */
CheckUtil.checkDownloadUrls(groupEntity.getUrls()); @CheckResult
return new DownloadGroupTarget(groupEntity, targetName); public DownloadGroupTarget loadGroup(DownloadGroupEntity groupEntity) {
} CheckUtil.checkDownloadUrls(groupEntity.getUrls());
return new DownloadGroupTarget(groupEntity, targetName);
/** }
* 加载ftp文件夹下载地址
*/ /**
@CheckResult * 加载ftp文件夹下载地址
public FtpDirDownloadTarget loadFtpDir(@NonNull String dirUrl) { */
CheckUtil.checkUrlInvalidThrow(dirUrl); @CheckResult
return new FtpDirDownloadTarget(dirUrl, targetName); public FtpDirDownloadTarget loadFtpDir(@NonNull String dirUrl) {
} CheckUtil.checkUrlInvalidThrow(dirUrl);
return new FtpDirDownloadTarget(dirUrl, targetName);
/** }
* 将当前类注册到Aria
*/ /**
public void register() { * 将当前类注册到Aria
if (TextUtils.isEmpty(targetName)) { */
ALog.e(TAG, "download register target null"); public void register() {
return; if (TextUtils.isEmpty(targetName)) {
} ALog.e(TAG, "download register target null");
Object obj = OBJ_MAP.get(getKey()); return;
if (obj == null) { }
ALog.e(TAG, String.format("【%s】观察者为空", targetName)); Object obj = OBJ_MAP.get(getKey());
return; if (obj == null) {
} ALog.e(TAG, String.format("【%s】观察者为空", targetName));
Set<Integer> set = ProxyHelper.getInstance().checkProxyType(obj.getClass()); return;
if (set != null && !set.isEmpty()) { }
for (Integer type : set) { Set<Integer> set = ProxyHelper.getInstance().checkProxyType(obj.getClass());
if (type == ProxyHelper.PROXY_TYPE_DOWNLOAD) { if (set != null && !set.isEmpty()) {
DownloadSchedulers.getInstance().register(obj); for (Integer type : set) {
} else if (type == ProxyHelper.PROXY_TYPE_DOWNLOAD_GROUP) { if (type == ProxyHelper.PROXY_TYPE_DOWNLOAD) {
DownloadGroupSchedulers.getInstance().register(obj); DownloadSchedulers.getInstance().register(obj);
} } else if (type == ProxyHelper.PROXY_TYPE_DOWNLOAD_GROUP) {
} DownloadGroupSchedulers.getInstance().register(obj);
} else { }
ALog.w(TAG, "没有Aria的注解方法"); }
} } else {
} ALog.w(TAG, "没有Aria的注解方法");
}
/** }
* 取消注册如果是Activity或fragmentAria会界面销毁时自动调用该方法不需要你手动调用
* 注意事项 /**
* 1如果在activity中一定要调用该方法那么请在{@code onDestroy()}中调用 * 取消注册如果是Activity或fragmentAria会界面销毁时自动调用该方法不需要你手动调用
* 2不要在activity的{@code onStop()}中调用改方法 * 注意事项
* 3如果是Dialog或popupwindow需要你在撤销界面时调用该方法 * 1如果在activity中一定要调用该方法那么请在{@code onDestroy()}中调用
* 4如果你是在Module非android组件类中注册了Aria那么你也需要在Module类中调用该方法而不是在组件类中 * 2不要在activity的{@code onStop()}中调用改方法
* 调用销毁详情见 * 3如果是Dialog或popupwindow需要你在撤销界面时调用该方法
* * 4如果你是在Module非android组件类中注册了Aria那么你也需要在Module类中调用该方法而不是在组件类中
* @see <a href="https://aria.laoyuyu.me/aria_doc/start/any_java.html#%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9">module类中销毁</a> * 调用销毁详情见
*/ *
@Override public void unRegister() { * @see <a href="https://aria.laoyuyu.me/aria_doc/start/any_java.html#%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9">module类中销毁</a>
if (needRmListener) { */
unRegisterListener(); @Override public void unRegister() {
} if (needRmListener) {
AriaManager.getInstance(AriaManager.APP).removeReceiver(OBJ_MAP.get(getKey())); unRegisterListener();
} }
AriaManager.getInstance(AriaManager.APP).removeReceiver(OBJ_MAP.get(getKey()));
@Override public String getType() { }
return ReceiverType.DOWNLOAD;
} @Override public String getType() {
return ReceiverType.DOWNLOAD;
@Override protected void unRegisterListener() { }
if (TextUtils.isEmpty(targetName)) {
ALog.e(TAG, "download unRegisterListener target null"); @Override protected void unRegisterListener() {
return; if (TextUtils.isEmpty(targetName)) {
} ALog.e(TAG, "download unRegisterListener target null");
Object obj = OBJ_MAP.get(getKey()); return;
if (obj == null) { }
ALog.e(TAG, String.format("【%s】观察者为空", targetName)); Object obj = OBJ_MAP.get(getKey());
return; if (obj == null) {
} ALog.e(TAG, String.format("【%s】观察者为空", targetName));
Set<Integer> set = ProxyHelper.getInstance().mProxyCache.get(obj.getClass().getName()); return;
if (set != null) { }
for (Integer integer : set) { Set<Integer> set = ProxyHelper.getInstance().mProxyCache.get(obj.getClass().getName());
if (integer == ProxyHelper.PROXY_TYPE_DOWNLOAD) { if (set != null) {
DownloadSchedulers.getInstance().unRegister(obj); for (Integer integer : set) {
} else if (integer == ProxyHelper.PROXY_TYPE_DOWNLOAD_GROUP) { if (integer == ProxyHelper.PROXY_TYPE_DOWNLOAD) {
DownloadGroupSchedulers.getInstance().unRegister(obj); DownloadSchedulers.getInstance().unRegister(obj);
} } else if (integer == ProxyHelper.PROXY_TYPE_DOWNLOAD_GROUP) {
} DownloadGroupSchedulers.getInstance().unRegister(obj);
} }
} }
}
/** }
* 通过下载链接获取下载实体
* /**
* @return 如果url错误或查找不到数据则返回null * 通过下载链接获取下载实体
*/ *
public DownloadEntity getDownloadEntity(String downloadUrl) { * @return 如果url错误或查找不到数据则返回null
if (!CheckUtil.checkUrl(downloadUrl)) { */
return null; public DownloadEntity getDownloadEntity(String downloadUrl) {
} CheckUtil.checkUrl(downloadUrl);
return DbEntity.findFirst(DownloadEntity.class, "url=? and isGroupChild='false'", downloadUrl); return DbEntity.findFirst(DownloadEntity.class, "url=? and isGroupChild='false'", downloadUrl);
} }
/** /**
* 通过下载地址和文件保存路径获取下载任务实体 * 获取组合任务实在实体
* *
* @param downloadUrl 下载地址 * @param urls 组合任务的url
* @return 如果url错误或查找不到数据则返回null * @return 如果实体不存在返回null
*/ */
public DTaskWrapper getDownloadTask(String downloadUrl) { public DownloadGroupEntity getDownloadGroupEntity(List<String> urls) {
if (!CheckUtil.checkUrl(downloadUrl)) { CheckUtil.checkDownloadUrls(urls);
return null; return DbDataHelper.getDGEntity(CommonUtil.getMd5Code(urls));
} }
if (!taskExists(downloadUrl)) {
return null; /**
} * 获取组合任务实在实体
return TaskWrapperManager.getInstance().getHttpTaskWrapper(DTaskWrapper.class, downloadUrl); *
} * @param key 组合任务的key{@link DownloadGroupEntity#getKey()}
* @return 如果实体不存在返回null
/** */
* 通过下载链接获取保存在数据库的下载任务组实体 public DownloadGroupEntity getDownloadGroupEntity(String key) {
* if (TextUtils.isEmpty(key)) {
* @param urls 任务组子任务下载地址列表 throw new IllegalArgumentException("key为空");
* @return 返回对应的任务组实体如果查找不到对应的数据或子任务列表为null返回null }
*/ return DbDataHelper.getDGEntity(key);
public DGTaskWrapper getGroupTask(List<String> urls) { }
if (urls == null || urls.isEmpty()) {
ALog.e(TAG, "获取任务组实体失败:任务组子任务下载地址列表为null"); /**
return null; * 获取Ftp文件夹任务的实体
} *
if (!taskExists(urls)) { * @param url ftp文件夹下载路径
return null; * @return 如果实体不存在返回null
} */
return TaskWrapperManager.getInstance().getDGTaskWrapper(DGTaskWrapper.class, urls); public DownloadGroupEntity getFtpDirEntity(String url) {
} CheckUtil.checkUrl(url);
return DbDataHelper.getDGEntity(url);
/** }
* 获取FTP文件夹下载任务实体
* /**
* @param dirUrl FTP文件夹本地下载路径 * 下载任务是否存在
* @return 返回对应的任务组实体如果查找不到对应的数据或路径为null返回null *
*/ * @return {@code true}存在{@code false} 不存在
public DGTaskWrapper getFtpDirTask(String dirUrl) { */
if (TextUtils.isEmpty(dirUrl)) { public boolean taskExists(String downloadUrl) {
ALog.e(TAG, "获取FTP文件夹实体失败:下载路径为null"); return DbEntity.checkDataExist(DownloadEntity.class, "url=?", downloadUrl);
return null; }
}
boolean b = /**
DownloadGroupEntity.findFirst(DownloadGroupEntity.class, "groupHash=?", dirUrl) != null; * 判断任务组是否存在
if (!b) { *
return null; * @return {@code true} 存在{@code false} 不存在
} */
return TaskWrapperManager.getInstance().getFtpTaskWrapper(DGTaskWrapper.class, dirUrl); public boolean taskExists(List<String> urls) {
} if (urls == null || urls.isEmpty()) {
return false;
/** }
* 下载任务是否存在 String groupHash = CommonUtil.getMd5Code(urls);
* return DbEntity.checkDataExist(DownloadGroupEntity.class, "groupHash=?", groupHash);
* @return {@code true}存在{@code false} 不存在 }
*/
public boolean taskExists(String downloadUrl) { /**
return DbEntity.checkDataExist(DownloadEntity.class, "url=?", downloadUrl); * 获取所有普通下载任务
} * 获取未完成的普通任务列表{@link #getAllNotCompleteTask()}
* 获取已经完成的普通任务列表{@link #getAllCompleteTask()}
/** */
* 判断任务组是否存在 public List<DownloadEntity> getTaskList() {
* return DbEntity.findDatas(DownloadEntity.class, "isGroupChild=? and downloadPath!=''",
* @return {@code true} 存在{@code false} 不存在 "false");
*/ }
public boolean taskExists(List<String> urls) {
if (urls == null || urls.isEmpty()) { /**
return false; * 分页获取所有普通下载任务
} * 获取未完成的普通任务列表{@link #getAllNotCompleteTask()}
String groupHash = CommonUtil.getMd5Code(urls); * 获取已经完成的普通任务列表{@link #getAllCompleteTask()}
return DbEntity.checkDataExist(DownloadGroupEntity.class, "groupHash=?", groupHash); *
} * @param page 当前页不能小于1
* @param num 每页数量不能小于1
/** * @return 如果页数大于总页数返回null
* 获取所有普通下载任务 */
* 获取未完成的普通任务列表{@link #getAllNotCompleteTask()} public List<DownloadEntity> getTaskList(int page, int num) {
* 获取已经完成的普通任务列表{@link #getAllCompleteTask()} CheckUtil.checkPageParams(page, num);
*/ return DbEntity.findDatas(DownloadEntity.class, page, num,
public List<DownloadEntity> getTaskList() { "isGroupChild=? and downloadPath!=''", "false");
return DbEntity.findDatas(DownloadEntity.class, "isGroupChild=? and downloadPath!=''", }
"false");
} /**
* 获取所有未完成的普通下载任务
/** */
* 分页获取所有普通下载任务 public List<DownloadEntity> getAllNotCompleteTask() {
* 获取未完成的普通任务列表{@link #getAllNotCompleteTask()} return DbEntity.findDatas(DownloadEntity.class,
* 获取已经完成的普通任务列表{@link #getAllCompleteTask()} "isGroupChild=? and downloadPath!='' and isComplete=?", "false", "false");
* }
* @param page 当前页不能小于1
* @param num 每页数量不能小于1 /**
* @return 如果页数大于总页数返回null * 分页获取所有未完成的普通下载任务
*/ *
public List<DownloadEntity> getTaskList(int page, int num) { * @param page 当前页不能小于1
CheckUtil.checkPageParams(page, num); * @param num 每页数量不能小于1
return DbEntity.findDatas(DownloadEntity.class, page, num, * @return 如果页数大于总页数返回null
"isGroupChild=? and downloadPath!=''", "false"); */
} public List<DownloadEntity> getAllNotCompleteTask(int page, int num) {
CheckUtil.checkPageParams(page, num);
/** return DbEntity.findDatas(DownloadEntity.class, page, num,
* 获取所有未完成的普通下载任务 "isGroupChild=? and downloadPath!='' and isComplete=?", "false", "false");
*/ }
public List<DownloadEntity> getAllNotCompleteTask() {
return DbEntity.findDatas(DownloadEntity.class, /**
"isGroupChild=? and downloadPath!='' and isComplete=?", "false", "false"); * 获取所有已经完成的普通任务
} */
public List<DownloadEntity> getAllCompleteTask() {
/** return DbEntity.findDatas(DownloadEntity.class,
* 分页获取所有未完成的普通下载任务 "isGroupChild=? and downloadPath!='' and isComplete=?", "false", "true");
* }
* @param page 当前页不能小于1
* @param num 每页数量不能小于1 /**
* @return 如果页数大于总页数返回null * 分页获取所有已经完成的普通任务
*/ *
public List<DownloadEntity> getAllNotCompleteTask(int page, int num) { * @param page 当前页不能小于1
CheckUtil.checkPageParams(page, num); * @param num 每页数量不能小于1
return DbEntity.findDatas(DownloadEntity.class, page, num, * @return 如果页数大于总页数返回null
"isGroupChild=? and downloadPath!='' and isComplete=?", "false", "false"); */
} public List<DownloadEntity> getAllCompleteTask(int page, int num) {
CheckUtil.checkPageParams(page, num);
/** return DbEntity.findDatas(DownloadEntity.class,
* 获取所有已经完成的普通任务 "isGroupChild=? and downloadPath!='' and isComplete=?", "false", "true");
*/ }
public List<DownloadEntity> getAllCompleteTask() {
return DbEntity.findDatas(DownloadEntity.class, /**
"isGroupChild=? and downloadPath!='' and isComplete=?", "false", "true"); * 获取任务组列表
} *
* @return 如果没有任务组列表则返回null
/** */
* 分页获取所有已经完成的普通任务 public List<DownloadGroupEntity> getGroupTaskList() {
* List<DGEntityWrapper> wrappers = DbEntity.findRelationData(DGEntityWrapper.class);
* @param page 当前页不能小于1 if (wrappers == null || wrappers.isEmpty()) {
* @param num 每页数量不能小于1 return null;
* @return 如果页数大于总页数返回null }
*/ List<DownloadGroupEntity> entities = new ArrayList<>();
public List<DownloadEntity> getAllCompleteTask(int page, int num) { for (DGEntityWrapper wrapper : wrappers) {
CheckUtil.checkPageParams(page, num); entities.add(wrapper.groupEntity);
return DbEntity.findDatas(DownloadEntity.class, }
"isGroupChild=? and downloadPath!='' and isComplete=?", "false", "true"); return entities;
} }
/** /**
* 获取任务组列表 * 分页获取祝贺任务列表
* *
* @return 如果没有任务组列表则返回null * @param page 当前页不能小于1
*/ * @param num 每页数量不能小于1
public List<DownloadGroupEntity> getGroupTaskList() { * @return 如果没有任务组列表则返回null
List<DGEntityWrapper> wrappers = DbEntity.findRelationData(DGEntityWrapper.class); */
if (wrappers == null || wrappers.isEmpty()) { public List<DownloadGroupEntity> getGroupTaskList(int page, int num) {
return null; List<DGEntityWrapper> wrappers = DbEntity.findRelationData(DGEntityWrapper.class, page, num);
} if (wrappers == null || wrappers.isEmpty()) {
List<DownloadGroupEntity> entities = new ArrayList<>(); return null;
for (DGEntityWrapper wrapper : wrappers) { }
entities.add(wrapper.groupEntity); List<DownloadGroupEntity> entities = new ArrayList<>();
} for (DGEntityWrapper wrapper : wrappers) {
return entities; entities.add(wrapper.groupEntity);
} }
return entities;
/** }
* 分页获取祝贺任务列表
* /**
* @param page 当前页不能小于1 * 获取普通任务和任务组的任务列表
* @param num 每页数量不能小于1 */
* @return 如果没有任务组列表则返回null public List<AbsEntity> getTotalTaskList() {
*/ List<AbsEntity> list = new ArrayList<>();
public List<DownloadGroupEntity> getGroupTaskList(int page, int num) { List<DownloadEntity> simpleTask = getTaskList();
List<DGEntityWrapper> wrappers = DbEntity.findRelationData(DGEntityWrapper.class, page, num); List<DownloadGroupEntity> groupTask = getGroupTaskList();
if (wrappers == null || wrappers.isEmpty()) { if (simpleTask != null && !simpleTask.isEmpty()) {
return null; list.addAll(simpleTask);
} }
List<DownloadGroupEntity> entities = new ArrayList<>(); if (groupTask != null && !groupTask.isEmpty()) {
for (DGEntityWrapper wrapper : wrappers) { list.addAll(groupTask);
entities.add(wrapper.groupEntity); }
} return list;
return entities; }
}
/**
/** * 停止所有正在下载的任务并清空等待队列
* 获取普通任务和任务组的任务列表 */
*/ public void stopAllTask() {
public List<AbsEntity> getTotalTaskList() { AriaManager.getInstance(AriaManager.APP)
List<AbsEntity> list = new ArrayList<>(); .setCmd(NormalCmdFactory.getInstance()
List<DownloadEntity> simpleTask = getTaskList(); .createCmd(new DTaskWrapper(null), NormalCmdFactory.TASK_STOP_ALL,
List<DownloadGroupEntity> groupTask = getGroupTaskList(); ICmd.TASK_TYPE_DOWNLOAD))
if (simpleTask != null && !simpleTask.isEmpty()) { .exe();
list.addAll(simpleTask); }
}
if (groupTask != null && !groupTask.isEmpty()) { /**
list.addAll(groupTask); * 恢复所有正在下载的任务
} * 1.如果执行队列没有满则开始下载任务直到执行队列满
return list; * 2.如果队列执行队列已经满了则将所有任务添加到等待队列中
} */
public void resumeAllTask() {
/** AriaManager.getInstance(AriaManager.APP)
* 停止所有正在下载的任务并清空等待队列 .setCmd(NormalCmdFactory.getInstance()
*/ .createCmd(new DTaskWrapper(null), NormalCmdFactory.TASK_RESUME_ALL,
public void stopAllTask() { ICmd.TASK_TYPE_DOWNLOAD))
AriaManager.getInstance(AriaManager.APP) .exe();
.setCmd(NormalCmdFactory.getInstance() }
.createCmd(new DTaskWrapper(null), NormalCmdFactory.TASK_STOP_ALL,
ICmd.TASK_TYPE_DOWNLOAD)) /**
.exe(); * 删除所有任务
} *
* @param removeFile {@code true} 删除已经下载完成的任务不仅删除下载记录还会删除已经下载完成的文件{@code false}
/** * 如果文件已经下载完成只删除下载记录
* 恢复所有正在下载的任务 */
* 1.如果执行队列没有满则开始下载任务直到执行队列满 public void removeAllTask(boolean removeFile) {
* 2.如果队列执行队列已经满了则将所有任务添加到等待队列中 final AriaManager ariaManager = AriaManager.getInstance(AriaManager.APP);
*/ CancelAllCmd cancelCmd =
public void resumeAllTask() { (CancelAllCmd) CommonUtil.createNormalCmd(new DTaskWrapper(null),
AriaManager.getInstance(AriaManager.APP) NormalCmdFactory.TASK_CANCEL_ALL, ICmd.TASK_TYPE_DOWNLOAD);
.setCmd(NormalCmdFactory.getInstance() cancelCmd.removeFile = removeFile;
.createCmd(new DTaskWrapper(null), NormalCmdFactory.TASK_RESUME_ALL, ariaManager.setCmd(cancelCmd).exe();
ICmd.TASK_TYPE_DOWNLOAD)) Set<String> keys = ariaManager.getReceiver().keySet();
.exe(); for (String key : keys) {
} ariaManager.getReceiver().remove(key);
}
/** }
* 删除所有任务
*
* @param removeFile {@code true} 删除已经下载完成的任务不仅删除下载记录还会删除已经下载完成的文件{@code false}
* 如果文件已经下载完成只删除下载记录
*/
public void removeAllTask(boolean removeFile) {
final AriaManager ariaManager = AriaManager.getInstance(AriaManager.APP);
CancelAllCmd cancelCmd =
(CancelAllCmd) CommonUtil.createNormalCmd(new DTaskWrapper(null),
NormalCmdFactory.TASK_CANCEL_ALL, ICmd.TASK_TYPE_DOWNLOAD);
cancelCmd.removeFile = removeFile;
ariaManager.setCmd(cancelCmd).exe();
Set<String> keys = ariaManager.getReceiver().keySet();
for (String key : keys) {
ariaManager.getReceiver().remove(key);
}
}
} }

@ -0,0 +1,101 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.downloader;
import com.arialyy.aria.util.ALog;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
// Data is expected to be a series of data chunks in the form <chunk size><chunk bytes><chunk size><chunk bytes>
// The final data chunk should be a 0-length chunk which will indicate end of input.
@Deprecated
public class ChunkedInputStream extends InputStream {
private static final String TAG = "ChunkedInputStream";
private DataInputStream din;
private int unreadBytes = 0; // Bytes remaining in the current chunk of data
private byte[] singleByte = new byte[1];
private boolean endOfData = false;
private String id;
public ChunkedInputStream(InputStream in, String id) {
din = new DataInputStream(in);
this.id = id;
ALog.d(TAG, String.format("Creating chunked input for %s", id));
}
@Override
public void close() throws IOException {
ALog.d(TAG, String.format("%s: Closing chunked input.", id));
din.close();
}
@Override
public int read() throws IOException {
int bytesRead = read(singleByte, 0, 1);
return (bytesRead == -1) ? -1 : (int) singleByte[0];
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int bytesRead = 0;
if (len < 0) {
throw new IllegalArgumentException(id + ": Negative read length");
} else if (len == 0) {
return 0;
}
// If there is a current unread chunk, read from that, or else get the next chunk.
if (unreadBytes == 0) {
try {
// Find the next chunk size
unreadBytes = din.readInt();
if (ALog.DEBUG) {
ALog.d(TAG, String.format("%s: Chunk size %s", id, unreadBytes));
}
if (unreadBytes == 0) {
ALog.d(TAG, String.format("%s: Hit end of data", id));
endOfData = true;
return -1;
}
} catch (IOException err) {
throw new IOException(id + ": Error while attempting to read chunk length", err);
}
}
int bytesToRead = Math.min(len, unreadBytes);
try {
din.readFully(b, off, bytesToRead);
} catch (IOException err) {
throw new IOException(
id + ": Error while attempting to read " + bytesToRead + " bytes from current chunk",
err);
}
unreadBytes -= bytesToRead;
bytesRead += bytesToRead;
return bytesRead;
}
public boolean isEndOfData() {
return endOfData;
}
}

@ -163,27 +163,27 @@ class ConnectionHelp {
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"); "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36");
} }
if (conn.getRequestProperty("Accept") == null) { if (conn.getRequestProperty("Accept") == null) {
//StringBuilder accept = new StringBuilder(); StringBuilder accept = new StringBuilder();
//accept accept.append("image/gif, ")
//.append("image/gif, ") .append("image/jpeg, ")
//.append("image/jpeg, ") .append("image/pjpeg, ")
//.append("image/pjpeg, ") .append("image/webp, ")
//.append("image/webp, ") .append("image/apng, ")
//.append("image/apng, ") .append("application/xml, ")
//.append("application/xml, ") .append("application/xaml+xml, ")
//.append("application/xaml+xml, ") .append("application/xhtml+xml, ")
//.append("application/xhtml+xml, ") .append("application/x-shockwave-flash, ")
//.append("application/x-shockwave-flash, ") .append("application/x-ms-xbap, ")
//.append("application/x-ms-xbap, ") .append("application/x-ms-application, ")
//.append("application/x-ms-application, ") .append("application/msword, ")
//.append("application/msword, ") .append("application/vnd.ms-excel, ")
//.append("application/vnd.ms-excel, ") .append("application/vnd.ms-xpsdocument, ")
//.append("application/vnd.ms-xpsdocument, ") .append("application/vnd.ms-powerpoint, ")
//.append("application/vnd.ms-powerpoint, ") .append("application/signed-exchange, ")
//.append("text/plain, ") .append("text/plain, ")
//.append("text/html, ") .append("text/html, ")
//.append("*/*"); .append("*/*");
conn.setRequestProperty("Accept", "*/*"); conn.setRequestProperty("Accept", accept.toString());
} }
//302获取重定向地址 //302获取重定向地址
conn.setInstanceFollowRedirects(false); conn.setInstanceFollowRedirects(false);

@ -24,6 +24,7 @@ import com.arialyy.aria.core.inf.AbsTaskWrapper;
import com.arialyy.aria.core.inf.IDownloadListener; import com.arialyy.aria.core.inf.IDownloadListener;
import com.arialyy.aria.exception.BaseException; import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.exception.TaskException; import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.BufferedRandomAccessFile; import com.arialyy.aria.util.BufferedRandomAccessFile;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -83,6 +84,20 @@ public class Downloader extends AbsFileer<DownloadEntity, DTaskWrapper> {
return false; return false;
} }
/**
* 如果使用"Content-Disposition"中的文件名需要更新{@link #mTempFile}的路径
*/
void updateTempFile() {
if (!mTempFile.getPath().equals(mEntity.getDownloadPath())) {
if (!mTempFile.exists()) {
mTempFile = new File(mEntity.getDownloadPath());
} else {
boolean b = mTempFile.renameTo(new File(mEntity.getDownloadPath()));
ALog.d(TAG, String.format("更新tempFile文件名%s", b ? "成功" : "失败"));
}
}
}
@Override protected int getType() { @Override protected int getType() {
return DOWNLOAD; return DOWNLOAD;
} }

@ -36,6 +36,7 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.CookieManager; import java.net.CookieManager;
import java.net.HttpCookie; import java.net.HttpCookie;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
@ -77,7 +78,6 @@ public class HttpFileInfoThread implements Runnable {
ConnectionHelp.setConnectParam(mTaskDelegate, conn); ConnectionHelp.setConnectParam(mTaskDelegate, conn);
conn.setRequestProperty("Range", "bytes=" + 0 + "-"); conn.setRequestProperty("Range", "bytes=" + 0 + "-");
conn.setConnectTimeout(mConnectTimeOut); conn.setConnectTimeout(mConnectTimeOut);
//conn.setChunkedStreamingMode(0);
conn.connect(); conn.connect();
handleConnect(conn); handleConnect(conn);
} catch (IOException e) { } catch (IOException e) {
@ -149,20 +149,7 @@ public class HttpFileInfoThread implements Runnable {
String disposition = conn.getHeaderField("Content-Disposition"); String disposition = conn.getHeaderField("Content-Disposition");
if (mTaskDelegate.isUseServerFileName() && !TextUtils.isEmpty(disposition)) { if (mTaskDelegate.isUseServerFileName() && !TextUtils.isEmpty(disposition)) {
mEntity.setDisposition(CommonUtil.encryptBASE64(disposition)); mEntity.setDisposition(CommonUtil.encryptBASE64(disposition));
if (disposition.contains(";")) { handleContentDisposition(disposition);
String[] infos = disposition.split(";");
for (String info : infos) {
if (info.startsWith("filename") && info.contains("=")) {
String[] temp = info.split("=");
if (temp.length > 1) {
String newName = URLDecoder.decode(temp[1], "utf-8").replaceAll("\"", "");
mEntity.setServerFileName(newName);
renameFile(newName);
break;
}
}
}
}
} }
CookieManager msCookieManager = new CookieManager(); CookieManager msCookieManager = new CookieManager();
List<String> cookiesHeader = headers.get("Set-Cookie"); List<String> cookiesHeader = headers.get("Set-Cookie");
@ -240,6 +227,40 @@ public class HttpFileInfoThread implements Runnable {
} }
} }
/**
* 处理"Content-Disposition"参数
* <a href=https://cloud.tencent.com/developer/section/1189916>Content-Disposition</a></>
*
* @throws UnsupportedEncodingException
*/
private void handleContentDisposition(String disposition) throws UnsupportedEncodingException {
if (disposition.contains(";")) {
String[] infos = disposition.split(";");
if (infos[0].equals("attachment")) {
for (String info : infos) {
if (info.startsWith("filename") && info.contains("=")) {
String[] temp = info.split("=");
if (temp.length > 1) {
String newName = URLDecoder.decode(temp[1], "utf-8").replaceAll("\"", "");
mEntity.setServerFileName(newName);
renameFile(newName);
break;
}
}
}
} else if (infos[0].equals("form-data") && infos.length > 2) {
String[] temp = infos[2].split("=");
if (temp.length > 1) {
String newName = URLDecoder.decode(temp[1], "utf-8").replaceAll("\"", "");
mEntity.setServerFileName(newName);
renameFile(newName);
}
} else {
ALog.w(TAG, "不识别的Content-Disposition参数");
}
}
}
/** /**
* 重命名文件 * 重命名文件
*/ */
@ -248,10 +269,12 @@ public class HttpFileInfoThread implements Runnable {
ALog.w(TAG, "重命名失败【服务器返回的文件名为空】"); ALog.w(TAG, "重命名失败【服务器返回的文件名为空】");
return; return;
} }
ALog.d(TAG, String.format("文件重命名为:%s", newName));
File oldFile = new File(mEntity.getDownloadPath()); File oldFile = new File(mEntity.getDownloadPath());
String newPath = oldFile.getParent() + "/" + newName; String newPath = oldFile.getParent() + "/" + newName;
if (oldFile.exists()) { if (oldFile.exists()) {
oldFile.renameTo(new File(newPath)); boolean b = oldFile.renameTo(new File(newPath));
ALog.d(TAG, String.format("文件重命名%s", b ? "成功" : "失败"));
} }
mEntity.setFileName(newName); mEntity.setFileName(newName);
mEntity.setDownloadPath(newPath); mEntity.setDownloadPath(newPath);
@ -269,7 +292,7 @@ public class HttpFileInfoThread implements Runnable {
} }
return; return;
} }
if (!CheckUtil.checkUrl(newUrl)) { if (!CheckUtil.checkUrlNotThrow(newUrl)) {
failDownload(new TaskException(TAG, "下载失败,重定向url错误"), false); failDownload(new TaskException(TAG, "下载失败,重定向url错误"), false);
return; return;
} }
@ -296,6 +319,7 @@ public class HttpFileInfoThread implements Runnable {
*/ */
private boolean checkLen(long len) { private boolean checkLen(long len) {
if (len != mEntity.getFileSize()) { if (len != mEntity.getFileSize()) {
ALog.d(TAG, "长度不一致,任务为新任务");
mTaskWrapper.setNewTask(true); mTaskWrapper.setNewTask(true);
} }
return true; return true;

@ -19,7 +19,6 @@ import com.arialyy.aria.core.common.AbsThreadTask;
import com.arialyy.aria.core.common.RequestEnum; import com.arialyy.aria.core.common.RequestEnum;
import com.arialyy.aria.core.common.StateConstance; import com.arialyy.aria.core.common.StateConstance;
import com.arialyy.aria.core.common.SubThreadConfig; import com.arialyy.aria.core.common.SubThreadConfig;
import com.arialyy.aria.core.config.BaseTaskConfig;
import com.arialyy.aria.core.config.DownloadConfig; import com.arialyy.aria.core.config.DownloadConfig;
import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadEntity;
@ -30,6 +29,7 @@ import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.BufferedRandomAccessFile; import com.arialyy.aria.util.BufferedRandomAccessFile;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -44,6 +44,7 @@ import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel; import java.nio.channels.ReadableByteChannel;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.zip.GZIPInputStream;
/** /**
* Created by lyy on 2017/1/18. 下载线程 * Created by lyy on 2017/1/18. 下载线程
@ -87,7 +88,12 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DTaskWrapper> {
ConnectionHelp.setConnectParam(taskDelegate, conn); ConnectionHelp.setConnectParam(taskDelegate, conn);
conn.setConnectTimeout(getTaskConfig().getConnectTimeOut()); conn.setConnectTimeout(getTaskConfig().getConnectTimeOut());
conn.setReadTimeout(getTaskConfig().getIOTimeOut()); //设置读取流的等待时间,必须设置该参数 conn.setReadTimeout(getTaskConfig().getIOTimeOut()); //设置读取流的等待时间,必须设置该参数
if (taskDelegate.isChunked()) {
conn.setDoInput(true);
conn.setChunkedStreamingMode(0);
}
conn.connect(); conn.connect();
// 传递参数
if (taskDelegate.getRequestEnum() == RequestEnum.POST) { if (taskDelegate.getRequestEnum() == RequestEnum.POST) {
Map<String, String> params = taskDelegate.getParams(); Map<String, String> params = taskDelegate.getParams();
if (params != null) { if (params != null) {
@ -106,7 +112,9 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DTaskWrapper> {
} }
is = new BufferedInputStream(ConnectionHelp.convertInputStream(conn)); is = new BufferedInputStream(ConnectionHelp.convertInputStream(conn));
if (isOpenDynamicFile) { if (taskDelegate.isChunked()) {
readChunked(is);
} else if (isOpenDynamicFile) {
readDynamicFile(is); readDynamicFile(is);
} else { } else {
//创建可设置位置的文件 //创建可设置位置的文件
@ -114,11 +122,7 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DTaskWrapper> {
new BufferedRandomAccessFile(mConfig.TEMP_FILE, "rwd", getTaskConfig().getBuffSize()); new BufferedRandomAccessFile(mConfig.TEMP_FILE, "rwd", getTaskConfig().getBuffSize());
//设置每条线程写入文件的位置 //设置每条线程写入文件的位置
file.seek(mConfig.START_LOCATION); file.seek(mConfig.START_LOCATION);
if (taskDelegate.isChunked()) { readNormal(is, file);
readChunk(is, file);
} else {
readNormal(is, file);
}
handleComplete(); handleComplete();
} }
} catch (MalformedURLException e) { } catch (MalformedURLException e) {
@ -151,6 +155,41 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DTaskWrapper> {
return this; return this;
} }
/**
* 读取chunked数据
*/
private void readChunked(InputStream is) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(mConfig.TEMP_FILE, true);
byte[] buffer = new byte[getTaskConfig().getBuffSize()];
int len;
while (isLive() && (len = is.read(buffer)) != -1) {
if (isBreak()) {
break;
}
if (mSpeedBandUtil != null) {
mSpeedBandUtil.limitNextBytes(len);
}
fos.write(buffer, 0, len);
progress(len);
}
handleComplete();
} catch (IOException e) {
fail(mChildCurrentLocation, new AriaIOException(TAG,
String.format("文件下载失败,savePath: %s, url: %s", mEntity.getDownloadPath(), mConfig.URL),
e));
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/** /**
* 动态长度文件读取方式 * 动态长度文件读取方式
*/ */
@ -210,16 +249,6 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DTaskWrapper> {
} }
} }
/**
* 读取chunk模式的文件流
*
* @deprecated 暂时先这样处理无chun
*/
private void readChunk(InputStream is, BufferedRandomAccessFile file)
throws IOException {
readNormal(is, file);
}
/** /**
* 读取普通的文件流 * 读取普通的文件流
*/ */
@ -249,6 +278,12 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DTaskWrapper> {
if (!checkBlock()) { if (!checkBlock()) {
return; return;
} }
if (mTaskWrapper.asHttp().isChunked()) {
ALog.i(TAG, "任务下载完成");
STATE.isRunning = false;
mListener.onComplete();
return;
}
if (mChildCurrentLocation == mConfig.END_LOCATION) { if (mChildCurrentLocation == mConfig.END_LOCATION) {
//支持断点的处理 //支持断点的处理

@ -126,6 +126,7 @@ public class SimpleDownloadUtil implements IUtil, Runnable {
case AbsTaskWrapper.D_FTP: case AbsTaskWrapper.D_FTP:
return new FtpFileInfoThread(mTaskWrapper, new OnFileInfoCallback() { return new FtpFileInfoThread(mTaskWrapper, new OnFileInfoCallback() {
@Override public void onComplete(String url, CompleteInfo info) { @Override public void onComplete(String url, CompleteInfo info) {
mDownloader.updateTempFile();
mDownloader.start(); mDownloader.start();
} }
@ -137,6 +138,7 @@ public class SimpleDownloadUtil implements IUtil, Runnable {
case AbsTaskWrapper.D_HTTP: case AbsTaskWrapper.D_HTTP:
return new HttpFileInfoThread(mTaskWrapper, new OnFileInfoCallback() { return new HttpFileInfoThread(mTaskWrapper, new OnFileInfoCallback() {
@Override public void onComplete(String url, CompleteInfo info) { @Override public void onComplete(String url, CompleteInfo info) {
mDownloader.updateTempFile();
mDownloader.start(); mDownloader.start();
} }

@ -44,14 +44,14 @@ class DGTaskWrapperFactory implements IGTEFactory<DownloadGroupEntity, DGTaskWra
} }
@Override public DGTaskWrapper getGTE(String groupHash, List<String> urls) { @Override public DGTaskWrapper getGTE(String groupHash, List<String> urls) {
DownloadGroupEntity entity = DbDataHelper.getHttpDGEntity(groupHash, urls); DownloadGroupEntity entity = DbDataHelper.getOrCreateHttpDGEntity(groupHash, urls);
DGTaskWrapper wrapper = new DGTaskWrapper(entity); DGTaskWrapper wrapper = new DGTaskWrapper(entity);
wrapper.setSubTaskWrapper(createDGSubTaskWrapper(entity)); wrapper.setSubTaskWrapper(createDGSubTaskWrapper(entity));
return wrapper; return wrapper;
} }
@Override public DGTaskWrapper getFTE(String ftpUrl) { @Override public DGTaskWrapper getFTE(String ftpUrl) {
DownloadGroupEntity entity = DbDataHelper.getFtpDGEntity(ftpUrl); DownloadGroupEntity entity = DbDataHelper.getOrCreateFtpDGEntity(ftpUrl);
DGTaskWrapper fte = new DGTaskWrapper(entity); DGTaskWrapper fte = new DGTaskWrapper(entity);
fte.asFtp().setUrlEntity(CommonUtil.getFtpUrlInfo(ftpUrl)); fte.asFtp().setUrlEntity(CommonUtil.getFtpUrlInfo(ftpUrl));

@ -73,24 +73,27 @@ class DTaskWrapperFactory implements INormalTEFactory<DownloadEntity, DTaskWrapp
entity.setDownloadPath(UUID.randomUUID().toString().replace("-", "")); //设置临时路径 entity.setDownloadPath(UUID.randomUUID().toString().replace("-", "")); //设置临时路径
} }
File file = new File(entity.getDownloadPath()); File file = new File(entity.getDownloadPath());
TaskRecord record =
TaskRecord.findFirst(TaskRecord.class, "filePath=?", entity.getDownloadPath()); if (!entity.isComplete()) {
if (record == null) { TaskRecord record =
resetEntity(entity); TaskRecord.findFirst(TaskRecord.class, "filePath=?", entity.getDownloadPath());
} else { if (record == null) {
if (record.isBlock) { resetEntity(entity);
int count = 0; } else {
for (int i = 0, len = record.threadNum; i < len; i++) { if (record.isBlock) {
File temp = new File(String.format(AbsFileer.SUB_PATH, record.filePath, i)); int count = 0;
if (!temp.exists()) { for (int i = 0, len = record.threadNum; i < len; i++) {
count++; File temp = new File(String.format(AbsFileer.SUB_PATH, record.filePath, i));
if (!temp.exists()) {
count++;
}
} }
} if (count == record.threadNum) {
if (count == record.threadNum) { resetEntity(entity);
}
} else if (!file.exists()) { // 非分块文件需要判断文件是否存在
resetEntity(entity); resetEntity(entity);
} }
} else if (!file.exists()) { // 非分块文件需要判断文件是否存在
resetEntity(entity);
} }
} }
return entity; return entity;

@ -15,11 +15,9 @@
*/ */
package com.arialyy.aria.core.upload; package com.arialyy.aria.core.upload;
import android.content.Context;
import android.support.annotation.CheckResult; import android.support.annotation.CheckResult;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.text.TextUtils; import android.text.TextUtils;
import com.arialyy.aria.core.Aria;
import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.core.command.ICmd; import com.arialyy.aria.core.command.ICmd;
import com.arialyy.aria.core.command.normal.CancelAllCmd; import com.arialyy.aria.core.command.normal.CancelAllCmd;

@ -17,7 +17,6 @@ package com.arialyy.aria.orm;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.support.v4.util.LruCache;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.CommonUtil;
import java.lang.reflect.Field; import java.lang.reflect.Field;
@ -28,60 +27,6 @@ import java.net.URLEncoder;
*/ */
abstract class AbsDelegate { abstract class AbsDelegate {
static final String TAG = "AbsDelegate"; static final String TAG = "AbsDelegate";
static final int CREATE_TABLE = 0;
static final int TABLE_EXISTS = 1;
static final int INSERT_DATA = 2;
static final int MODIFY_DATA = 3;
static final int FIND_DATA = 4;
static final int DEL_DATA = 6;
static final int ROW_ID = 7;
static final int RELATION = 8;
static final int DROP_TABLE = 9;
static LruCache<String, DbEntity> mDataCache = new LruCache<>(1024);
/**
* 打印数据库日志
*
* @param type {@link DelegateWrapper}
*/
static void print(int type, String sql) {
if (ALog.DEBUG) {
return;
}
String str = "";
switch (type) {
case CREATE_TABLE:
str = "创建表 >>>> ";
break;
case TABLE_EXISTS:
str = "表是否存在 >>>> ";
break;
case INSERT_DATA:
str = "插入数据 >>>> ";
break;
case MODIFY_DATA:
str = "修改数据 >>>> ";
break;
case FIND_DATA:
str = "查询数据 >>>> ";
break;
case ROW_ID:
str = "查询RowId >>> ";
break;
case RELATION:
str = "查询关联表 >>> ";
break;
case DROP_TABLE:
str = "删除表 >>> ";
break;
}
ALog.d(TAG, str.concat(sql));
}
String getCacheKey(DbEntity dbEntity) {
return dbEntity.getClass().getName() + "_" + dbEntity.rowID;
}
/** /**
* URL编码字符串 * URL编码字符串
@ -140,10 +85,6 @@ abstract class AbsDelegate {
} }
} }
void close(SQLiteDatabase db) {
//if (db != null && db.isOpen()) db.close();
}
/** /**
* 检查数据库是否关闭已经关闭的话打开数据库 * 检查数据库是否关闭已经关闭的话打开数据库
* *
@ -151,7 +92,7 @@ abstract class AbsDelegate {
*/ */
SQLiteDatabase checkDb(SQLiteDatabase db) { SQLiteDatabase checkDb(SQLiteDatabase db) {
if (db == null || !db.isOpen()) { if (db == null || !db.isOpen()) {
db = SqlHelper.INSTANCE.getWritableDatabase(); db = SqlHelper.getInstance().getDb();
} }
return db; return db;
} }

@ -33,6 +33,7 @@ import java.util.Map;
*/ */
class DBConfig { class DBConfig {
/*adb pull /mnt/sdcard/Android/data/com.arialyy.simple/files/DB/AriaLyyDb d:/db*/ /*adb pull /mnt/sdcard/Android/data/com.arialyy.simple/files/DB/AriaLyyDb d:/db*/
static boolean DEBUG = false;
static Map<String, Class> mapping = new HashMap<>(); static Map<String, Class> mapping = new HashMap<>();
static String DB_NAME; static String DB_NAME;
static int VERSION = 45; static int VERSION = 45;

@ -42,7 +42,6 @@ class DelegateCommon extends AbsDelegate {
void dropTable(SQLiteDatabase db, String tableName) { void dropTable(SQLiteDatabase db, String tableName) {
db = checkDb(db); db = checkDb(db);
String deleteSQL = String.format("DROP TABLE IF EXISTS %s", tableName); String deleteSQL = String.format("DROP TABLE IF EXISTS %s", tableName);
print(DROP_TABLE, deleteSQL);
//db.beginTransaction(); //db.beginTransaction();
db.execSQL(deleteSQL); db.execSQL(deleteSQL);
//db.setTransactionSuccessful(); //db.setTransactionSuccessful();
@ -71,7 +70,6 @@ class DelegateCommon extends AbsDelegate {
return tableExists(db, CommonUtil.getClassName(clazz)); return tableExists(db, CommonUtil.getClassName(clazz));
} }
/** /**
* 查找表是否存在 * 查找表是否存在
* *
@ -85,7 +83,6 @@ class DelegateCommon extends AbsDelegate {
String sql = String sql =
String.format("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='%s'", String.format("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='%s'",
tableName); tableName);
print(TABLE_EXISTS, sql);
cursor = db.rawQuery(sql, null); cursor = db.rawQuery(sql, null);
if (cursor != null && cursor.moveToNext()) { if (cursor != null && cursor.moveToNext()) {
int count = cursor.getInt(0); int count = cursor.getInt(0);
@ -97,7 +94,6 @@ class DelegateCommon extends AbsDelegate {
e.printStackTrace(); e.printStackTrace();
} finally { } finally {
closeCursor(cursor); closeCursor(cursor);
close(db);
} }
return false; return false;
} }
@ -119,11 +115,9 @@ class DelegateCommon extends AbsDelegate {
params[i] = String.format("'%s'", encodeStr(expression[i + 1])); params[i] = String.format("'%s'", encodeStr(expression[i + 1]));
} }
sql = String.format(sql, params); sql = String.format(sql, params);
print(FIND_DATA, sql);
Cursor cursor = db.rawQuery(sql, null); Cursor cursor = db.rawQuery(sql, null);
final boolean isExist = cursor.getCount() > 0; final boolean isExist = cursor.getCount() > 0;
closeCursor(cursor); closeCursor(cursor);
close(db);
return isExist; return isExist;
} }
@ -223,9 +217,7 @@ class DelegateCommon extends AbsDelegate {
String str = sb.toString(); String str = sb.toString();
str = str.substring(0, str.length() - 1) + ");"; str = str.substring(0, str.length() - 1) + ");";
print(CREATE_TABLE, str);
db.execSQL(str); db.execSQL(str);
} }
close(db);
} }
} }

File diff suppressed because it is too large Load Diff

@ -49,9 +49,7 @@ class DelegateUpdate extends AbsDelegate {
params[i] = String.format("'%s'", encodeStr(expression[i + 1])); params[i] = String.format("'%s'", encodeStr(expression[i + 1]));
} }
sql = String.format(sql, params); sql = String.format(sql, params);
print(DEL_DATA, sql);
db.execSQL(sql); db.execSQL(sql);
close(db);
} }
/** /**
@ -66,7 +64,6 @@ class DelegateUpdate extends AbsDelegate {
} else { } else {
ALog.e(TAG, "更新记录失败,记录没有属性字段"); ALog.e(TAG, "更新记录失败,记录没有属性字段");
} }
close(db);
} }
/** /**
@ -139,7 +136,6 @@ class DelegateUpdate extends AbsDelegate {
} else { } else {
ALog.e(TAG, "保存记录失败,记录没有属性字段"); ALog.e(TAG, "保存记录失败,记录没有属性字段");
} }
close(db);
} }
/** /**

@ -36,7 +36,7 @@ public class DelegateWrapper {
private DelegateWrapper(Context context) { private DelegateWrapper(Context context) {
SqlHelper helper = SqlHelper.init(context.getApplicationContext()); SqlHelper helper = SqlHelper.init(context.getApplicationContext());
mDb = helper.getWritableDatabase(); mDb = helper.getDb();
mDManager = DelegateManager.getInstance(); mDManager = DelegateManager.getInstance();
} }

@ -1,257 +1,302 @@
/* /*
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.arialyy.aria.orm; package com.arialyy.aria.orm;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
import com.arialyy.aria.util.ALog; import android.os.Build;
import java.util.ArrayList; import com.arialyy.aria.util.ALog;
import java.util.HashMap; import java.io.File;
import java.util.List; import java.util.ArrayList;
import java.util.Map; import java.util.HashMap;
import java.util.Set; import java.util.List;
import java.util.Map;
/** import java.util.Set;
* Created by lyy on 2015/11/2.
* sql帮助类 /**
*/ * Created by lyy on 2015/11/2.
final class SqlHelper extends SQLiteOpenHelper { * sql帮助类
private static final String TAG = "SqlHelper"; */
static volatile SqlHelper INSTANCE = null; final class SqlHelper extends SQLiteOpenHelper {
private static final String TAG = "SqlHelper";
private DelegateCommon mDelegate; private static volatile SqlHelper INSTANCE = null;
private Context mContext;
synchronized static SqlHelper init(Context context) {
if (INSTANCE == null) { private DelegateCommon mDelegate;
synchronized (SqlHelper.class) {
DelegateCommon delegate = DelegateManager.getInstance().getDelegate(DelegateCommon.class); synchronized static SqlHelper init(Context context) {
INSTANCE = new SqlHelper(context.getApplicationContext(), delegate); if (INSTANCE == null) {
SQLiteDatabase db = INSTANCE.getWritableDatabase(); synchronized (SqlHelper.class) {
db = delegate.checkDb(db); DelegateCommon delegate = DelegateManager.getInstance().getDelegate(DelegateCommon.class);
// SQLite在3.6.19版本中开始支持外键约束, INSTANCE = new SqlHelper(context.getApplicationContext(), delegate);
// 而在Android中 2.1以前的版本使用的SQLite版本是3.5.9, 在2.2版本中使用的是3.6.22. }
// 但是为了兼容以前的程序,默认并没有启用该功能,如果要启用该功能 }
// 需要使用如下语句: return INSTANCE;
db.execSQL("PRAGMA foreign_keys=ON;"); }
Set<String> tables = DBConfig.mapping.keySet();
for (String tableName : tables) { static SqlHelper getInstance() {
Class clazz = DBConfig.mapping.get(tableName); return INSTANCE;
}
if (!delegate.tableExists(db, clazz)) {
delegate.createTable(db, clazz); private SqlHelper(Context context, DelegateCommon delegate) {
} super(DBConfig.SAVE_IN_SDCARD ? new DatabaseContext(context) : context, DBConfig.DB_NAME, null,
} DBConfig.VERSION);
} mContext = context;
} mDelegate = delegate;
return INSTANCE; }
}
@Override public void onOpen(SQLiteDatabase db) {
private SqlHelper(Context context, DelegateCommon delegate) { super.onOpen(db);
super(DBConfig.SAVE_IN_SDCARD ? new DatabaseContext(context) : context, DBConfig.DB_NAME, null, }
DBConfig.VERSION);
mDelegate = delegate; @Override public void onConfigure(SQLiteDatabase db) {
} super.onConfigure(db);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
@Override public void onCreate(SQLiteDatabase db) { db.setForeignKeyConstraintsEnabled(true);
} else {
} // SQLite在3.6.19版本中开始支持外键约束,
// 而在Android中 2.1以前的版本使用的SQLite版本是3.5.9, 在2.2版本中使用的是3.6.22.
@Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // 但是为了兼容以前的程序,默认并没有启用该功能,如果要启用该功能
if (oldVersion < newVersion) { // 需要使用如下语句:
if (oldVersion < 31) { db.execSQL("PRAGMA foreign_keys=ON;");
handle314AriaUpdate(db); }
} else if (oldVersion < 45) { if (DBConfig.DEBUG) {
handle360AriaUpdate(db); db.enableWriteAheadLogging();
} else { }
handleDbUpdate(db, null, null); }
}
} @Override public void onCreate(SQLiteDatabase db) {
} DelegateCommon delegate = DelegateManager.getInstance().getDelegate(DelegateCommon.class);
Set<String> tables = DBConfig.mapping.keySet();
@Override public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { for (String tableName : tables) {
if (oldVersion > newVersion) { Class clazz = DBConfig.mapping.get(tableName);
handleDbUpdate(db, null, null); if (!delegate.tableExists(db, clazz)) {
} delegate.createTable(db, clazz);
} }
}
/** }
* 处理数据库升级该段代码无法修改表字段
* @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
* @param modifyColumns 需要修改的表字段的映射key为表名 if (oldVersion < newVersion) {
* value{@code Map<String, String>}中的Map的key为老字段名称value为该老字段对应的新字段名称 if (oldVersion < 31) {
* @param delColumns 需要删除的表字段key为表名value{@code List<String>}为需要删除的字段列表 handle314AriaUpdate(db);
*/ } else if (oldVersion < 45) {
private void handleDbUpdate(SQLiteDatabase db, Map<String, Map<String, String>> modifyColumns, handle360AriaUpdate(db);
Map<String, List<String>> delColumns) { } else {
if (db == null) { handleDbUpdate(db, null, null);
ALog.e("SqlHelper", "db 为 null"); }
return; }
} else if (!db.isOpen()) { }
ALog.e("SqlHelper", "db已关闭");
return; @Override public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
} if (oldVersion > newVersion) {
handleDbUpdate(db, null, null);
try { }
db.beginTransaction(); }
Set<String> tables = DBConfig.mapping.keySet();
for (String tableName : tables) { /**
Class clazz = DBConfig.mapping.get(tableName); * 获取数据库连接
if (mDelegate.tableExists(db, clazz)) { */
db = mDelegate.checkDb(db); SQLiteDatabase getDb() {
//修改表名为中介表名 SQLiteDatabase db;
String alertSql = String.format("ALTER TABLE %s RENAME TO %s_temp", tableName, tableName); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
db.execSQL(alertSql); SQLiteDatabase.OpenParams params = new SQLiteDatabase.OpenParams.Builder().setOpenFlags(
SQLiteDatabase.NO_LOCALIZED_COLLATORS | SQLiteDatabase.OPEN_READWRITE |
//创建新表 SQLiteDatabase.CREATE_IF_NECESSARY).build();
mDelegate.createTable(db, clazz); setOpenParams(params);
db = getWritableDatabase();
String sql = String.format("SELECT COUNT(*) FROM %s_temp", tableName); } else {
Cursor cursor = db.rawQuery(sql, null); //SQLiteDatabase.openOrCreateDatabase()
cursor.moveToFirst(); File dbFile = mContext.getDatabasePath(DBConfig.DB_NAME);
long count = cursor.getLong(0); if (!dbFile.exists()) {
cursor.close(); db = getWritableDatabase();
} else {
if (count > 0) { // 触发一次SQLiteOpenHelper的流程,再使用NO_LOCALIZED_COLLATORS标志打开数据库
// 获取所有表字段名称 db = getReadableDatabase();
Cursor columnC = db.close();
db.rawQuery(String.format("PRAGMA table_info(%s_temp)", tableName), null); db = SQLiteDatabase.openDatabase(dbFile.getPath(), null,
StringBuilder params = new StringBuilder(); SQLiteDatabase.NO_LOCALIZED_COLLATORS | SQLiteDatabase.OPEN_READWRITE |
SQLiteDatabase.CREATE_IF_NECESSARY);
while (columnC.moveToNext()) { }
String columnName = columnC.getString(columnC.getColumnIndex("name")); }
if (delColumns != null && delColumns.get(tableName) != null) { return db;
List<String> delColumn = delColumns.get(tableName); }
if (delColumn != null && !delColumn.isEmpty()) {
if (delColumn.contains(columnName)) { /**
continue; * 处理数据库升级该段代码无法修改表字段
} *
} * @param modifyColumns 需要修改的表字段的映射key为表名
} * value{@code Map<String, String>}中的Map的key为老字段名称value为该老字段对应的新字段名称
* @param delColumns 需要删除的表字段key为表名value{@code List<String>}为需要删除的字段列表
params.append(columnName).append(","); */
} private void handleDbUpdate(SQLiteDatabase db, Map<String, Map<String, String>> modifyColumns,
columnC.close(); Map<String, List<String>> delColumns) {
if (db == null) {
String oldParamStr = params.toString(); ALog.e("SqlHelper", "db 为 null");
oldParamStr = oldParamStr.substring(0, oldParamStr.length() - 1); return;
String newParamStr = oldParamStr; } else if (!db.isOpen()) {
// 处理字段名称改变 ALog.e("SqlHelper", "db已关闭");
if (modifyColumns != null) { return;
//newParamStr = params.toString(); }
Map<String, String> columnMap = modifyColumns.get(tableName);
if (columnMap != null && !columnMap.isEmpty()) { try {
Set<String> keys = columnMap.keySet(); db.beginTransaction();
for (String key : keys) { Set<String> tables = DBConfig.mapping.keySet();
if (newParamStr.contains(key)) { for (String tableName : tables) {
newParamStr = newParamStr.replace(key, columnMap.get(key)); Class clazz = DBConfig.mapping.get(tableName);
} if (mDelegate.tableExists(db, clazz)) {
} //修改表名为中介表名
} String alertSql = String.format("ALTER TABLE %s RENAME TO %s_temp", tableName, tableName);
} db.execSQL(alertSql);
//恢复数据
String insertSql = //创建新表
String.format("INSERT INTO %s (%s) SELECT %s FROM %s_temp", tableName, newParamStr, mDelegate.createTable(db, clazz);
oldParamStr, tableName);
ALog.d(TAG, "insertSql = " + insertSql); String sql = String.format("SELECT COUNT(*) FROM %s_temp", tableName);
db.execSQL(insertSql); Cursor cursor = db.rawQuery(sql, null);
} cursor.moveToFirst();
//删除中介表 long count = cursor.getLong(0);
mDelegate.dropTable(db, tableName + "_temp"); cursor.close();
}
} if (count > 0) {
db.setTransactionSuccessful(); // 获取所有表字段名称
} catch (Exception e) { Cursor columnC =
ALog.e(TAG, e); db.rawQuery(String.format("PRAGMA table_info(%s_temp)", tableName), null);
} finally { StringBuilder params = new StringBuilder();
db.endTransaction();
} while (columnC.moveToNext()) {
String columnName = columnC.getString(columnC.getColumnIndex("name"));
mDelegate.close(db); if (delColumns != null && delColumns.get(tableName) != null) {
} List<String> delColumn = delColumns.get(tableName);
if (delColumn != null && !delColumn.isEmpty()) {
/** if (delColumn.contains(columnName)) {
* 处理3.6以下版本的数据库升级 continue;
*/ }
private void handle360AriaUpdate(SQLiteDatabase db) { }
String[] taskTables = }
new String[] { "UploadTaskEntity", "DownloadTaskEntity", "DownloadGroupTaskEntity" };
for (String taskTable : taskTables) { params.append(columnName).append(",");
if (mDelegate.tableExists(db, taskTable)) { }
mDelegate.dropTable(db, taskTable); columnC.close();
}
} String oldParamStr = params.toString();
Map<String, Map<String, String>> columnMap = new HashMap<>(); oldParamStr = oldParamStr.substring(0, oldParamStr.length() - 1);
Map<String, String> map = new HashMap<>(); String newParamStr = oldParamStr;
map.put("groupName", "groupHash"); // 处理字段名称改变
columnMap.put("DownloadEntity", map); if (modifyColumns != null) {
columnMap.put("DownloadGroupEntity", map); //newParamStr = params.toString();
handleDbUpdate(db, columnMap, null); Map<String, String> columnMap = modifyColumns.get(tableName);
} if (columnMap != null && !columnMap.isEmpty()) {
Set<String> keys = columnMap.keySet();
/** for (String key : keys) {
* 处理3.4版本之前数据库迁移主要是修改子表外键字段对应的值 if (newParamStr.contains(key)) {
*/ newParamStr = newParamStr.replace(key, columnMap.get(key));
private void handle314AriaUpdate(SQLiteDatabase db) { }
String[] taskTables = }
new String[] { "UploadTaskEntity", "DownloadTaskEntity", "DownloadGroupTaskEntity" }; }
for (String taskTable : taskTables) { }
if (mDelegate.tableExists(db, taskTable)) { //恢复数据
mDelegate.dropTable(db, taskTable); String insertSql =
} String.format("INSERT INTO %s (%s) SELECT %s FROM %s_temp", tableName, newParamStr,
} oldParamStr, tableName);
ALog.d(TAG, "insertSql = " + insertSql);
//删除所有主键为null和逐渐重复的数据 db.execSQL(insertSql);
String[] tables = new String[] { "DownloadEntity", "DownloadGroupEntity" }; }
String[] keys = new String[] { "downloadPath", "groupName" }; //删除中介表
int i = 0; mDelegate.dropTable(db, tableName + "_temp");
for (String tableName : tables) { }
String pColumn = keys[i]; }
String nullSql = db.setTransactionSuccessful();
String.format("DELETE FROM %s WHERE %s='' OR %s IS NULL", tableName, pColumn, pColumn); } catch (Exception e) {
ALog.d(TAG, nullSql); ALog.e(TAG, e);
db.execSQL(nullSql); } finally {
db.endTransaction();
//删除所有主键重复的数据 }
String repeatSql = }
String.format(
"DELETE FROM %s WHERE %s IN(SELECT %s FROM %s GROUP BY %s HAVING COUNT(%s) > 1)", /**
tableName, pColumn, pColumn, tableName, pColumn, pColumn); * 处理3.6以下版本的数据库升级
*/
ALog.d(TAG, repeatSql); private void handle360AriaUpdate(SQLiteDatabase db) {
db.execSQL(repeatSql); String[] taskTables =
i++; new String[] { "UploadTaskEntity", "DownloadTaskEntity", "DownloadGroupTaskEntity" };
} for (String taskTable : taskTables) {
if (mDelegate.tableExists(db, taskTable)) {
Map<String, Map<String, String>> modifyColumnMap = new HashMap<>(); mDelegate.dropTable(db, taskTable);
Map<String, String> map = new HashMap<>(); }
map.put("groupName", "groupHash"); }
modifyColumnMap.put("DownloadEntity", map); Map<String, Map<String, String>> columnMap = new HashMap<>();
modifyColumnMap.put("DownloadGroupEntity", map); Map<String, String> map = new HashMap<>();
map.put("groupName", "groupHash");
Map<String, List<String>> delColumnMap = new HashMap<>(); columnMap.put("DownloadEntity", map);
List<String> dEntityDel = new ArrayList<>(); columnMap.put("DownloadGroupEntity", map);
dEntityDel.add("taskKey"); handleDbUpdate(db, columnMap, null);
delColumnMap.put("DownloadEntity", dEntityDel); }
List<String> dgEntityDel = new ArrayList<>();
dgEntityDel.add("subtask"); /**
delColumnMap.put("DownloadGroupEntity", dgEntityDel); * 处理3.4版本之前数据库迁移主要是修改子表外键字段对应的值
*/
handleDbUpdate(db, modifyColumnMap, delColumnMap); private void handle314AriaUpdate(SQLiteDatabase db) {
} String[] taskTables =
new String[] { "UploadTaskEntity", "DownloadTaskEntity", "DownloadGroupTaskEntity" };
for (String taskTable : taskTables) {
if (mDelegate.tableExists(db, taskTable)) {
mDelegate.dropTable(db, taskTable);
}
}
//删除所有主键为null和逐渐重复的数据
String[] tables = new String[] { "DownloadEntity", "DownloadGroupEntity" };
String[] keys = new String[] { "downloadPath", "groupName" };
int i = 0;
for (String tableName : tables) {
String pColumn = keys[i];
String nullSql =
String.format("DELETE FROM %s WHERE %s='' OR %s IS NULL", tableName, pColumn, pColumn);
ALog.d(TAG, nullSql);
db.execSQL(nullSql);
//删除所有主键重复的数据
String repeatSql =
String.format(
"DELETE FROM %s WHERE %s IN(SELECT %s FROM %s GROUP BY %s HAVING COUNT(%s) > 1)",
tableName, pColumn, pColumn, tableName, pColumn, pColumn);
ALog.d(TAG, repeatSql);
db.execSQL(repeatSql);
i++;
}
Map<String, Map<String, String>> modifyColumnMap = new HashMap<>();
Map<String, String> map = new HashMap<>();
map.put("groupName", "groupHash");
modifyColumnMap.put("DownloadEntity", map);
modifyColumnMap.put("DownloadGroupEntity", map);
Map<String, List<String>> delColumnMap = new HashMap<>();
List<String> dEntityDel = new ArrayList<>();
dEntityDel.add("taskKey");
delColumnMap.put("DownloadEntity", dEntityDel);
List<String> dgEntityDel = new ArrayList<>();
dgEntityDel.add("subtask");
delColumnMap.put("DownloadGroupEntity", dgEntityDel);
handleDbUpdate(db, modifyColumnMap, delColumnMap);
}
} }

@ -1,254 +1,269 @@
/* /*
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.arialyy.aria.util; package com.arialyy.aria.util;
import android.text.TextUtils; import android.text.TextUtils;
import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.inf.AbsTaskWrapper; import com.arialyy.aria.core.inf.AbsTaskWrapper;
import com.arialyy.aria.core.upload.UTaskWrapper; import com.arialyy.aria.core.upload.UTaskWrapper;
import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.upload.UploadEntity;
import com.arialyy.aria.exception.ParamException; import com.arialyy.aria.exception.ParamException;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
/** /**
* Created by Lyy on 2016/9/23. * Created by Lyy on 2016/9/23.
* 检查帮助类 * 检查帮助类
*/ */
public class CheckUtil { public class CheckUtil {
private static final String TAG = "CheckUtil"; private static final String TAG = "CheckUtil";
/** /**
* 检查ftp上传路径如果ftp上传路径为空抛出空指针异常 * 检查ftp上传路径如果ftp上传路径为空抛出空指针异常
* 如果ftp上传路径不是以"ftp""sftp"抛出参数异常 * 如果ftp上传路径不是以"ftp""sftp"抛出参数异常
* *
* @param ftpUrl ftp上传路径 * @param ftpUrl ftp上传路径
*/ */
public static void checkFtpUploadUrl(String ftpUrl) { public static void checkFtpUploadUrl(String ftpUrl) {
if (TextUtils.isEmpty(ftpUrl)) { if (TextUtils.isEmpty(ftpUrl)) {
throw new ParamException("ftp上传路径为空"); throw new ParamException("ftp上传路径为空");
} else if (!ftpUrl.startsWith("ftp") || !ftpUrl.startsWith("sftp")) { } else if (!ftpUrl.startsWith("ftp") || !ftpUrl.startsWith("sftp")) {
throw new ParamException("ftp上传路径无效"); throw new ParamException("ftp上传路径无效");
} }
} }
/** /**
* 判空 * 判空
*/ */
public static void checkNull(Object obj) { public static void checkNull(Object obj) {
if (obj == null) throw new IllegalArgumentException("不能传入空对象"); if (obj == null) throw new IllegalArgumentException("不能传入空对象");
} }
/** /**
* 检查分页数据需要查询的页数从1开始如果page小于1 num 小于1则抛出{@link NullPointerException} * 检查分页数据需要查询的页数从1开始如果page小于1 num 小于1则抛出{@link NullPointerException}
* *
* @param page 从1 开始 * @param page 从1 开始
* @param num 每页数量 * @param num 每页数量
*/ */
public static void checkPageParams(int page, int num) { public static void checkPageParams(int page, int num) {
if (page < 1 || num < 1) throw new NullPointerException("page和num不能小于1"); if (page < 1 || num < 1) throw new NullPointerException("page和num不能小于1");
} }
/** /**
* 检查sql的expression是否合法 * 检查sql的expression是否合法
*/ */
public static void checkSqlExpression(String... expression) { public static void checkSqlExpression(String... expression) {
if (expression.length == 0) { if (expression.length == 0) {
throw new IllegalArgumentException("sql语句表达式不能为null"); throw new IllegalArgumentException("sql语句表达式不能为null");
} }
if (expression.length == 1) { if (expression.length == 1) {
throw new IllegalArgumentException("表达式需要写入参数"); throw new IllegalArgumentException("表达式需要写入参数");
} }
String where = expression[0]; String where = expression[0];
if (!where.contains("?")) { if (!where.contains("?")) {
throw new IllegalArgumentException("请在where语句的'='后编写?"); throw new IllegalArgumentException("请在where语句的'='后编写?");
} }
Pattern pattern = Pattern.compile("\\?"); Pattern pattern = Pattern.compile("\\?");
Matcher matcher = pattern.matcher(where); Matcher matcher = pattern.matcher(where);
int count = 0; int count = 0;
while (matcher.find()) { while (matcher.find()) {
count++; count++;
} }
if (count < expression.length - 1) { if (count < expression.length - 1) {
throw new IllegalArgumentException("条件语句的?个数不能小于参数个数"); throw new IllegalArgumentException("条件语句的?个数不能小于参数个数");
} }
if (count > expression.length - 1) { if (count > expression.length - 1) {
throw new IllegalArgumentException("条件语句的?个数不能大于参数个数"); throw new IllegalArgumentException("条件语句的?个数不能大于参数个数");
} }
} }
/** /**
* 检查下载实体 * 检查下载实体
*/ */
public static void checkDownloadEntity(DownloadEntity entity) { public static void checkDownloadEntity(DownloadEntity entity) {
checkUrlInvalidThrow(entity.getUrl()); checkUrlInvalidThrow(entity.getUrl());
entity.setUrl(entity.getUrl()); entity.setUrl(entity.getUrl());
checkPath(entity.getDownloadPath()); checkPath(entity.getDownloadPath());
} }
/** /**
* 检测下载链接是否为null * 检测下载链接是否为null
*/ */
public static void checkPath(String path) { public static void checkPath(String path) {
if (TextUtils.isEmpty(path)) { if (TextUtils.isEmpty(path)) {
throw new IllegalArgumentException("保存路径不能为null"); throw new IllegalArgumentException("保存路径不能为null");
} }
} }
/** /**
* 检测url是否合法如果url不合法将抛异常 * 检测url是否合法如果url不合法将抛异常
*/ */
public static void checkUrlInvalidThrow(String url) { public static void checkUrlInvalidThrow(String url) {
if (TextUtils.isEmpty(url)) { if (TextUtils.isEmpty(url)) {
throw new IllegalArgumentException("url不能为null"); throw new IllegalArgumentException("url不能为null");
} else if (!url.startsWith("http") && !url.startsWith("ftp")) { } else if (!url.startsWith("http") && !url.startsWith("ftp")) {
throw new IllegalArgumentException("url错误"); throw new IllegalArgumentException("url错误");
} }
int index = url.indexOf("://"); int index = url.indexOf("://");
if (index == -1) { if (index == -1) {
throw new IllegalArgumentException("url不合法"); throw new IllegalArgumentException("url不合法");
} }
} }
/** /**
* 检测url是否合法 * 检测url是否合法如果url不合法将抛出{@link IllegalArgumentException}异常
* */
* @return {@code true} 合法{@code false} 非法 public static void checkUrl(String url) {
*/ if (TextUtils.isEmpty(url)) {
public static boolean checkUrl(String url) { throw new NullPointerException("url为空");
if (TextUtils.isEmpty(url)) { } else if (!url.startsWith("http") && !url.startsWith("ftp")) {
ALog.e(TAG, "url不能为null"); throw new IllegalArgumentException(String.format("url【%s】错误", url));
return false; }
} else if (!url.startsWith("http") && !url.startsWith("ftp")) { int index = url.indexOf("://");
ALog.e(TAG, "url【" + url + "】错误"); if (index == -1) {
return false; throw new IllegalArgumentException(String.format("url【%s】不合法", url));
} }
int index = url.indexOf("://"); }
if (index == -1) {
ALog.e(TAG, "url【" + url + "】不合法"); /**
} * 检测url是否合法
return true; *
} * @return {@code true} 合法{@code false} 非法
*/
/** public static boolean checkUrlNotThrow(String url) {
* 检测下载链接组是否为null if (TextUtils.isEmpty(url)) {
*/ ALog.e(TAG, "url不能为null");
public static void checkDownloadUrls(List<String> urls) { return false;
if (urls == null || urls.isEmpty()) { } else if (!url.startsWith("http") && !url.startsWith("ftp")) {
throw new IllegalArgumentException("链接组不能为null"); ALog.e(TAG, "url【" + url + "】错误");
} return false;
} }
int index = url.indexOf("://");
/** if (index == -1) {
* 检查下载任务组保存路径 ALog.e(TAG, "url【" + url + "】不合法");
*/ }
public static void checkDownloadPaths(List<String> paths) { return true;
if (paths == null || paths.isEmpty()) { }
throw new IllegalArgumentException("链接保存路径不能为null");
} /**
} * 检测下载链接组是否为null
*/
/** public static void checkDownloadUrls(List<String> urls) {
* 检测上传地址是否为null if (urls == null || urls.isEmpty()) {
*/ throw new IllegalArgumentException("链接组不能为null");
public static void checkUploadPath(String uploadPath) { }
if (TextUtils.isEmpty(uploadPath)) { }
throw new IllegalArgumentException("上传地址不能为null");
} /**
File file = new File(uploadPath); * 检查下载任务组保存路径
if (!file.exists()) { */
throw new IllegalArgumentException("上传文件不存在"); public static void checkDownloadPaths(List<String> paths) {
} if (paths == null || paths.isEmpty()) {
} throw new IllegalArgumentException("链接保存路径不能为null");
}
/** }
* 检查任务实体
*/ /**
public static void checkTaskEntity(AbsTaskWrapper entity) { * 检测上传地址是否为null
if (entity instanceof DTaskWrapper) { */
checkDownloadTaskEntity(((DTaskWrapper) entity).getEntity()); public static void checkUploadPath(String uploadPath) {
} else if (entity instanceof UTaskWrapper) { if (TextUtils.isEmpty(uploadPath)) {
checkUploadTaskEntity(((UTaskWrapper) entity).getEntity()); throw new IllegalArgumentException("上传地址不能为null");
} }
} File file = new File(uploadPath);
if (!file.exists()) {
/** throw new IllegalArgumentException("上传文件不存在");
* 检查命令实体 }
* }
* @param checkType 删除命令和停止命令不需要检查下载链接和保存路径
* @return {@code false}实体无效 /**
*/ * 检查任务实体
public static boolean checkCmdEntity(AbsTaskWrapper entity, boolean checkType) { */
boolean b = false; public static void checkTaskEntity(AbsTaskWrapper entity) {
if (entity instanceof DTaskWrapper) { if (entity instanceof DTaskWrapper) {
DownloadEntity entity1 = ((DTaskWrapper) entity).getEntity(); checkDownloadTaskEntity(((DTaskWrapper) entity).getEntity());
if (entity1 == null) { } else if (entity instanceof UTaskWrapper) {
ALog.e(TAG, "下载实体不能为空"); checkUploadTaskEntity(((UTaskWrapper) entity).getEntity());
} else if (checkType && TextUtils.isEmpty(entity1.getUrl())) { }
ALog.e(TAG, "下载链接不能为空"); }
} else if (checkType && TextUtils.isEmpty(entity1.getDownloadPath())) {
ALog.e(TAG, "保存路径不能为空"); /**
} else { * 检查命令实体
b = true; *
} * @param checkType 删除命令和停止命令不需要检查下载链接和保存路径
} else if (entity instanceof UTaskWrapper) { * @return {@code false}实体无效
UploadEntity entity1 = ((UTaskWrapper) entity).getEntity(); */
if (entity1 == null) { public static boolean checkCmdEntity(AbsTaskWrapper entity, boolean checkType) {
ALog.e(TAG, "上传实体不能为空"); boolean b = false;
} else if (TextUtils.isEmpty(entity1.getFilePath())) { if (entity instanceof DTaskWrapper) {
ALog.e(TAG, "上传文件路径不能为空"); DownloadEntity entity1 = ((DTaskWrapper) entity).getEntity();
} else { if (entity1 == null) {
b = true; ALog.e(TAG, "下载实体不能为空");
} } else if (checkType && TextUtils.isEmpty(entity1.getUrl())) {
} ALog.e(TAG, "下载链接不能为空");
return b; } else if (checkType && TextUtils.isEmpty(entity1.getDownloadPath())) {
} ALog.e(TAG, "保存路径不能为空");
} else {
/** b = true;
* 检查上传实体是否合法 }
*/ } else if (entity instanceof UTaskWrapper) {
private static void checkUploadTaskEntity(UploadEntity entity) { UploadEntity entity1 = ((UTaskWrapper) entity).getEntity();
if (entity == null) { if (entity1 == null) {
throw new NullPointerException("上传实体不能为空"); ALog.e(TAG, "上传实体不能为空");
} else if (TextUtils.isEmpty(entity.getFilePath())) { } else if (TextUtils.isEmpty(entity1.getFilePath())) {
throw new IllegalArgumentException("上传文件路径不能为空"); ALog.e(TAG, "上传文件路径不能为空");
} else if (TextUtils.isEmpty(entity.getFileName())) { } else {
throw new IllegalArgumentException("上传文件名不能为空"); b = true;
} }
} }
return b;
/** }
* 检测下载实体是否合法
* 合法(true) /**
* * 检查上传实体是否合法
* @param entity 下载实体 */
*/ private static void checkUploadTaskEntity(UploadEntity entity) {
private static void checkDownloadTaskEntity(DownloadEntity entity) { if (entity == null) {
if (entity == null) { throw new NullPointerException("上传实体不能为空");
throw new NullPointerException("下载实体不能为空"); } else if (TextUtils.isEmpty(entity.getFilePath())) {
} else if (TextUtils.isEmpty(entity.getUrl())) { throw new IllegalArgumentException("上传文件路径不能为空");
throw new IllegalArgumentException("下载链接不能为空"); } else if (TextUtils.isEmpty(entity.getFileName())) {
} else if (TextUtils.isEmpty(entity.getFileName())) { throw new IllegalArgumentException("上传文件名不能为空");
throw new NullPointerException("文件名不能为null"); }
} else if (TextUtils.isEmpty(entity.getDownloadPath())) { }
throw new NullPointerException("文件保存路径不能为null");
} /**
} * 检测下载实体是否合法
* 合法(true)
*
* @param entity 下载实体
*/
private static void checkDownloadTaskEntity(DownloadEntity entity) {
if (entity == null) {
throw new NullPointerException("下载实体不能为空");
} else if (TextUtils.isEmpty(entity.getUrl())) {
throw new IllegalArgumentException("下载链接不能为空");
} else if (TextUtils.isEmpty(entity.getFileName())) {
throw new NullPointerException("文件名不能为null");
} else if (TextUtils.isEmpty(entity.getDownloadPath())) {
throw new NullPointerException("文件保存路径不能为null");
}
}
} }

@ -45,13 +45,27 @@ public class DbDataHelper {
return records.get(0).taskRecord; return records.get(0).taskRecord;
} }
/**
* 获取组合任务实体ftpDir任务实体
*
* @param groupHash 组合任务Hash
* @return 实体不存在返回null
*/
public static DownloadGroupEntity getDGEntity(String groupHash) {
List<DGEntityWrapper> wrapper =
DbEntity.findRelationData(DGEntityWrapper.class, "DownloadGroupEntity.groupHash=?",
groupHash);
return wrapper == null ? null : wrapper.get(0).groupEntity;
}
/** /**
* 获取组合任务实体 如果数据库不存在该实体则新创建一个新的任务组实体 * 获取组合任务实体 如果数据库不存在该实体则新创建一个新的任务组实体
* *
* @param groupHash 组合任务Hash * @param groupHash 组合任务Hash
* @param urls 子任务url列表 * @param urls 子任务url列表
*/ */
public static DownloadGroupEntity getHttpDGEntity(String groupHash, List<String> urls) { public static DownloadGroupEntity getOrCreateHttpDGEntity(String groupHash, List<String> urls) {
List<DGEntityWrapper> wrapper = List<DGEntityWrapper> wrapper =
DbEntity.findRelationData(DGEntityWrapper.class, "DownloadGroupEntity.groupHash=?", DbEntity.findRelationData(DGEntityWrapper.class, "DownloadGroupEntity.groupHash=?",
groupHash); groupHash);
@ -96,7 +110,7 @@ public class DbDataHelper {
* *
* @param ftpUrl ftp下载地址 * @param ftpUrl ftp下载地址
*/ */
public static DownloadGroupEntity getFtpDGEntity(String ftpUrl) { public static DownloadGroupEntity getOrCreateFtpDGEntity(String ftpUrl) {
List<DGEntityWrapper> wrapper = List<DGEntityWrapper> wrapper =
DbEntity.findRelationData(DGEntityWrapper.class, "DownloadGroupEntity.groupHash=?", DbEntity.findRelationData(DGEntityWrapper.class, "DownloadGroupEntity.groupHash=?",
ftpUrl); ftpUrl);

@ -1,219 +1,226 @@
## 开发日志 ## 开发日志
+ v_3.6.4 + v_3.6.4
- 优化任务接收器的代码结构 - 优化任务接收器的代码结构
- 修复`DbEntity.saveAll()`失败的问题 - 修复`DbEntity.saveAll()`失败的问题
- 修复分块任务重命名失败的问题 - 修复分块任务重命名失败的问题
+ v_3.6.3 (2019/4/2) - fix bug https://github.com/AriaLyy/Aria/issues/379
- fix bug https://github.com/AriaLyy/Aria/issues/377 - 移除`getDownloadTask(String url)`、`getGroupTask(List<String> urls)`、`getFtpDirTask(String path)`
+ v_3.6.2 (2019/4/1) 等获取任务的api,如果你希望获取对应状态,请使用实体的状态判断,如:`getDownloadEntity()`、`getDownloadGroupEntity()`
- fix bug https://github.com/AriaLyy/Aria/issues/368 `getFtpDirEntity()`
- 增加gradle 5.0支持 - fix bug https://github.com/AriaLyy/Aria/issues/388
- fix bug https://github.com/AriaLyy/Aria/issues/374 - 修复使用`Content-Disposition`的文件名时,第一次下载无法重命名文件的问题
- 增加分页功能,详情见:https://aria.laoyuyu.me/aria_doc/api/task_list.html#%E4%BB%BB%E5%8A%A1%E5%88%97%E8%A1%A8%E5%88%86%E9%A1%B5%EF%BC%88362%E4%BB%A5%E4%B8%8A%E7%89%88%E6%9C%AC%E6%94%AF%E6%8C%81%EF%BC%89 - 修复使用`Content-Disposition`的文件名时,多次重命名文件的问题
+ v_3.6.1 (2019/3/5) + v_3.6.3 (2019/4/2)
- fix bug https://github.com/AriaLyy/Aria/issues/367 - fix bug https://github.com/AriaLyy/Aria/issues/377
+ v_3.6 (2019/2/27) + v_3.6.2 (2019/4/1)
- 优化数据库写入\修改的速度 - fix bug https://github.com/AriaLyy/Aria/issues/368
- 精减任务实体的存储 - 增加gradle 5.0支持
- 增加下载组合任务的配置 - fix bug https://github.com/AriaLyy/Aria/issues/374
- useBroadcast\notNetRetry这两个配置,统一在AppConfig中配置 - 增加分页功能,详情见:https://aria.laoyuyu.me/aria_doc/api/task_list.html#%E4%BB%BB%E5%8A%A1%E5%88%97%E8%A1%A8%E5%88%86%E9%A1%B5%EF%BC%88362%E4%BB%A5%E4%B8%8A%E7%89%88%E6%9C%AC%E6%94%AF%E6%8C%81%EF%BC%89
- fix bug https://github.com/AriaLyy/Aria/issues/361 + v_3.6.1 (2019/3/5)
- fix bug https://github.com/AriaLyy/Aria/issues/365 - fix bug https://github.com/AriaLyy/Aria/issues/367
+ v_3.5.4 (2019/1/8) + v_3.6 (2019/2/27)
- 修复不支持断点的下载地址,重复下载出现的数据库主键冲突问题 - 优化数据库写入\修改的速度
+ v_3.5.3 (2018/12/23) - 精减任务实体的存储
- 修复ftps不能加载默认证书的bug https://github.com/AriaLyy/Aria/issues/334 - 增加下载组合任务的配置
- 优化注解性能,移除不必要的判断代码 - useBroadcast\notNetRetry这两个配置,统一在AppConfig中配置
- 增加广播支持,详情见:http://aria.laoyuyu.me/aria_doc/api/use_broadcast.html - fix bug https://github.com/AriaLyy/Aria/issues/361
- 增加get参数支持 - fix bug https://github.com/AriaLyy/Aria/issues/365
```java + v_3.5.4 (2019/1/8)
Aria.download(SingleTaskActivity.this) - 修复不支持断点的下载地址,重复下载出现的数据库主键冲突问题
.load(DOWNLOAD_URL) // url 必须是主体url,也就是?前面的内容 + v_3.5.3 (2018/12/23)
.setFilePath(path, true) - 修复ftps不能加载默认证书的bug https://github.com/AriaLyy/Aria/issues/334
.asGet() - 优化注解性能,移除不必要的判断代码
.setParams(params) // 设置参数 - 增加广播支持,详情见:http://aria.laoyuyu.me/aria_doc/api/use_broadcast.html
.start(); - 增加get参数支持
``` ```java
- fix bug https://github.com/AriaLyy/Aria/issues/335 Aria.download(SingleTaskActivity.this)
- 新增进度百分比保存 https://github.com/AriaLyy/Aria/issues/336 .load(DOWNLOAD_URL) // url 必须是主体url,也就是?前面的内容
- fix bug https://github.com/AriaLyy/Aria/issues/335 .setFilePath(path, true)
+ v_3.5.2 .asGet()
- 添加Serializable接口支持 https://github.com/AriaLyy/Aria/issues/320 .setParams(params) // 设置参数
- 失败回调增加错误原因 https://github.com/AriaLyy/Aria/issues/310 .start();
``` ```
@Download.onTaskFail void taskFail(DownloadTask task, Exception e) { - fix bug https://github.com/AriaLyy/Aria/issues/335
e.getMessage(); - 新增进度百分比保存 https://github.com/AriaLyy/Aria/issues/336
... - fix bug https://github.com/AriaLyy/Aria/issues/335
} + v_3.5.2
``` - 添加Serializable接口支持 https://github.com/AriaLyy/Aria/issues/320
- fix bug https://github.com/AriaLyy/Aria/issues/322 - 失败回调增加错误原因 https://github.com/AriaLyy/Aria/issues/310
- 新增201 重定向支持 https://github.com/AriaLyy/Aria/issues/318 ```
- 修复使用`useServerFileName(true)`中含有`"`导致的文件后缀名错误问题 @Download.onTaskFail void taskFail(DownloadTask task, Exception e) {
- 优化logcat日志提示 e.getMessage();
- 修改下载线程的优先级为`Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);` ...
- fix bug https://github.com/AriaLyy/Aria/issues/319 }
- 修复分卡下载失败的问题 https://github.com/AriaLyy/Aria/issues/326 ```
- 初始化Aria时会将所有数据库状态为下载中的任务改为已停止,防止应用被kill后,任务状态错误 - fix bug https://github.com/AriaLyy/Aria/issues/322
- 初始化时自动判断文件是否被删除,文件被删除的任务将自动重置默认值 - 新增201 重定向支持 https://github.com/AriaLyy/Aria/issues/318
- 修复刷新url后,文件无法删除的 bug - 修复使用`useServerFileName(true)`中含有`"`导致的文件后缀名错误问题
- fix bug https://github.com/AriaLyy/Aria/issues/309 - 优化logcat日志提示
- 优化配置文件的读取 - 修改下载线程的优先级为`Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);`
+ v_3.5.1 - fix bug https://github.com/AriaLyy/Aria/issues/319
- 优化`taskExists`方法 - 修复分卡下载失败的问题 https://github.com/AriaLyy/Aria/issues/326
- 添加`post`参数请求支持 - 初始化Aria时会将所有数据库状态为下载中的任务改为已停止,防止应用被kill后,任务状态错误
```java - 初始化时自动判断文件是否被删除,文件被删除的任务将自动重置默认值
Aria.download(SingleTaskActivity.this) - 修复刷新url后,文件无法删除的 bug
.load(DOWNLOAD_URL) - fix bug https://github.com/AriaLyy/Aria/issues/309
.setFilePath(path) - 优化配置文件的读取
.asPost() // post请求 + v_3.5.1
.setParam("key", "value") //传递参数 - 优化`taskExists`方法
//.setParams(Map<String, String>) // 传递多参数 - 添加`post`参数请求支持
.start(); ```java
``` Aria.download(SingleTaskActivity.this)
- 增加强制设置文件路径的api, https://github.com/AriaLyy/Aria/issues/311 .load(DOWNLOAD_URL)
``` .setFilePath(path)
Aria.download(SingleTaskActivity.this) .asPost() // post请求
.load(DOWNLOAD_URL) .setParam("key", "value") //传递参数
.setFilePath(path, true) // true表示忽略路径是否被占用 //.setParams(Map<String, String>) // 传递多参数
.start(); .start();
``` ```
+ v_3.5 - 增加强制设置文件路径的api, https://github.com/AriaLyy/Aria/issues/311
- fix bug https://github.com/AriaLyy/Aria/issues/302 ```java
- fix bug https://github.com/AriaLyy/Aria/issues/283 Aria.download(SingleTaskActivity.this)
- fix bug https://github.com/AriaLyy/Aria/issues/305 .load(DOWNLOAD_URL)
- fix bug https://github.com/AriaLyy/Aria/issues/306 .setFilePath(path, true) // true表示忽略路径是否被占用
- fix bug https://github.com/AriaLyy/Aria/issues/272 (现在,停止所有任务,未开始的任务状态将变为停止) .start();
- fix bug https://github.com/AriaLyy/Aria/issues/277 ```
- fix bug https://github.com/AriaLyy/Aria/issues/303 + v_3.5
- 优化停止任务的速度 - fix bug https://github.com/AriaLyy/Aria/issues/302
- 修复组合任务修改子任务文件名失败的问题 - fix bug https://github.com/AriaLyy/Aria/issues/283
+ v_3.4.12 - fix bug https://github.com/AriaLyy/Aria/issues/305
- fix bug https://github.com/AriaLyy/Aria/issues/286 - fix bug https://github.com/AriaLyy/Aria/issues/306
- 优化线程池任务 - fix bug https://github.com/AriaLyy/Aria/issues/272 (现在,停止所有任务,未开始的任务状态将变为停止)
+ v_3.4.11 - fix bug https://github.com/AriaLyy/Aria/issues/277
- fix bug https://github.com/AriaLyy/Aria/issues/288 - fix bug https://github.com/AriaLyy/Aria/issues/303
- fix bug https://github.com/AriaLyy/Aria/issues/282 - 优化停止任务的速度
+ v_3.4.10 - 修复组合任务修改子任务文件名失败的问题
- fix bug https://github.com/AriaLyy/Aria/issues/280 + v_3.4.12
+ v_3.4.9 - fix bug https://github.com/AriaLyy/Aria/issues/286
- fix bug https://github.com/AriaLyy/Aria/issues/276 - 优化线程池任务
+ v_3.4.8 + v_3.4.11
- 组合任务新增`updateUrls(List<String>)`用于修改组合子任务的url,[see](https://aria.laoyuyu.me/aria_doc/api/update_url.html) - fix bug https://github.com/AriaLyy/Aria/issues/288
- 出于安全考虑,FTP数据库去掉密码的保存 - fix bug https://github.com/AriaLyy/Aria/issues/282
- 增加FTPS支持 [see](https://aria.laoyuyu.me/aria_doc/download/ftps.html) + v_3.4.10
- 增加速度限制支持[see](https://aria.laoyuyu.me/aria_doc/api/speed_handle.html) - fix bug https://github.com/AriaLyy/Aria/issues/280
- 增加内存空间不足验证 + v_3.4.9
+ v_3.4.7 - fix bug https://github.com/AriaLyy/Aria/issues/276
- 修复分块任务异常操作导致的问题 + v_3.4.8
+ v_3.4.6 - 组合任务新增`updateUrls(List<String>)`用于修改组合子任务的url,[see](https://aria.laoyuyu.me/aria_doc/api/update_url.html)
- 修复android 4.4.4 版本多dex下无法进行回调的问题 - 出于安全考虑,FTP数据库去掉密码的保存
- 新增`updateUrl(newUrl)`用于修改任务的url,[see](https://aria.laoyuyu.me/aria_doc/api/task_handle.html#%E6%9B%B4%E6%96%B0%E4%BB%BB%E5%8A%A1url) - 增加FTPS支持 [see](https://aria.laoyuyu.me/aria_doc/download/ftps.html)
- 优化分块下载 - 增加速度限制支持[see](https://aria.laoyuyu.me/aria_doc/api/speed_handle.html)
- 修复了字符串中有特殊字符导致的路径冲突问题;修复ftp分块下载失败问题 - 增加内存空间不足验证
- 修复连接中有`+`导致的地址呗使用问题。 + v_3.4.7
- 修复表重复创建导致的崩溃问题 https://github.com/AriaLyy/Aria/issues/264 - 修复分块任务异常操作导致的问题
+ v_3.4.4 + v_3.4.6
- 实现[多线程分块下载](https://aria.laoyuyu.me/aria_doc/start/config.html) - 修复android 4.4.4 版本多dex下无法进行回调的问题
- 修复`stopAll()`和`resumeAll()`导致的进度为0问题 - 新增`updateUrl(newUrl)`用于修改任务的url,[see](https://aria.laoyuyu.me/aria_doc/api/task_handle.html#%E6%9B%B4%E6%96%B0%E4%BB%BB%E5%8A%A1url)
- 修复任务组添加header无效的问题 - 优化分块下载
+ v_3.4.3 - 修复了字符串中有特殊字符导致的路径冲突问题;修复ftp分块下载失败问题
- 修复在activity 的onStop中取消注册导致的内存泄露问题 - 修复连接中有`+`导致的地址呗使用问题。
- fix bug https://github.com/AriaLyy/Aria/issues/258 - 修复表重复创建导致的崩溃问题 https://github.com/AriaLyy/Aria/issues/264
- fix bug https://github.com/AriaLyy/Aria/issues/259 + v_3.4.4
+ v_3.4.2 - 实现[多线程分块下载](https://aria.laoyuyu.me/aria_doc/start/config.html)
- fix bug https://github.com/AriaLyy/Aria/issues/248 - 修复`stopAll()`和`resumeAll()`导致的进度为0问题
- fix bug https://github.com/AriaLyy/Aria/issues/247 - 修复任务组添加header无效的问题
- fix bug https://github.com/AriaLyy/Aria/issues/250 + v_3.4.3
- 添加任务判断是否存在的api - 修复在activity 的onStop中取消注册导致的内存泄露问题
- 添加代理api - fix bug https://github.com/AriaLyy/Aria/issues/258
- 修复删除所有没有进出等待的问题 - fix bug https://github.com/AriaLyy/Aria/issues/259
- 进度有时出错的问题 + v_3.4.2
- FTP添加超时处理 - fix bug https://github.com/AriaLyy/Aria/issues/248
+ v_3.4.1 - fix bug https://github.com/AriaLyy/Aria/issues/247
- 移除记录配置文件,改用数据库记录任务记录 - fix bug https://github.com/AriaLyy/Aria/issues/250
- 上传配置添加io超时时间、缓存大小配置 - 添加任务判断是否存在的api
- 添加没有网络也会重试的开关 - 添加代理api
- 修复多次删除记录的bug - 修复删除所有没有进出等待的问题
- 文件长度现在可动态增加,详情见 https://aria.laoyuyu.me/aria_doc/start/config.html - 进度有时出错的问题
- 修复多module同时引用Aria导致打正式包出错的问题 https://github.com/AriaLyy/Aria/issues/240 - FTP添加超时处理
+ v_3.4 + v_3.4.1
- 优化大量代码 - 移除记录配置文件,改用数据库记录任务记录
- 重构Aria的ORM模型,提高了数据读取的可靠性和读写速度 - 上传配置添加io超时时间、缓存大小配置
- 现在可在任意类中使用Aria了,[使用方法](http://aria.laoyuyu.me/aria_doc/start/any_java.html) - 添加没有网络也会重试的开关
- 添加`window.location.replace("http://xxxx")`类型的网页重定向支持 - 修复多次删除记录的bug
- 支持gzip、deflate 压缩类型的输入流 - 文件长度现在可动态增加,详情见 https://aria.laoyuyu.me/aria_doc/start/config.html
- 添加`useServerFileName`,可使用服务端响应header的`Content-Disposition`携带的文件名 - 修复多module同时引用Aria导致打正式包出错的问题 https://github.com/AriaLyy/Aria/issues/240
+ v_3.3.16 + v_3.4
- 修复一个activity启动多次,无法进行回掉的bug https://github.com/AriaLyy/Aria/issues/200 - 优化大量代码
- 优化target代码结构,移除路径被占用的提示 - 重构Aria的ORM模型,提高了数据读取的可靠性和读写速度
- 添加支持chunked模式的下载 - 现在可在任意类中使用Aria了,[使用方法](http://aria.laoyuyu.me/aria_doc/start/any_java.html)
- 去掉上一个版本"//"的限制 - 添加`window.location.replace("http://xxxx")`类型的网页重定向支持
+ v_3.3.14 - 支持gzip、deflate 压缩类型的输入流
- 修复ftp上传和下载的兼容性问题 - 添加`useServerFileName`,可使用服务端响应header的`Content-Disposition`携带的文件名
- 如果url中的path有"//"将替换为"/" + v_3.3.16
- 修复http上传成功后,如果服务器没有设置返回码导致上传失败的问题 - 修复一个activity启动多次,无法进行回掉的bug https://github.com/AriaLyy/Aria/issues/200
- 上传实体UploadEntity增加responseStr字段,http上传完成后,在被`@Upload.onComplete`注解的方法中,可通过`task.getEntity().getResponseStr())`获取服务器返回的数据 - 优化target代码结构,移除路径被占用的提示
- 如果服务器存在已上传了一部分的文件,用户执行删除该FTP上传任务,再次重新上传,会出现550,权限错误;本版本已修复该问题 - 添加支持chunked模式的下载
+ v_3.3.13 - 去掉上一个版本"//"的限制
- 添加`@Download.onWait、@Upload.onWait、@DownloadGroup.onWait`三个新注解,队列已经满了,继续创建新任务,任务处于等待中,将会执行被这三个注解标志的方法 + v_3.3.14
- app被kill,但是还存在等待中的任务A;第二次重新启动,先创建一个新的任务B,Aria会自动把B放进等待队列中,这时再次创建任务A,会导致重复下载,进度错乱的问题;本版本已修复这个问题 - 修复ftp上传和下载的兼容性问题
+ v_3.3.11 - 如果url中的path有"//"将替换为"/"
- 添加进度更新间隔api,在`aria_config.xml`配置`<updateInterval value="1000"/>`或在代码中调用 - 修复http上传成功后,如果服务器没有设置返回码导致上传失败的问题
`AriaManager.getInstance(AriaManager.APP).getDownloadConfig().setUpdateInterval(3000)`便可以改变进度刷新间隔 - 上传实体UploadEntity增加responseStr字段,http上传完成后,在被`@Upload.onComplete`注解的方法中,可通过`task.getEntity().getResponseStr())`获取服务器返回的数据
- 修复下载过程中kill进程可能出现的文件错误的问题 https://github.com/AriaLyy/Aria/issues/192 - 如果服务器存在已上传了一部分的文件,用户执行删除该FTP上传任务,再次重新上传,会出现550,权限错误;本版本已修复该问题
- 修复http上传的空指针问题 https://github.com/AriaLyy/Aria/issues/193 + v_3.3.13
- 修复下载地址中含有`'`导致的崩溃问题 https://github.com/AriaLyy/Aria/issues/194 - 添加`@Download.onWait、@Upload.onWait、@DownloadGroup.onWait`三个新注解,队列已经满了,继续创建新任务,任务处于等待中,将会执行被这三个注解标志的方法
+ v_3.3.10 - app被kill,但是还存在等待中的任务A;第二次重新启动,先创建一个新的任务B,Aria会自动把B放进等待队列中,这时再次创建任务A,会导致重复下载,进度错乱的问题;本版本已修复这个问题
- 修复地址切换导致下载失败的问题 https://github.com/AriaLyy/Aria/issues/181 + v_3.3.11
- 添加重置状态的api,当下载信息不改变,只是替换了服务器的对应的文件,可用`Aria.download(this).load(url).resetState()`重置下载状态 https://github.com/AriaLyy/Aria/issues/182 - 添加进度更新间隔api,在`aria_config.xml`配置`<updateInterval value="1000"/>`或在代码中调用
+ v_3.3.9 `AriaManager.getInstance(AriaManager.APP).getDownloadConfig().setUpdateInterval(3000)`便可以改变进度刷新间隔
- 添加POST支持 - 修复下载过程中kill进程可能出现的文件错误的问题 https://github.com/AriaLyy/Aria/issues/192
- 任务执行的过程中,如果调用removeRecord()方法,将会取消任务 https://github.com/AriaLyy/Aria/issues/174 - 修复http上传的空指针问题 https://github.com/AriaLyy/Aria/issues/193
- 修复一个数据库初始化的问题 https://github.com/AriaLyy/Aria/issues/173 - 修复下载地址中含有`'`导致的崩溃问题 https://github.com/AriaLyy/Aria/issues/194
- 修复head头部信息过长时出现的崩溃问题 https://github.com/AriaLyy/Aria/issues/177 + v_3.3.10
+ v_3.3.7 - 修复地址切换导致下载失败的问题 https://github.com/AriaLyy/Aria/issues/181
- 修复一个线程重启的问题 https://github.com/AriaLyy/Aria/issues/160 - 添加重置状态的api,当下载信息不改变,只是替换了服务器的对应的文件,可用`Aria.download(this).load(url).resetState()`重置下载状态 https://github.com/AriaLyy/Aria/issues/182
- 修复配置文件异常问题、格式化速度为0问题 https://github.com/AriaLyy/Aria/issues/161 + v_3.3.9
+ v_3.3.6 - 添加POST支持
- 增加日志输出级别控制 - 任务执行的过程中,如果调用removeRecord()方法,将会取消任务 https://github.com/AriaLyy/Aria/issues/174
- 修复公网FTP地址不能下载的问题 https://github.com/AriaLyy/Aria/issues/146 - 修复一个数据库初始化的问题 https://github.com/AriaLyy/Aria/issues/173
- 修复http下载地址有空格的时候下载失败的问题 https://github.com/AriaLyy/Aria/issues/131 - 修复head头部信息过长时出现的崩溃问题 https://github.com/AriaLyy/Aria/issues/177
- 修复Activity在`onDestroy()`中调用`Aria.download(this).unRegister();`导致回调失效的问题 + v_3.3.7
- 修复Adapter下载FTP任务问题、任务调度问题 https://github.com/AriaLyy/Aria/issues/157 - 修复一个线程重启的问题 https://github.com/AriaLyy/Aria/issues/160
- 优化代码,优化了IO性能 - 修复配置文件异常问题、格式化速度为0问题 https://github.com/AriaLyy/Aria/issues/161
+ v_3.3.5 修复任务组、上传任务无法启动的bug + v_3.3.6
+ v_3.3.4 优化任务代码结构,修复上一个版本暂停后无法自动执行任务的问题 - 增加日志输出级别控制
+ v_3.3.3 修复进度条错乱的问题,修复同一时间多次调用start导致重复下载的问题 - 修复公网FTP地址不能下载的问题 https://github.com/AriaLyy/Aria/issues/146
+ v_3.3.2 新加reTry(),修复上一个版本不会回调失败事件的问题;增加running状态下5秒钟保存一次数据库的功能;修复FTP断点上传文件不完整的问题 - 修复http下载地址有空格的时候下载失败的问题 https://github.com/AriaLyy/Aria/issues/131
+ v_3.3.1 增加网络事件,网络未连接,将不会重试下载,修复删除未开始任务,状态回调错误 - 修复Activity在`onDestroy()`中调用`Aria.download(this).unRegister();`导致回调失效的问题
+ v_3.3.0 增加任务组子任务暂停和开始控制功能、修复5.0系统以上数据库多生成两个字段的bug、去掉addSchedulerListener事件 - 修复Adapter下载FTP任务问题、任务调度问题 https://github.com/AriaLyy/Aria/issues/157
+ v_3.2.26 修复任务组有时注解不起作用的问题 - 优化代码,优化了IO性能
+ v_3.2.25 修复删除任务组文件,记录无法删除的问题 + v_3.3.5 修复任务组、上传任务无法启动的bug
+ v_3.2.17 修复一个版本兼容性问题,线程中使用Aria出错问题 + v_3.3.4 优化任务代码结构,修复上一个版本暂停后无法自动执行任务的问题
+ v_3.2.15 修复大型文件分段下载失败的问题,修复中文URL乱码问题 + v_3.3.3 修复进度条错乱的问题,修复同一时间多次调用start导致重复下载的问题
+ v_3.2.14 修复恢复所有任务的api接口,不能恢复下载组任务的问题 + v_3.3.2 新加reTry(),修复上一个版本不会回调失败事件的问题;增加running状态下5秒钟保存一次数据库的功能;修复FTP断点上传文件不完整的问题
+ v_3.2.13 修复某些服务器头文件返回描述文件格式错误的问题、修复有时删除任务,需要两次删除的问题 + v_3.3.1 增加网络事件,网络未连接,将不会重试下载,修复删除未开始任务,状态回调错误
+ v_3.2.12 实现FTP多线程断点续传下载,FTP断点续传上传功能 + v_3.3.0 增加任务组子任务暂停和开始控制功能、修复5.0系统以上数据库多生成两个字段的bug、去掉addSchedulerListener事件
+ v_3.2.9 修复任务组下载完成两次回掉的问题,修复又是获取不到下载状态的问题 + v_3.2.26 修复任务组有时注解不起作用的问题
+ v_3.2.8 修复下载超过2G大小的文件失败的问题 + v_3.2.25 修复删除任务组文件,记录无法删除的问题
+ v_3.2.7 移除设置文件名的api接口,修复断开网络时出现的进度条错误的问题 + v_3.2.17 修复一个版本兼容性问题,线程中使用Aria出错问题
+ v_3.2.6 移除广播事件,增加任务组下载功能 + v_3.2.15 修复大型文件分段下载失败的问题,修复中文URL乱码问题
+ v_3.1.9 修复stopAll队列没有任务时崩溃的问题,增加针对单个任务监听的功能 + v_3.2.14 修复恢复所有任务的api接口,不能恢复下载组任务的问题
+ v_3.1.7 修复某些文件下载不了的bug,增加apt注解方法,事件获取更加简单了 + v_3.2.13 修复某些服务器头文件返回描述文件格式错误的问题、修复有时删除任务,需要两次删除的问题
+ v_3.1.6 取消任务时onTaskCancel回调两次的bug + v_3.2.12 实现FTP多线程断点续传下载,FTP断点续传上传功能
+ v_3.1.5 优化代码结构,增加优先下载任务功能。 + v_3.2.9 修复任务组下载完成两次回掉的问题,修复又是获取不到下载状态的问题
+ v_3.1.4 修复快速切换,暂停、恢复功能时,概率性出现的重新下载问题,添加onPre()回调,onPre()用于请求地址之前执行界面UI更新操作。 + v_3.2.8 修复下载超过2G大小的文件失败的问题
+ v_3.1.0 添加Aria配置文件,优化代码 + v_3.2.7 移除设置文件名的api接口,修复断开网络时出现的进度条错误的问题
+ v_3.0.3 修复暂停后删除任务,闪退问题,添加删除记录的api + v_3.2.6 移除广播事件,增加任务组下载功能
+ v_3.0.2 支持30x重定向链接下载 + v_3.1.9 修复stopAll队列没有任务时崩溃的问题,增加针对单个任务监听的功能
+ v_3.0.0 添加上传任务支持,修复一些已发现的bug + v_3.1.7 修复某些文件下载不了的bug,增加apt注解方法,事件获取更加简单了
+ v_2.4.4 修复不支持断点的下载链接拿不到文件大小的问题 + v_3.1.6 取消任务时onTaskCancel回调两次的bug
+ v_2.4.3 修复404链接卡顿的问题 + v_3.1.5 优化代码结构,增加优先下载任务功能。
+ v_2.4.2 修复失败重试无效的bug + v_3.1.4 修复快速切换,暂停、恢复功能时,概率性出现的重新下载问题,添加onPre()回调,onPre()用于请求地址之前执行界面UI更新操作。
+ v_2.4.1 修复下载慢的问题,修复application、service 不能使用的问题 + v_3.1.0 添加Aria配置文件,优化代码
+ v_2.4.0 支持https链接下载 + v_3.0.3 修复暂停后删除任务,闪退问题,添加删除记录的api
+ v_2.3.8 修复数据错乱的bug、添加fragment支持 + v_3.0.2 支持30x重定向链接下载
+ v_2.3.6 添加dialog、popupWindow支持 + v_3.0.0 添加上传任务支持,修复一些已发现的bug
+ v_2.3.3 添加断点支持、修改下载逻辑,让使用更加简单、修复一个内存泄露的bug + v_2.4.4 修复不支持断点的下载链接拿不到文件大小的问题
+ v_2.3.1 重命名为Aria,下载流程简化 + v_2.4.3 修复404链接卡顿的问题
+ v_2.4.2 修复失败重试无效的bug
+ v_2.4.1 修复下载慢的问题,修复application、service 不能使用的问题
+ v_2.4.0 支持https链接下载
+ v_2.3.8 修复数据错乱的bug、添加fragment支持
+ v_2.3.6 添加dialog、popupWindow支持
+ v_2.3.3 添加断点支持、修改下载逻辑,让使用更加简单、修复一个内存泄露的bug
+ v_2.3.1 重命名为Aria,下载流程简化
+ v_2.1.1 增加,选择最大下载任务数接口 + v_2.1.1 增加,选择最大下载任务数接口

@ -1,172 +1,172 @@
# Aria # Aria
![图标](https://github.com/AriaLyy/DownloadUtil/blob/v_3.0/app/src/main/res/mipmap-hdpi/ic_launcher.png)</br> ![图标](https://github.com/AriaLyy/DownloadUtil/blob/v_3.0/app/src/main/res/mipmap-hdpi/ic_launcher.png)</br>
## [ENGLISH DOC](https://github.com/AriaLyy/Aria/blob/master/ENGLISH_README.md)</br> ## [ENGLISH DOC](https://github.com/AriaLyy/Aria/blob/master/ENGLISH_README.md)</br>
## [中文文档](https://aria.laoyuyu.me/aria_doc) ## [中文文档](https://aria.laoyuyu.me/aria_doc)
Aria项目源于工作中遇到的一个文件下载管理的需求,当时被下载折磨的痛不欲生,从那时起便萌生了编写一个简单易用,稳当高效的下载框架,aria经历了1.0到3.0的开发,算是越来越接近当初所制定的目标了。 Aria项目源于工作中遇到的一个文件下载管理的需求,当时被下载折磨的痛不欲生,从那时起便萌生了编写一个简单易用,稳当高效的下载框架,aria经历了1.0到3.0的开发,算是越来越接近当初所制定的目标了。
Aria有以下特点: Aria有以下特点:
+ 简单、方便 + 简单、方便
- 可以在Activity、Service、Fragment、Dialog、popupWindow、Notification等组件中使用 - 可以在Activity、Service、Fragment、Dialog、popupWindow、Notification等组件中使用
- 支持HTTP\FTP断点续传下载、多任务自动调度 - 支持HTTP\FTP断点续传下载、多任务自动调度
- 支持多文件打包下载,多文件共享同一进度(如:视频 + 封面 + 字幕) - 支持多文件打包下载,多文件共享同一进度(如:视频 + 封面 + 字幕)
- 支持下载FTP文件夹 - 支持下载FTP文件夹
- 支持HTTP表单上传 - 支持HTTP表单上传
- 支持文件FTP断点续传上传 - 支持文件FTP断点续传上传
- 支持FTPS/SFTP断点续传,[see](https://aria.laoyuyu.me/aria_doc/download/ftps.html) - 支持FTPS/SFTP断点续传,[see](https://aria.laoyuyu.me/aria_doc/download/ftps.html)
+ 支持https地址下载 + 支持https地址下载
- 在配置文件中很容易就可以设置CA证书的信息 - 在配置文件中很容易就可以设置CA证书的信息
+ 支持[多线程分块下载](https://aria.laoyuyu.me/aria_doc/start/config.html),能更有效的发挥机器IO性能 + 支持[多线程分块下载](https://aria.laoyuyu.me/aria_doc/start/config.html),能更有效的发挥机器IO性能
+ 支持300、301、302重定向下载链接下载 + 支持300、301、302重定向下载链接下载
+ 下载支持文件长度动态增加,文件下载初始化时将不再占用过多的内存空间,见[动态长度配置](https://aria.laoyuyu.me/aria_doc/start/config.html#%E4%B8%8B%E8%BD%BD%E5%8A%A8%E6%80%81%E6%96%87%E4%BB%B6%E8%AF%B4%E6%98%8E) + 下载支持文件长度动态增加,文件下载初始化时将不再占用过多的内存空间,见[动态长度配置](https://aria.laoyuyu.me/aria_doc/start/config.html#%E4%B8%8B%E8%BD%BD%E5%8A%A8%E6%80%81%E6%96%87%E4%BB%B6%E8%AF%B4%E6%98%8E)
[怎样使用Aria?](#使用) [怎样使用Aria?](#使用)
如果你觉得Aria对你有帮助,您的star和issues将是对我最大支持.`^_^` 如果你觉得Aria对你有帮助,您的star和issues将是对我最大支持.`^_^`
## 示例 ## 示例
* 多任务下载 * 多任务下载
![多任务下载](https://github.com/AriaLyy/DownloadUtil/blob/master/img/download_img.gif) ![多任务下载](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/max_speed.gif)
* 多文件打包下载 * 多文件打包下载
<img src="https://github.com/AriaLyy/DownloadUtil/blob/master/img/group_task.gif" width="360" height="640"/> <img src="https://github.com/AriaLyy/DownloadUtil/blob/master/img/group_task.gif" width="360" height="640"/>
## 引入库 ## 引入库
[![Core](https://api.bintray.com/packages/arialyy/maven/AriaApi/images/download.svg)](https://bintray.com/arialyy/maven/AriaApi/_latestVersion) [![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) [![Compiler](https://api.bintray.com/packages/arialyy/maven/AriaCompiler/images/download.svg)](https://bintray.com/arialyy/maven/AriaCompiler/_latestVersion)
```java ```java
compile 'com.arialyy.aria:aria-core:3.6.3' compile 'com.arialyy.aria:aria-core:3.6.3'
annotationProcessor 'com.arialyy.aria:aria-compiler:3.6.3' annotationProcessor 'com.arialyy.aria:aria-compiler:3.6.3'
``` ```
如果出现android support依赖错误,请将 `compile 'com.arialyy.aria:aria-core:<last-version>'`替换为 如果出现android support依赖错误,请将 `compile 'com.arialyy.aria:aria-core:<last-version>'`替换为
``` ```
api('com.arialyy.aria:aria-core:<last-version>'){ api('com.arialyy.aria:aria-core:<last-version>'){
exclude group: 'com.android.support' exclude group: 'com.android.support'
} }
``` ```
如果你使用的是kotlin,请使用kotlin官方提供的方法配置apt,[kotlin kapt官方配置传送门](https://www.kotlincn.net/docs/reference/kapt.html) 如果你使用的是kotlin,请使用kotlin官方提供的方法配置apt,[kotlin kapt官方配置传送门](https://www.kotlincn.net/docs/reference/kapt.html)
__注意:3.5.4以下版本升级时,需要更新[配置文件]!!(https://aria.laoyuyu.me/aria_doc/start/config.html)__ __注意:3.5.4以下版本升级时,需要更新[配置文件]!!(https://aria.laoyuyu.me/aria_doc/start/config.html)__
*** ***
## 使用 ## 使用
由于Aria涉及到文件和网络的操作,因此需要你在manifest文件中添加以下权限,如果你希望在6.0以上的系统中使用Aria,那么你需要动态向安卓系统申请文件系统读写权限,[如何使用安卓系统权限](https://developer.android.com/training/permissions/index.html?hl=zh-cn) 由于Aria涉及到文件和网络的操作,因此需要你在manifest文件中添加以下权限,如果你希望在6.0以上的系统中使用Aria,那么你需要动态向安卓系统申请文件系统读写权限,[如何使用安卓系统权限](https://developer.android.com/training/permissions/index.html?hl=zh-cn)
```xml ```xml
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
``` ```
## 使用Aria ## 使用Aria
### 基本使用 ### 基本使用
例子为单任务下载,只需要很简单的代码,便可以实现下载功能 例子为单任务下载,只需要很简单的代码,便可以实现下载功能
```java ```java
Aria.download(this) Aria.download(this)
.load(DOWNLOAD_URL) //读取下载地址 .load(DOWNLOAD_URL) //读取下载地址
.setDownloadPath(DOWNLOAD_PATH) //设置文件保存的完整路径 .setDownloadPath(DOWNLOAD_PATH) //设置文件保存的完整路径
.start(); //启动下载 .start(); //启动下载
``` ```
### 任务状态的获取 ### 任务状态的获取
基于解耦合的考虑,Aria的下载功能是和状态获取相分离的,状态的获取并不会集成到链式代码中,但是Aria提供了另一种更简单更灵活的方案。 基于解耦合的考虑,Aria的下载功能是和状态获取相分离的,状态的获取并不会集成到链式代码中,但是Aria提供了另一种更简单更灵活的方案。
通过注解,你可以很容易获取任务的所有状态。 通过注解,你可以很容易获取任务的所有状态。
1. 将对象注册到Aria 1. 将对象注册到Aria
```java ```java
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
Aria.download(this).register(); Aria.download(this).register();
} }
``` ```
2. 通过注解获取任务执行状态 2. 通过注解获取任务执行状态
**注意:** **注意:**
- 注解回掉采用Apt的方式实现,所以,你不需要担心这会影响你机器的性能 - 注解回掉采用Apt的方式实现,所以,你不需要担心这会影响你机器的性能
- 被注解的方法**不能被private修饰** - 被注解的方法**不能被private修饰**
- 被注解的方法**只能有一个参数,并且参数类型必须是`DownloadTask`或`UploadTask`或`DownloadGroupTask`** - 被注解的方法**只能有一个参数,并且参数类型必须是`DownloadTask`或`UploadTask`或`DownloadGroupTask`**
- 方法名可以为任意字符串 - 方法名可以为任意字符串
```java ```java
//在这里处理任务执行中的状态,如进度进度条的刷新 //在这里处理任务执行中的状态,如进度进度条的刷新
@Download.onTaskRunning protected void running(DownloadTask task) { @Download.onTaskRunning protected void running(DownloadTask task) {
if(task.getUrl().eques(url)){ if(task.getKey().eques(url)){
.... ....
可以通过url判断是否是指定任务的回调 可以通过url判断是否是指定任务的回调
} }
int p = task.getPercent(); //任务进度百分比 int p = task.getPercent(); //任务进度百分比
String speed = task.getConvertSpeed(); //转换单位后的下载速度,单位转换需要在配置文件中打开 String speed = task.getConvertSpeed(); //转换单位后的下载速度,单位转换需要在配置文件中打开
String speed1 = task.getSpeed(); //原始byte长度速度 String speed1 = task.getSpeed(); //原始byte长度速度
} }
@Download.onTaskComplete void taskComplete(DownloadTask task) { @Download.onTaskComplete void taskComplete(DownloadTask task) {
//在这里处理任务完成的状态 //在这里处理任务完成的状态
} }
``` ```
### 版本日志 ### 版本日志
* v3.6.3 (2019/4/2) * v3.6.3 (2019/4/2)
- fix bug https://github.com/AriaLyy/Aria/issues/368 - fix bug https://github.com/AriaLyy/Aria/issues/368
- 增加gradle 5.0支持 - 增加gradle 5.0支持
- fix bug https://github.com/AriaLyy/Aria/issues/374 - fix bug https://github.com/AriaLyy/Aria/issues/374
- fix bug https://github.com/AriaLyy/Aria/issues/377 - fix bug https://github.com/AriaLyy/Aria/issues/377
- 增加分页获取任务列表api, 详情见:https://aria.laoyuyu.me/aria_doc/api/task_list.html#%E4%BB%BB%E5%8A%A1%E5%88%97%E8%A1%A8%E5%88%86%E9%A1%B5%EF%BC%88362%E4%BB%A5%E4%B8%8A%E7%89%88%E6%9C%AC%E6%94%AF%E6%8C%81%EF%BC%89 - 增加分页获取任务列表api, 详情见:https://aria.laoyuyu.me/aria_doc/api/task_list.html#%E4%BB%BB%E5%8A%A1%E5%88%97%E8%A1%A8%E5%88%86%E9%A1%B5%EF%BC%88362%E4%BB%A5%E4%B8%8A%E7%89%88%E6%9C%AC%E6%94%AF%E6%8C%81%EF%BC%89
[更多版本记录](https://github.com/AriaLyy/Aria/blob/master/DEV_LOG.md) [更多版本记录](https://github.com/AriaLyy/Aria/blob/master/DEV_LOG.md)
## 混淆配置 ## 混淆配置
``` ```
-dontwarn com.arialyy.aria.** -dontwarn com.arialyy.aria.**
-keep class com.arialyy.aria.**{*;} -keep class com.arialyy.aria.**{*;}
-keep class **$$DownloadListenerProxy{ *; } -keep class **$$DownloadListenerProxy{ *; }
-keep class **$$UploadListenerProxy{ *; } -keep class **$$UploadListenerProxy{ *; }
-keep class **$$DownloadGroupListenerProxy{ *; } -keep class **$$DownloadGroupListenerProxy{ *; }
-keepclasseswithmembernames class * { -keepclasseswithmembernames class * {
@Download.* <methods>; @Download.* <methods>;
@Upload.* <methods>; @Upload.* <methods>;
@DownloadGroup.* <methods>; @DownloadGroup.* <methods>;
} }
``` ```
## 其他 ## 其他
有任何问题,可以在[issues](https://github.com/AriaLyy/Aria/issues)给我留言反馈。</br> 有任何问题,可以在[issues](https://github.com/AriaLyy/Aria/issues)给我留言反馈。</br>
在提交问题前,希望你已经查看过[wiki](https://aria.laoyuyu.me/aria_doc/)或搜索过[issues](https://github.com/AriaLyy/Aria/issues)。</br> 在提交问题前,希望你已经查看过[wiki](https://aria.laoyuyu.me/aria_doc/)或搜索过[issues](https://github.com/AriaLyy/Aria/issues)。</br>
交流群:524329160 交流群:524329160
*** ***
License License
------- -------
Copyright 2016 AriaLyy(https://github.com/AriaLyy/Aria) Copyright 2016 AriaLyy(https://github.com/AriaLyy/Aria)
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.

@ -1,161 +1,161 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<aria> <aria>
<!--注意,修改该配置文件中的属性会覆盖代码中所设置的属性--> <!--注意,修改该配置文件中的属性会覆盖代码中所设置的属性-->
<!--Aria框架配置--> <!--Aria框架配置-->
<app> <app>
<!--是否使用AriaCrashHandler来捕获异常,异常日志保存在:/mnt/sdcard/Android/data/{package_name}/files/log/--> <!--是否使用AriaCrashHandler来捕获异常,异常日志保存在:/mnt/sdcard/Android/data/{package_name}/files/log/-->
<useAriaCrashHandler value="true"/> <useAriaCrashHandler value="true"/>
<!--设置Aria的日志级别,{@link ALog#LOG_LEVEL_VERBOSE}--> <!--设置Aria的日志级别,{@link ALog#LOG_LEVEL_VERBOSE}-->
<logLevel value="2"/> <logLevel value="2"/>
<!-- 是否检查网络 true: 检查网络,false: 不检查网络--> <!-- 是否检查网络 true: 检查网络,false: 不检查网络-->
<netCheck value="false"/> <netCheck value="false"/>
<!--除非无法使用注解,否则不建议使用广播来接受任务状态,true:使用广播接收任务状态,false:不适用广播接收状态 --> <!--除非无法使用注解,否则不建议使用广播来接受任务状态,true:使用广播接收任务状态,false:不适用广播接收状态 -->
<!-- http://aria.laoyuyu.me/aria_doc/api/use_broadcast.html --> <!-- http://aria.laoyuyu.me/aria_doc/api/use_broadcast.html -->
<useBroadcast value="true"/> <useBroadcast value="true"/>
<!--断网的时候是否重试,true:断网也重试;false:断网不重试,直接走失败的回调--> <!--断网的时候是否重试,true:断网也重试;false:断网不重试,直接走失败的回调-->
<notNetRetry value="true"/> <notNetRetry value="true"/>
</app> </app>
<!--普通下载任务--> <!--普通下载任务-->
<download> <download>
<!--设置任务最大下载速度,0表示不限速,单位为:kb--> <!--设置任务最大下载速度,0表示不限速,单位为:kb-->
<maxSpeed value="128"/> <maxSpeed value="128"/>
<!-- <!--
多线程下载是否使用块下载模式,{@code true}使用,{@code false}不使用 多线程下载是否使用块下载模式,{@code true}使用,{@code false}不使用
注意: 注意:
1、使用分块模式,在I/O性能底下的手机上,合并文件需要的时间会更加长; 1、使用分块模式,在I/O性能底下的手机上,合并文件需要的时间会更加长;
2、优点是使用多线程的块下载,初始化时,文件初始化时将不会预占用对应长度的空间; 2、优点是使用多线程的块下载,初始化时,文件初始化时将不会预占用对应长度的空间;
3、只对新的多线程下载任务有效 3、只对新的多线程下载任务有效
4、只对多线程的任务有效 4、只对多线程的任务有效
--> -->
<useBlock value="true"/> <useBlock value="true"/>
<!--设置下载线程数,下载线程数不能小于1 <!--设置下载线程数,下载线程数不能小于1
注意: 注意:
1、线程下载数改变后,新的下载任务才会生效; 1、线程下载数改变后,新的下载任务才会生效;
2、如果任务大小小于1m,该设置不会生效; 2、如果任务大小小于1m,该设置不会生效;
3、从3.4.1开始,如果线程数为1,文件初始化时将不再预占用对应长度的空间,下载多少byte,则占多大的空间; 3、从3.4.1开始,如果线程数为1,文件初始化时将不再预占用对应长度的空间,下载多少byte,则占多大的空间;
对于3.4.1之前版本的未完成的老任务,依然采用原来的文件空间占用方式; 对于3.4.1之前版本的未完成的老任务,依然采用原来的文件空间占用方式;
--> -->
<threadNum value="1"/> <threadNum value="3"/>
<!--设置下载队列最大任务数, 默认为2--> <!--设置下载队列最大任务数, 默认为2-->
<maxTaskNum value="1"/> <maxTaskNum value="1"/>
<!--设置下载失败,重试次数,默认为10--> <!--设置下载失败,重试次数,默认为10-->
<reTryNum value="1"/> <reTryNum value="1"/>
<!--设置重试间隔,单位为毫秒,默认2000毫秒--> <!--设置重试间隔,单位为毫秒,默认2000毫秒-->
<reTryInterval value="5000"/> <reTryInterval value="5000"/>
<!--设置url连接超时时间,单位为毫秒,默认5000毫秒--> <!--设置url连接超时时间,单位为毫秒,默认5000毫秒-->
<connectTimeOut value="5000"/> <connectTimeOut value="5000"/>
<!--设置IO流读取时间,单位为毫秒,默认20000毫秒,该时间不能少于10000毫秒--> <!--设置IO流读取时间,单位为毫秒,默认20000毫秒,该时间不能少于10000毫秒-->
<iOTimeOut value="10000"/> <iOTimeOut value="10000"/>
<!--设置写文件buff大小,该数值大小不能小于2048,数值变小,下载速度会变慢--> <!--设置写文件buff大小,该数值大小不能小于2048,数值变小,下载速度会变慢-->
<buffSize value="8192"/> <buffSize value="8192"/>
<!--设置https ca 证书信息;path 为assets目录下的CA证书完整路径,name 为CA证书名--> <!--设置https ca 证书信息;path 为assets目录下的CA证书完整路径,name 为CA证书名-->
<ca name="" path=""/> <ca name="" path=""/>
<!--是否需要转换速度单位,转换完成后为:1b/s、1kb/s、1mb/s、1gb/s、1tb/s,如果不需要将返回byte长度--> <!--是否需要转换速度单位,转换完成后为:1b/s、1kb/s、1mb/s、1gb/s、1tb/s,如果不需要将返回byte长度-->
<convertSpeed value="true"/> <convertSpeed value="true"/>
<!--执行队列类型,见com.arialyy.aria.core.QueueMod,默认类型为wait--> <!--执行队列类型,见com.arialyy.aria.core.QueueMod,默认类型为wait-->
<queueMod value="wait"/> <queueMod value="wait"/>
<!--进度更新更新间隔,默认1000毫秒--> <!--进度更新更新间隔,默认1000毫秒-->
<updateInterval value="1000"/> <updateInterval value="1000"/>
</download> </download>
<!--普通上传任务--> <!--普通上传任务-->
<upload> <upload>
<!--设置任务最大上传速度,0表示不限速,单位为:kb--> <!--设置任务最大上传速度,0表示不限速,单位为:kb-->
<maxSpeed value="0"/> <maxSpeed value="0"/>
<!--设置IO流读取时间,单位为毫秒,默认20000毫秒,该时间不能少于10000毫秒--> <!--设置IO流读取时间,单位为毫秒,默认20000毫秒,该时间不能少于10000毫秒-->
<iOTimeOut value="10000"/> <iOTimeOut value="10000"/>
<!--设置写文件buff大小,该数值大小不能小于2048,数值变小,速度会变慢--> <!--设置写文件buff大小,该数值大小不能小于2048,数值变小,速度会变慢-->
<buffSize value="8192"/> <buffSize value="8192"/>
<!--是否需要转换速度单位,转换完成后为:1b/s、1kb/s、1mb/s、1gb/s、1tb/s,如果不需要将返回byte长度--> <!--是否需要转换速度单位,转换完成后为:1b/s、1kb/s、1mb/s、1gb/s、1tb/s,如果不需要将返回byte长度-->
<convertSpeed value="true"/> <convertSpeed value="true"/>
<!--设置上传队列最大任务数, 默认为2--> <!--设置上传队列最大任务数, 默认为2-->
<maxTaskNum value="2"/> <maxTaskNum value="2"/>
<!--设置上传失败,重试次数,默认为10--> <!--设置上传失败,重试次数,默认为10-->
<reTryNum value="3"/> <reTryNum value="3"/>
<!--设置重试间隔,单位为毫秒--> <!--设置重试间隔,单位为毫秒-->
<reTryInterval value="2000"/> <reTryInterval value="2000"/>
<!--设置url连接超时时间,单位为毫秒,默认5000毫秒--> <!--设置url连接超时时间,单位为毫秒,默认5000毫秒-->
<connectTimeOut value="5000"/> <connectTimeOut value="5000"/>
<!--执行队列类型,见com.arialyy.aria.core.QueueMod,默认类型为wait--> <!--执行队列类型,见com.arialyy.aria.core.QueueMod,默认类型为wait-->
<queueMod value="wait"/> <queueMod value="wait"/>
<!--进度更新更新间隔,默认1000毫秒--> <!--进度更新更新间隔,默认1000毫秒-->
<updateInterval value="1000"/> <updateInterval value="1000"/>
</upload> </upload>
<!-- 下载类组合任务 --> <!-- 下载类组合任务 -->
<dGroup> <dGroup>
<!--组合任务下载队列最大任务数, 默认为2--> <!--组合任务下载队列最大任务数, 默认为2-->
<maxTaskNum value="1"/> <maxTaskNum value="1"/>
<!--设置下载失败,重试次数,默认为10--> <!--设置下载失败,重试次数,默认为10-->
<reTryNum value="1"/> <reTryNum value="1"/>
<!--设置重试间隔,单位为毫秒,默认2000毫秒--> <!--设置重试间隔,单位为毫秒,默认2000毫秒-->
<reTryInterval value="5000"/> <reTryInterval value="5000"/>
<!--执行队列类型,见com.arialyy.aria.core.QueueMod,默认类型为wait--> <!--执行队列类型,见com.arialyy.aria.core.QueueMod,默认类型为wait-->
<queueMod value="wait"/> <queueMod value="wait"/>
<!--进度更新更新间隔,默认1000毫秒--> <!--进度更新更新间隔,默认1000毫秒-->
<updateInterval value="1000"/> <updateInterval value="1000"/>
<!-- =============================以下为子任务的配置====================================--> <!-- =============================以下为子任务的配置====================================-->
<!--能同时下载的子任务最大任务数,默认3--> <!--能同时下载的子任务最大任务数,默认3-->
<subMaxTaskNum value="1"/> <subMaxTaskNum value="1"/>
<!--子任务下载失败时的重试次数,默认为5--> <!--子任务下载失败时的重试次数,默认为5-->
<subReTryNum value="5"/> <subReTryNum value="5"/>
<!--子任务下载失败时的重试间隔,单位为毫秒,默认2000毫秒--> <!--子任务下载失败时的重试间隔,单位为毫秒,默认2000毫秒-->
<subReTryInterval value="5000"/> <subReTryInterval value="5000"/>
<!--子任务url连接超时时间,单位为毫秒,默认5000毫秒--> <!--子任务url连接超时时间,单位为毫秒,默认5000毫秒-->
<connectTimeOut value="5000"/> <connectTimeOut value="5000"/>
<!--子任务IO流读取时间,单位为毫秒,默认20000毫秒,该时间不能少于10000毫秒--> <!--子任务IO流读取时间,单位为毫秒,默认20000毫秒,该时间不能少于10000毫秒-->
<iOTimeOut value="10000"/> <iOTimeOut value="10000"/>
<!--子任务写文件buff大小,该数值大小不能小于2048,数值变小,下载速度会变慢--> <!--子任务写文件buff大小,该数值大小不能小于2048,数值变小,下载速度会变慢-->
<buffSize value="8192"/> <buffSize value="8192"/>
<!--子任务 https ca 证书信息;path 为assets目录下的CA证书完整路径,name 为CA证书名--> <!--子任务 https ca 证书信息;path 为assets目录下的CA证书完整路径,name 为CA证书名-->
<ca name="" path=""/> <ca name="" path=""/>
<!--子任务是否需要转换速度单位,转换完成后为:1b/s、1kb/s、1mb/s、1gb/s、1tb/s,如果不需要将返回byte长度--> <!--子任务是否需要转换速度单位,转换完成后为:1b/s、1kb/s、1mb/s、1gb/s、1tb/s,如果不需要将返回byte长度-->
<convertSpeed value="true"/> <convertSpeed value="true"/>
<!--子任务的最大下载速度,0表示不限速,单位为:kb; --> <!--子任务的最大下载速度,0表示不限速,单位为:kb; -->
<maxSpeed value="0"/> <maxSpeed value="0"/>
</dGroup> </dGroup>
</aria> </aria>

@ -31,6 +31,7 @@ import android.widget.Toast;
import com.arialyy.annotations.Download; import com.arialyy.annotations.Download;
import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.Aria;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.download.DownloadTarget; import com.arialyy.aria.core.download.DownloadTarget;
import com.arialyy.aria.core.download.DownloadTask; import com.arialyy.aria.core.download.DownloadTask;
@ -57,14 +58,11 @@ public class SingleTaskActivity extends BaseActivity<ActivitySingleBinding> {
//"http://static.gaoshouyou.com/d/22/94/822260b849944492caadd2983f9bb624.apks"; //"http://static.gaoshouyou.com/d/22/94/822260b849944492caadd2983f9bb624.apks";
//"http://120.55.95.61:8811/ghcg/zg/武义总规纲要成果.zip"; //"http://120.55.95.61:8811/ghcg/zg/武义总规纲要成果.zip";
//"https://yizi-kejian.oss-cn-beijing.aliyuncs.com/qimeng/package1/qmtable11.zip"; //"https://yizi-kejian.oss-cn-beijing.aliyuncs.com/qimeng/package1/qmtable11.zip";
"http://rs.0.gaoshouyou.com/d/04/1e/400423a7551e1f3f0eb1812afa1f9b44.apk"; //"http://rs.0.gaoshouyou.com/d/04/1e/400423a7551e1f3f0eb1812afa1f9b44.apk";
//"http://chargepile2.techsum.net/car-manage/file/download?path=2019-04-26/c0242efd18be4ecbb23911b1c509dcad--掌通各系统汇总.xls"; // 无长度的chunked
//"http://58.210.9.131/tpk/sipgt//TDLYZTGH.tpk"; //chunked 下载 //"http://58.210.9.131/tpk/sipgt//TDLYZTGH.tpk"; //chunked 下载
//"https://static.donguo.me/video/ip/course/pfys_1.mp4"; "http://apk500.bce.baidu-mgame.com/game/67000/67734/20170622040827_oem_5502845.apk?r=1";
//"https://www.baidu.com/link?url=_LFCuTPtnzFxVJByJ504QymRywIA1Z_T5xUxe9ZLuxcGM0C_RcdpWyB1eGjbJC-e5wv5wAKM4WmLMAS5KeF6EZJHB8Va3YqZUiaErqK_pxm&wd=&eqid=e8583fe70002d126000000065a99f864";
//"https://d.pcs.baidu.com/file/a02c89a2d479d4fd2756f3313d42491d?fid=4232431903-250528-1114369760340736&dstime=1525491372&rt=sh&sign=FDtAERVY-DCb740ccc5511e5e8fedcff06b081203-3C13vkOkuk4TqXvVYW05zj1K0ao%3D&expires=8h&chkv=1&chkbd=0&chkpc=et&dp-logid=8651730921842106225&dp-callid=0&r=165533013";
//"http://apk500.bce.baidu-mgame.com/game/67000/67734/20170622040827_oem_5502845.apk?r=1";
//"https://dl.genymotion.com/releases/genymotion-2.12.1/genymotion-2.12.1-vbox.exe"; //"https://dl.genymotion.com/releases/genymotion-2.12.1/genymotion-2.12.1-vbox.exe";
//"http://9.9.9.50:5000/download1";
//"http://9.9.9.50:5000/download/CentOS-7-x86_64-Minimal-1804.iso"; //"http://9.9.9.50:5000/download/CentOS-7-x86_64-Minimal-1804.iso";
//"http://v2.qingdian1.com/m_20180730_991/2/2B9FB34A4BCD8CE61481D1C8418EFE36_1080P.m3u8"; //"http://v2.qingdian1.com/m_20180730_991/2/2B9FB34A4BCD8CE61481D1C8418EFE36_1080P.m3u8";
//"https://firmwareapi.azurewebsites.net/firmware-overview?name=A19_Filament_W_IMG0038_00102411-encrypted.ota"; //"https://firmwareapi.azurewebsites.net/firmware-overview?name=A19_Filament_W_IMG0038_00102411-encrypted.ota";
@ -236,18 +234,17 @@ public class SingleTaskActivity extends BaseActivity<ActivitySingleBinding> {
} }
/** /**
* @param task *
* @param e
*/ */
@Download.onTaskFail @Download.onTaskFail
void taskFail(DownloadTask task, Exception e) { void taskFail(DownloadTask task, Exception e) {
if (task.getKey().equals(DOWNLOAD_URL)) { if (task.getKey().equals(DOWNLOAD_URL)) {
Toast.makeText(SingleTaskActivity.this, "下载失败", Toast.LENGTH_SHORT).show(); Toast.makeText(SingleTaskActivity.this, "下载失败", Toast.LENGTH_SHORT).show();
setBtState(true); setBtState(true);
Aria.download(this) //Aria.download(this)
.load(DOWNLOAD_URL) // .load(DOWNLOAD_URL)
.updateUrl("http://120.55.95.61:8811/ghcg/zg/武义总规纲要成果.zip") // .updateUrl("http://120.55.95.61:8811/ghcg/zg/武义总规纲要成果.zip")
.start(); // .start();
//DOWNLOAD_URL = "http://120.55.95.61:8811/ghcg/zg/武义总规纲要成果.zip"; //DOWNLOAD_URL = "http://120.55.95.61:8811/ghcg/zg/武义总规纲要成果.zip";
//ALog.d(TAG, ALog.getExceptionString(e)); //ALog.d(TAG, ALog.getExceptionString(e));
} }
@ -262,9 +259,11 @@ public class SingleTaskActivity extends BaseActivity<ActivitySingleBinding> {
//mCancel.setEnabled(false); //mCancel.setEnabled(false);
setBtState(true); setBtState(true);
getBinding().setSpeed(""); getBinding().setSpeed("");
L.d(TAG, "path ==> " + task.getDownloadEntity().getDownloadPath()); L.d(TAG, "path = " + task.getDownloadEntity().getDownloadPath());
L.d(TAG, "md5Code ==> " + CommonUtil.getFileMD5(new File(task.getDownloadPath()))); L.d(TAG, "md5Code = " + CommonUtil.getFileMD5(new File(task.getDownloadPath())));
L.d(TAG, "data ==> " + Aria.download(this).getDownloadEntity(DOWNLOAD_URL)); L.d(TAG, "data = " + Aria.download(this).getDownloadEntity(DOWNLOAD_URL));
DownloadEntity temp = Aria.download(this).getDownloadEntity(DOWNLOAD_URL);
L.d(TAG, "status = " + temp.getState() + ", isComplete = " + temp.isComplete());
//Intent install = new Intent(Intent.ACTION_VIEW); //Intent install = new Intent(Intent.ACTION_VIEW);
//install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//File apkFile = new File(task.getDownloadPath()); //File apkFile = new File(task.getDownloadPath());
@ -286,8 +285,6 @@ public class SingleTaskActivity extends BaseActivity<ActivitySingleBinding> {
return R.layout.activity_single; return R.layout.activity_single;
} }
public void onClick(View view) { public void onClick(View view) {
switch (view.getId()) { switch (view.getId()) {
case R.id.start: case R.id.start:
@ -318,9 +315,9 @@ public class SingleTaskActivity extends BaseActivity<ActivitySingleBinding> {
//params.put("filename", "CentOS-7-x86_64-Minimal-1804.iso"); //params.put("filename", "CentOS-7-x86_64-Minimal-1804.iso");
Aria.download(SingleTaskActivity.this) Aria.download(SingleTaskActivity.this)
.load(DOWNLOAD_URL) .load(DOWNLOAD_URL)
//.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8") //.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3")
//.addHeader("Accept-Encoding", "gzip, deflate") .addHeader("Accept-Encoding", "gzip, deflate")
//.addHeader("DNT", "1") .addHeader("DNT", "1")
//.addHeader("Cookie", "BAIDUID=648E5FF020CC69E8DD6F492D1068AAA9:FG=1; BIDUPSID=648E5FF020CC69E8DD6F492D1068AAA9; PSTM=1519099573; BD_UPN=12314753; locale=zh; BDSVRTM=0") //.addHeader("Cookie", "BAIDUID=648E5FF020CC69E8DD6F492D1068AAA9:FG=1; BIDUPSID=648E5FF020CC69E8DD6F492D1068AAA9; PSTM=1519099573; BD_UPN=12314753; locale=zh; BDSVRTM=0")
.useServerFileName(true) .useServerFileName(true)
.setFilePath(path, true) .setFilePath(path, true)

@ -48,16 +48,15 @@ public class DownloadGroupActivity extends BaseActivity<ActivityDownloadGroupBin
setTitle("任务组"); setTitle("任务组");
mChildList = getBinding().childList; mChildList = getBinding().childList;
mUrls = getModule(GroupModule.class).getUrls(); mUrls = getModule(GroupModule.class).getUrls();
DGTaskWrapper entity = Aria.download(this).getGroupTask(mUrls); DownloadGroupEntity entity = Aria.download(this).getDownloadGroupEntity(mUrls);
if (entity != null && entity.getEntity() != null) { if (entity != null) {
DownloadGroupEntity groupEntity = entity.getEntity(); mChildList.addData(entity.getSubEntities());
mChildList.addData(groupEntity.getSubEntities()); getBinding().setFileSize(entity.getConvertFileSize());
getBinding().setFileSize(groupEntity.getConvertFileSize()); if (entity.getFileSize() == 0) {
if (groupEntity.getFileSize() == 0) {
getBinding().setProgress(0); getBinding().setProgress(0);
} else { } else {
getBinding().setProgress(groupEntity.isComplete() ? 100 getBinding().setProgress(entity.isComplete() ? 100
: (int) (groupEntity.getCurrentProgress() * 100 / groupEntity.getFileSize())); : (int) (entity.getCurrentProgress() * 100 / entity.getFileSize()));
} }
} }
@ -82,7 +81,8 @@ public class DownloadGroupActivity extends BaseActivity<ActivityDownloadGroupBin
switch (view.getId()) { switch (view.getId()) {
case R.id.start: case R.id.start:
List<String> temp = new ArrayList<>(); List<String> temp = new ArrayList<>();
temp.add("https://d.pcs.baidu.com/file/130335545f3f4d9cc38afe709c19af5a?fid=1411168371-250528-1010657263806840&dstime=1531134714&rt=sh&sign=FDtAERVY-DCb740ccc5511e5e8fedcff06b081203-h8KgJ6gl4oY9UR6NqvwJsT4nVSM%3D&expires=8h&chkv=1&chkbd=0&chkpc=et&dp-logid=4401996296756616039&dp-callid=0&r=279987343"); temp.add(
"https://d.pcs.baidu.com/file/130335545f3f4d9cc38afe709c19af5a?fid=1411168371-250528-1010657263806840&dstime=1531134714&rt=sh&sign=FDtAERVY-DCb740ccc5511e5e8fedcff06b081203-h8KgJ6gl4oY9UR6NqvwJsT4nVSM%3D&expires=8h&chkv=1&chkbd=0&chkpc=et&dp-logid=4401996296756616039&dp-callid=0&r=279987343");
Aria.download(this) Aria.download(this)
.loadGroup(mUrls) .loadGroup(mUrls)
.setDirPath( .setDirPath(

@ -36,23 +36,22 @@ import com.arialyy.simple.widget.SubStateLinearLayout;
public class FTPDirDownloadActivity extends BaseActivity<ActivityDownloadGroupBinding> { public class FTPDirDownloadActivity extends BaseActivity<ActivityDownloadGroupBinding> {
private static final String dir = "ftp://9.9.9.50:21/upload/测试"; private static final String dir = "ftp://9.9.9.50:21/upload/测试";
SubStateLinearLayout mChildList; SubStateLinearLayout mChildList;
@Override protected void init(Bundle savedInstanceState) { @Override protected void init(Bundle savedInstanceState) {
super.init(savedInstanceState); super.init(savedInstanceState);
Aria.download(this).register(); Aria.download(this).register();
setTitle("FTP文件夹下载"); setTitle("FTP文件夹下载");
mChildList = findViewById(R.id.child_list); mChildList = findViewById(R.id.child_list);
DGTaskWrapper entity = Aria.download(this).getFtpDirTask(dir); DownloadGroupEntity entity = Aria.download(this).getFtpDirEntity(dir);
if (entity != null && entity.getEntity() != null) { if (entity != null) {
DownloadGroupEntity groupEntity = entity.getEntity(); mChildList.addData(entity.getSubEntities());
mChildList.addData(groupEntity.getSubEntities()); getBinding().setFileSize(entity.getConvertFileSize());
getBinding().setFileSize(groupEntity.getConvertFileSize()); if (entity.getFileSize() == 0) {
if (groupEntity.getFileSize() == 0) {
getBinding().setProgress(0); getBinding().setProgress(0);
} else { } else {
getBinding().setProgress(groupEntity.isComplete() ? 100 getBinding().setProgress(entity.isComplete() ? 100
: (int) (groupEntity.getCurrentProgress() * 100 / groupEntity.getFileSize())); : (int) (entity.getCurrentProgress() * 100 / entity.getFileSize()));
} }
} }
} }

@ -43,8 +43,6 @@ public class AnyRunActivity extends BaseActivity<ActivityTestBinding> {
mBar.setVisibility(View.GONE); mBar.setVisibility(View.GONE);
module = new AnyRunnModule(this); module = new AnyRunnModule(this);
urls = getResources().getStringArray(R.array.group_urls); urls = getResources().getStringArray(R.array.group_urls);
DTaskWrapper entity = Aria.download(this).getDownloadTask(URL);
Log.d(TAG, entity + "");
} }
public void onClick(View view) { public void onClick(View view) {

@ -1,58 +1,58 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext.kotlin_version = '1.3.11' ext.kotlin_version = '1.3.11'
repositories { repositories {
jcenter() jcenter()
mavenCentral() mavenCentral()
google() google()
maven { url 'https://jitpack.io' } maven { url 'https://jitpack.io' }
} }
dependencies { dependencies {
// classpath 'com.android.tools.build:gradle:2.3.3' // classpath 'com.android.tools.build:gradle:2.3.3'
classpath 'com.android.tools.build:gradle:3.3.2' classpath 'com.android.tools.build:gradle:3.3.2'
classpath 'com.novoda:bintray-release:0.9' classpath 'com.novoda:bintray-release:0.9'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlin_version}" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlin_version}"
// classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7' // classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7'
// classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' // classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files
} }
} }
allprojects { allprojects {
repositories { repositories {
jcenter() jcenter()
mavenCentral() mavenCentral()
google() google()
maven { url 'https://jitpack.io' } maven { url 'https://jitpack.io' }
} }
tasks.withType(Javadoc) { tasks.withType(Javadoc) {
options { options {
encoding "UTF-8" encoding "UTF-8"
charSet 'UTF-8' charSet 'UTF-8'
links "http://docs.oracle.com/javase/7/docs/api" links "http://docs.oracle.com/javase/7/docs/api"
} }
} }
} }
task clean(type: Delete) { task clean(type: Delete) {
delete rootProject.buildDir delete rootProject.buildDir
} }
ext { ext {
userOrg = 'arialyy' userOrg = 'arialyy'
groupId = 'com.arialyy.aria' groupId = 'com.arialyy.aria'
publishVersion = '3.6.3' publishVersion = '3.6.4_dev_1'
// publishVersion = '1.0.4' //FTP插件 // publishVersion = '1.0.4' //FTP插件
repoName='maven' repoName='maven'
desc = 'android 下载框架' desc = 'android 下载框架'
website = 'https://github.com/AriaLyy/Aria' website = 'https://github.com/AriaLyy/Aria'
licences = ['Apache-2.0'] licences = ['Apache-2.0']
compileSdkVersion = 28 compileSdkVersion = 28
supportLibVersion = "28.0.0" supportLibVersion = "28.0.0"
buildToolsVersion = "28.0.3" buildToolsVersion = "28.0.3"
targetSdkVersion = 28 targetSdkVersion = 28
minSdkVersion = 15 minSdkVersion = 15
} }

Loading…
Cancel
Save