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

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

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

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

@ -36,6 +36,7 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.CookieManager;
import java.net.HttpCookie;
import java.net.HttpURLConnection;
@ -77,7 +78,6 @@ public class HttpFileInfoThread implements Runnable {
ConnectionHelp.setConnectParam(mTaskDelegate, conn);
conn.setRequestProperty("Range", "bytes=" + 0 + "-");
conn.setConnectTimeout(mConnectTimeOut);
//conn.setChunkedStreamingMode(0);
conn.connect();
handleConnect(conn);
} catch (IOException e) {
@ -149,20 +149,7 @@ public class HttpFileInfoThread implements Runnable {
String disposition = conn.getHeaderField("Content-Disposition");
if (mTaskDelegate.isUseServerFileName() && !TextUtils.isEmpty(disposition)) {
mEntity.setDisposition(CommonUtil.encryptBASE64(disposition));
if (disposition.contains(";")) {
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;
}
}
}
}
handleContentDisposition(disposition);
}
CookieManager msCookieManager = new CookieManager();
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, "重命名失败【服务器返回的文件名为空】");
return;
}
ALog.d(TAG, String.format("文件重命名为:%s", newName));
File oldFile = new File(mEntity.getDownloadPath());
String newPath = oldFile.getParent() + "/" + newName;
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.setDownloadPath(newPath);
@ -269,7 +292,7 @@ public class HttpFileInfoThread implements Runnable {
}
return;
}
if (!CheckUtil.checkUrl(newUrl)) {
if (!CheckUtil.checkUrlNotThrow(newUrl)) {
failDownload(new TaskException(TAG, "下载失败,重定向url错误"), false);
return;
}
@ -296,6 +319,7 @@ public class HttpFileInfoThread implements Runnable {
*/
private boolean checkLen(long len) {
if (len != mEntity.getFileSize()) {
ALog.d(TAG, "长度不一致,任务为新任务");
mTaskWrapper.setNewTask(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.StateConstance;
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.download.DTaskWrapper;
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.BufferedRandomAccessFile;
import java.io.BufferedInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@ -44,6 +44,7 @@ import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.util.Map;
import java.util.Set;
import java.util.zip.GZIPInputStream;
/**
* Created by lyy on 2017/1/18. 下载线程
@ -87,7 +88,12 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DTaskWrapper> {
ConnectionHelp.setConnectParam(taskDelegate, conn);
conn.setConnectTimeout(getTaskConfig().getConnectTimeOut());
conn.setReadTimeout(getTaskConfig().getIOTimeOut()); //设置读取流的等待时间,必须设置该参数
if (taskDelegate.isChunked()) {
conn.setDoInput(true);
conn.setChunkedStreamingMode(0);
}
conn.connect();
// 传递参数
if (taskDelegate.getRequestEnum() == RequestEnum.POST) {
Map<String, String> params = taskDelegate.getParams();
if (params != null) {
@ -106,7 +112,9 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DTaskWrapper> {
}
is = new BufferedInputStream(ConnectionHelp.convertInputStream(conn));
if (isOpenDynamicFile) {
if (taskDelegate.isChunked()) {
readChunked(is);
} else if (isOpenDynamicFile) {
readDynamicFile(is);
} else {
//创建可设置位置的文件
@ -114,11 +122,7 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DTaskWrapper> {
new BufferedRandomAccessFile(mConfig.TEMP_FILE, "rwd", getTaskConfig().getBuffSize());
//设置每条线程写入文件的位置
file.seek(mConfig.START_LOCATION);
if (taskDelegate.isChunked()) {
readChunk(is, file);
} else {
readNormal(is, file);
}
readNormal(is, file);
handleComplete();
}
} catch (MalformedURLException e) {
@ -151,6 +155,41 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DTaskWrapper> {
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()) {
return;
}
if (mTaskWrapper.asHttp().isChunked()) {
ALog.i(TAG, "任务下载完成");
STATE.isRunning = false;
mListener.onComplete();
return;
}
if (mChildCurrentLocation == mConfig.END_LOCATION) {
//支持断点的处理

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

@ -44,14 +44,14 @@ class DGTaskWrapperFactory implements IGTEFactory<DownloadGroupEntity, DGTaskWra
}
@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);
wrapper.setSubTaskWrapper(createDGSubTaskWrapper(entity));
return wrapper;
}
@Override public DGTaskWrapper getFTE(String ftpUrl) {
DownloadGroupEntity entity = DbDataHelper.getFtpDGEntity(ftpUrl);
DownloadGroupEntity entity = DbDataHelper.getOrCreateFtpDGEntity(ftpUrl);
DGTaskWrapper fte = new DGTaskWrapper(entity);
fte.asFtp().setUrlEntity(CommonUtil.getFtpUrlInfo(ftpUrl));

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

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

@ -17,7 +17,6 @@ package com.arialyy.aria.orm;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.support.v4.util.LruCache;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import java.lang.reflect.Field;
@ -28,60 +27,6 @@ import java.net.URLEncoder;
*/
abstract class 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编码字符串
@ -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) {
if (db == null || !db.isOpen()) {
db = SqlHelper.INSTANCE.getWritableDatabase();
db = SqlHelper.getInstance().getDb();
}
return db;
}

@ -33,6 +33,7 @@ import java.util.Map;
*/
class DBConfig {
/*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 String DB_NAME;
static int VERSION = 45;

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

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

@ -1,257 +1,302 @@
/*
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.arialyy.aria.orm;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import com.arialyy.aria.util.ALog;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Created by lyy on 2015/11/2.
* sql帮助类
*/
final class SqlHelper extends SQLiteOpenHelper {
private static final String TAG = "SqlHelper";
static volatile SqlHelper INSTANCE = null;
private DelegateCommon mDelegate;
synchronized static SqlHelper init(Context context) {
if (INSTANCE == null) {
synchronized (SqlHelper.class) {
DelegateCommon delegate = DelegateManager.getInstance().getDelegate(DelegateCommon.class);
INSTANCE = new SqlHelper(context.getApplicationContext(), delegate);
SQLiteDatabase db = INSTANCE.getWritableDatabase();
db = delegate.checkDb(db);
// SQLite在3.6.19版本中开始支持外键约束,
// 而在Android中 2.1以前的版本使用的SQLite版本是3.5.9, 在2.2版本中使用的是3.6.22.
// 但是为了兼容以前的程序,默认并没有启用该功能,如果要启用该功能
// 需要使用如下语句:
db.execSQL("PRAGMA foreign_keys=ON;");
Set<String> tables = DBConfig.mapping.keySet();
for (String tableName : tables) {
Class clazz = DBConfig.mapping.get(tableName);
if (!delegate.tableExists(db, clazz)) {
delegate.createTable(db, clazz);
}
}
}
}
return INSTANCE;
}
private SqlHelper(Context context, DelegateCommon delegate) {
super(DBConfig.SAVE_IN_SDCARD ? new DatabaseContext(context) : context, DBConfig.DB_NAME, null,
DBConfig.VERSION);
mDelegate = delegate;
}
@Override public void onCreate(SQLiteDatabase db) {
}
@Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion < newVersion) {
if (oldVersion < 31) {
handle314AriaUpdate(db);
} else if (oldVersion < 45) {
handle360AriaUpdate(db);
} else {
handleDbUpdate(db, null, null);
}
}
}
@Override public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion > newVersion) {
handleDbUpdate(db, null, null);
}
}
/**
* 处理数据库升级该段代码无法修改表字段
*
* @param modifyColumns 需要修改的表字段的映射key为表名
* value{@code Map<String, String>}中的Map的key为老字段名称value为该老字段对应的新字段名称
* @param delColumns 需要删除的表字段key为表名value{@code List<String>}为需要删除的字段列表
*/
private void handleDbUpdate(SQLiteDatabase db, Map<String, Map<String, String>> modifyColumns,
Map<String, List<String>> delColumns) {
if (db == null) {
ALog.e("SqlHelper", "db 为 null");
return;
} else if (!db.isOpen()) {
ALog.e("SqlHelper", "db已关闭");
return;
}
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);
//修改表名为中介表名
String alertSql = String.format("ALTER TABLE %s RENAME TO %s_temp", tableName, tableName);
db.execSQL(alertSql);
//创建新表
mDelegate.createTable(db, clazz);
String sql = String.format("SELECT COUNT(*) FROM %s_temp", tableName);
Cursor cursor = db.rawQuery(sql, null);
cursor.moveToFirst();
long count = cursor.getLong(0);
cursor.close();
if (count > 0) {
// 获取所有表字段名称
Cursor columnC =
db.rawQuery(String.format("PRAGMA table_info(%s_temp)", tableName), null);
StringBuilder params = new StringBuilder();
while (columnC.moveToNext()) {
String columnName = columnC.getString(columnC.getColumnIndex("name"));
if (delColumns != null && delColumns.get(tableName) != null) {
List<String> delColumn = delColumns.get(tableName);
if (delColumn != null && !delColumn.isEmpty()) {
if (delColumn.contains(columnName)) {
continue;
}
}
}
params.append(columnName).append(",");
}
columnC.close();
String oldParamStr = params.toString();
oldParamStr = oldParamStr.substring(0, oldParamStr.length() - 1);
String newParamStr = oldParamStr;
// 处理字段名称改变
if (modifyColumns != null) {
//newParamStr = params.toString();
Map<String, String> columnMap = modifyColumns.get(tableName);
if (columnMap != null && !columnMap.isEmpty()) {
Set<String> keys = columnMap.keySet();
for (String key : keys) {
if (newParamStr.contains(key)) {
newParamStr = newParamStr.replace(key, columnMap.get(key));
}
}
}
}
//恢复数据
String insertSql =
String.format("INSERT INTO %s (%s) SELECT %s FROM %s_temp", tableName, newParamStr,
oldParamStr, tableName);
ALog.d(TAG, "insertSql = " + insertSql);
db.execSQL(insertSql);
}
//删除中介表
mDelegate.dropTable(db, tableName + "_temp");
}
}
db.setTransactionSuccessful();
} catch (Exception e) {
ALog.e(TAG, e);
} finally {
db.endTransaction();
}
mDelegate.close(db);
}
/**
* 处理3.6以下版本的数据库升级
*/
private void handle360AriaUpdate(SQLiteDatabase db) {
String[] taskTables =
new String[] { "UploadTaskEntity", "DownloadTaskEntity", "DownloadGroupTaskEntity" };
for (String taskTable : taskTables) {
if (mDelegate.tableExists(db, taskTable)) {
mDelegate.dropTable(db, taskTable);
}
}
Map<String, Map<String, String>> columnMap = new HashMap<>();
Map<String, String> map = new HashMap<>();
map.put("groupName", "groupHash");
columnMap.put("DownloadEntity", map);
columnMap.put("DownloadGroupEntity", map);
handleDbUpdate(db, columnMap, null);
}
/**
* 处理3.4版本之前数据库迁移主要是修改子表外键字段对应的值
*/
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);
}
/*
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.arialyy.aria.orm;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Build;
import com.arialyy.aria.util.ALog;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Created by lyy on 2015/11/2.
* sql帮助类
*/
final class SqlHelper extends SQLiteOpenHelper {
private static final String TAG = "SqlHelper";
private static volatile SqlHelper INSTANCE = null;
private Context mContext;
private DelegateCommon mDelegate;
synchronized static SqlHelper init(Context context) {
if (INSTANCE == null) {
synchronized (SqlHelper.class) {
DelegateCommon delegate = DelegateManager.getInstance().getDelegate(DelegateCommon.class);
INSTANCE = new SqlHelper(context.getApplicationContext(), delegate);
}
}
return INSTANCE;
}
static SqlHelper getInstance() {
return INSTANCE;
}
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;
}
@Override public void onOpen(SQLiteDatabase db) {
super.onOpen(db);
}
@Override public void onConfigure(SQLiteDatabase db) {
super.onConfigure(db);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
db.setForeignKeyConstraintsEnabled(true);
} else {
// SQLite在3.6.19版本中开始支持外键约束,
// 而在Android中 2.1以前的版本使用的SQLite版本是3.5.9, 在2.2版本中使用的是3.6.22.
// 但是为了兼容以前的程序,默认并没有启用该功能,如果要启用该功能
// 需要使用如下语句:
db.execSQL("PRAGMA foreign_keys=ON;");
}
if (DBConfig.DEBUG) {
db.enableWriteAheadLogging();
}
}
@Override public void onCreate(SQLiteDatabase db) {
DelegateCommon delegate = DelegateManager.getInstance().getDelegate(DelegateCommon.class);
Set<String> tables = DBConfig.mapping.keySet();
for (String tableName : tables) {
Class clazz = DBConfig.mapping.get(tableName);
if (!delegate.tableExists(db, clazz)) {
delegate.createTable(db, clazz);
}
}
}
@Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion < newVersion) {
if (oldVersion < 31) {
handle314AriaUpdate(db);
} else if (oldVersion < 45) {
handle360AriaUpdate(db);
} else {
handleDbUpdate(db, null, null);
}
}
}
@Override public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion > newVersion) {
handleDbUpdate(db, null, null);
}
}
/**
* 获取数据库连接
*/
SQLiteDatabase getDb() {
SQLiteDatabase db;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
SQLiteDatabase.OpenParams params = new SQLiteDatabase.OpenParams.Builder().setOpenFlags(
SQLiteDatabase.NO_LOCALIZED_COLLATORS | SQLiteDatabase.OPEN_READWRITE |
SQLiteDatabase.CREATE_IF_NECESSARY).build();
setOpenParams(params);
db = getWritableDatabase();
} else {
//SQLiteDatabase.openOrCreateDatabase()
File dbFile = mContext.getDatabasePath(DBConfig.DB_NAME);
if (!dbFile.exists()) {
db = getWritableDatabase();
} else {
// 触发一次SQLiteOpenHelper的流程,再使用NO_LOCALIZED_COLLATORS标志打开数据库
db = getReadableDatabase();
db.close();
db = SQLiteDatabase.openDatabase(dbFile.getPath(), null,
SQLiteDatabase.NO_LOCALIZED_COLLATORS | SQLiteDatabase.OPEN_READWRITE |
SQLiteDatabase.CREATE_IF_NECESSARY);
}
}
return db;
}
/**
* 处理数据库升级该段代码无法修改表字段
*
* @param modifyColumns 需要修改的表字段的映射key为表名
* value{@code Map<String, String>}中的Map的key为老字段名称value为该老字段对应的新字段名称
* @param delColumns 需要删除的表字段key为表名value{@code List<String>}为需要删除的字段列表
*/
private void handleDbUpdate(SQLiteDatabase db, Map<String, Map<String, String>> modifyColumns,
Map<String, List<String>> delColumns) {
if (db == null) {
ALog.e("SqlHelper", "db 为 null");
return;
} else if (!db.isOpen()) {
ALog.e("SqlHelper", "db已关闭");
return;
}
try {
db.beginTransaction();
Set<String> tables = DBConfig.mapping.keySet();
for (String tableName : tables) {
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);
//创建新表
mDelegate.createTable(db, clazz);
String sql = String.format("SELECT COUNT(*) FROM %s_temp", tableName);
Cursor cursor = db.rawQuery(sql, null);
cursor.moveToFirst();
long count = cursor.getLong(0);
cursor.close();
if (count > 0) {
// 获取所有表字段名称
Cursor columnC =
db.rawQuery(String.format("PRAGMA table_info(%s_temp)", tableName), null);
StringBuilder params = new StringBuilder();
while (columnC.moveToNext()) {
String columnName = columnC.getString(columnC.getColumnIndex("name"));
if (delColumns != null && delColumns.get(tableName) != null) {
List<String> delColumn = delColumns.get(tableName);
if (delColumn != null && !delColumn.isEmpty()) {
if (delColumn.contains(columnName)) {
continue;
}
}
}
params.append(columnName).append(",");
}
columnC.close();
String oldParamStr = params.toString();
oldParamStr = oldParamStr.substring(0, oldParamStr.length() - 1);
String newParamStr = oldParamStr;
// 处理字段名称改变
if (modifyColumns != null) {
//newParamStr = params.toString();
Map<String, String> columnMap = modifyColumns.get(tableName);
if (columnMap != null && !columnMap.isEmpty()) {
Set<String> keys = columnMap.keySet();
for (String key : keys) {
if (newParamStr.contains(key)) {
newParamStr = newParamStr.replace(key, columnMap.get(key));
}
}
}
}
//恢复数据
String insertSql =
String.format("INSERT INTO %s (%s) SELECT %s FROM %s_temp", tableName, newParamStr,
oldParamStr, tableName);
ALog.d(TAG, "insertSql = " + insertSql);
db.execSQL(insertSql);
}
//删除中介表
mDelegate.dropTable(db, tableName + "_temp");
}
}
db.setTransactionSuccessful();
} catch (Exception e) {
ALog.e(TAG, e);
} finally {
db.endTransaction();
}
}
/**
* 处理3.6以下版本的数据库升级
*/
private void handle360AriaUpdate(SQLiteDatabase db) {
String[] taskTables =
new String[] { "UploadTaskEntity", "DownloadTaskEntity", "DownloadGroupTaskEntity" };
for (String taskTable : taskTables) {
if (mDelegate.tableExists(db, taskTable)) {
mDelegate.dropTable(db, taskTable);
}
}
Map<String, Map<String, String>> columnMap = new HashMap<>();
Map<String, String> map = new HashMap<>();
map.put("groupName", "groupHash");
columnMap.put("DownloadEntity", map);
columnMap.put("DownloadGroupEntity", map);
handleDbUpdate(db, columnMap, null);
}
/**
* 处理3.4版本之前数据库迁移主要是修改子表外键字段对应的值
*/
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)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.arialyy.aria.util;
import android.text.TextUtils;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.inf.AbsTaskWrapper;
import com.arialyy.aria.core.upload.UTaskWrapper;
import com.arialyy.aria.core.upload.UploadEntity;
import com.arialyy.aria.exception.ParamException;
import java.io.File;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by Lyy on 2016/9/23.
* 检查帮助类
*/
public class CheckUtil {
private static final String TAG = "CheckUtil";
/**
* 检查ftp上传路径如果ftp上传路径为空抛出空指针异常
* 如果ftp上传路径不是以"ftp""sftp"抛出参数异常
*
* @param ftpUrl ftp上传路径
*/
public static void checkFtpUploadUrl(String ftpUrl) {
if (TextUtils.isEmpty(ftpUrl)) {
throw new ParamException("ftp上传路径为空");
} else if (!ftpUrl.startsWith("ftp") || !ftpUrl.startsWith("sftp")) {
throw new ParamException("ftp上传路径无效");
}
}
/**
* 判空
*/
public static void checkNull(Object obj) {
if (obj == null) throw new IllegalArgumentException("不能传入空对象");
}
/**
* 检查分页数据需要查询的页数从1开始如果page小于1 num 小于1则抛出{@link NullPointerException}
*
* @param page 从1 开始
* @param num 每页数量
*/
public static void checkPageParams(int page, int num) {
if (page < 1 || num < 1) throw new NullPointerException("page和num不能小于1");
}
/**
* 检查sql的expression是否合法
*/
public static void checkSqlExpression(String... expression) {
if (expression.length == 0) {
throw new IllegalArgumentException("sql语句表达式不能为null");
}
if (expression.length == 1) {
throw new IllegalArgumentException("表达式需要写入参数");
}
String where = expression[0];
if (!where.contains("?")) {
throw new IllegalArgumentException("请在where语句的'='后编写?");
}
Pattern pattern = Pattern.compile("\\?");
Matcher matcher = pattern.matcher(where);
int count = 0;
while (matcher.find()) {
count++;
}
if (count < expression.length - 1) {
throw new IllegalArgumentException("条件语句的?个数不能小于参数个数");
}
if (count > expression.length - 1) {
throw new IllegalArgumentException("条件语句的?个数不能大于参数个数");
}
}
/**
* 检查下载实体
*/
public static void checkDownloadEntity(DownloadEntity entity) {
checkUrlInvalidThrow(entity.getUrl());
entity.setUrl(entity.getUrl());
checkPath(entity.getDownloadPath());
}
/**
* 检测下载链接是否为null
*/
public static void checkPath(String path) {
if (TextUtils.isEmpty(path)) {
throw new IllegalArgumentException("保存路径不能为null");
}
}
/**
* 检测url是否合法如果url不合法将抛异常
*/
public static void checkUrlInvalidThrow(String url) {
if (TextUtils.isEmpty(url)) {
throw new IllegalArgumentException("url不能为null");
} else if (!url.startsWith("http") && !url.startsWith("ftp")) {
throw new IllegalArgumentException("url错误");
}
int index = url.indexOf("://");
if (index == -1) {
throw new IllegalArgumentException("url不合法");
}
}
/**
* 检测url是否合法
*
* @return {@code true} 合法{@code false} 非法
*/
public static boolean checkUrl(String url) {
if (TextUtils.isEmpty(url)) {
ALog.e(TAG, "url不能为null");
return false;
} else if (!url.startsWith("http") && !url.startsWith("ftp")) {
ALog.e(TAG, "url【" + url + "】错误");
return false;
}
int index = url.indexOf("://");
if (index == -1) {
ALog.e(TAG, "url【" + url + "】不合法");
}
return true;
}
/**
* 检测下载链接组是否为null
*/
public static void checkDownloadUrls(List<String> urls) {
if (urls == null || urls.isEmpty()) {
throw new IllegalArgumentException("链接组不能为null");
}
}
/**
* 检查下载任务组保存路径
*/
public static void checkDownloadPaths(List<String> paths) {
if (paths == null || paths.isEmpty()) {
throw new IllegalArgumentException("链接保存路径不能为null");
}
}
/**
* 检测上传地址是否为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 checkTaskEntity(AbsTaskWrapper entity) {
if (entity instanceof DTaskWrapper) {
checkDownloadTaskEntity(((DTaskWrapper) entity).getEntity());
} else if (entity instanceof UTaskWrapper) {
checkUploadTaskEntity(((UTaskWrapper) entity).getEntity());
}
}
/**
* 检查命令实体
*
* @param checkType 删除命令和停止命令不需要检查下载链接和保存路径
* @return {@code false}实体无效
*/
public static boolean checkCmdEntity(AbsTaskWrapper entity, boolean checkType) {
boolean b = false;
if (entity instanceof DTaskWrapper) {
DownloadEntity entity1 = ((DTaskWrapper) entity).getEntity();
if (entity1 == null) {
ALog.e(TAG, "下载实体不能为空");
} else if (checkType && TextUtils.isEmpty(entity1.getUrl())) {
ALog.e(TAG, "下载链接不能为空");
} else if (checkType && TextUtils.isEmpty(entity1.getDownloadPath())) {
ALog.e(TAG, "保存路径不能为空");
} else {
b = true;
}
} else if (entity instanceof UTaskWrapper) {
UploadEntity entity1 = ((UTaskWrapper) entity).getEntity();
if (entity1 == null) {
ALog.e(TAG, "上传实体不能为空");
} else if (TextUtils.isEmpty(entity1.getFilePath())) {
ALog.e(TAG, "上传文件路径不能为空");
} else {
b = true;
}
}
return b;
}
/**
* 检查上传实体是否合法
*/
private static void checkUploadTaskEntity(UploadEntity entity) {
if (entity == null) {
throw new NullPointerException("上传实体不能为空");
} else if (TextUtils.isEmpty(entity.getFilePath())) {
throw new IllegalArgumentException("上传文件路径不能为空");
} else if (TextUtils.isEmpty(entity.getFileName())) {
throw new IllegalArgumentException("上传文件名不能为空");
}
}
/**
* 检测下载实体是否合法
* 合法(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");
}
}
/*
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.arialyy.aria.util;
import android.text.TextUtils;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.inf.AbsTaskWrapper;
import com.arialyy.aria.core.upload.UTaskWrapper;
import com.arialyy.aria.core.upload.UploadEntity;
import com.arialyy.aria.exception.ParamException;
import java.io.File;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by Lyy on 2016/9/23.
* 检查帮助类
*/
public class CheckUtil {
private static final String TAG = "CheckUtil";
/**
* 检查ftp上传路径如果ftp上传路径为空抛出空指针异常
* 如果ftp上传路径不是以"ftp""sftp"抛出参数异常
*
* @param ftpUrl ftp上传路径
*/
public static void checkFtpUploadUrl(String ftpUrl) {
if (TextUtils.isEmpty(ftpUrl)) {
throw new ParamException("ftp上传路径为空");
} else if (!ftpUrl.startsWith("ftp") || !ftpUrl.startsWith("sftp")) {
throw new ParamException("ftp上传路径无效");
}
}
/**
* 判空
*/
public static void checkNull(Object obj) {
if (obj == null) throw new IllegalArgumentException("不能传入空对象");
}
/**
* 检查分页数据需要查询的页数从1开始如果page小于1 num 小于1则抛出{@link NullPointerException}
*
* @param page 从1 开始
* @param num 每页数量
*/
public static void checkPageParams(int page, int num) {
if (page < 1 || num < 1) throw new NullPointerException("page和num不能小于1");
}
/**
* 检查sql的expression是否合法
*/
public static void checkSqlExpression(String... expression) {
if (expression.length == 0) {
throw new IllegalArgumentException("sql语句表达式不能为null");
}
if (expression.length == 1) {
throw new IllegalArgumentException("表达式需要写入参数");
}
String where = expression[0];
if (!where.contains("?")) {
throw new IllegalArgumentException("请在where语句的'='后编写?");
}
Pattern pattern = Pattern.compile("\\?");
Matcher matcher = pattern.matcher(where);
int count = 0;
while (matcher.find()) {
count++;
}
if (count < expression.length - 1) {
throw new IllegalArgumentException("条件语句的?个数不能小于参数个数");
}
if (count > expression.length - 1) {
throw new IllegalArgumentException("条件语句的?个数不能大于参数个数");
}
}
/**
* 检查下载实体
*/
public static void checkDownloadEntity(DownloadEntity entity) {
checkUrlInvalidThrow(entity.getUrl());
entity.setUrl(entity.getUrl());
checkPath(entity.getDownloadPath());
}
/**
* 检测下载链接是否为null
*/
public static void checkPath(String path) {
if (TextUtils.isEmpty(path)) {
throw new IllegalArgumentException("保存路径不能为null");
}
}
/**
* 检测url是否合法如果url不合法将抛异常
*/
public static void checkUrlInvalidThrow(String url) {
if (TextUtils.isEmpty(url)) {
throw new IllegalArgumentException("url不能为null");
} else if (!url.startsWith("http") && !url.startsWith("ftp")) {
throw new IllegalArgumentException("url错误");
}
int index = url.indexOf("://");
if (index == -1) {
throw new IllegalArgumentException("url不合法");
}
}
/**
* 检测url是否合法如果url不合法将抛出{@link IllegalArgumentException}异常
*/
public static void checkUrl(String url) {
if (TextUtils.isEmpty(url)) {
throw new NullPointerException("url为空");
} else if (!url.startsWith("http") && !url.startsWith("ftp")) {
throw new IllegalArgumentException(String.format("url【%s】错误", url));
}
int index = url.indexOf("://");
if (index == -1) {
throw new IllegalArgumentException(String.format("url【%s】不合法", url));
}
}
/**
* 检测url是否合法
*
* @return {@code true} 合法{@code false} 非法
*/
public static boolean checkUrlNotThrow(String url) {
if (TextUtils.isEmpty(url)) {
ALog.e(TAG, "url不能为null");
return false;
} else if (!url.startsWith("http") && !url.startsWith("ftp")) {
ALog.e(TAG, "url【" + url + "】错误");
return false;
}
int index = url.indexOf("://");
if (index == -1) {
ALog.e(TAG, "url【" + url + "】不合法");
}
return true;
}
/**
* 检测下载链接组是否为null
*/
public static void checkDownloadUrls(List<String> urls) {
if (urls == null || urls.isEmpty()) {
throw new IllegalArgumentException("链接组不能为null");
}
}
/**
* 检查下载任务组保存路径
*/
public static void checkDownloadPaths(List<String> paths) {
if (paths == null || paths.isEmpty()) {
throw new IllegalArgumentException("链接保存路径不能为null");
}
}
/**
* 检测上传地址是否为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 checkTaskEntity(AbsTaskWrapper entity) {
if (entity instanceof DTaskWrapper) {
checkDownloadTaskEntity(((DTaskWrapper) entity).getEntity());
} else if (entity instanceof UTaskWrapper) {
checkUploadTaskEntity(((UTaskWrapper) entity).getEntity());
}
}
/**
* 检查命令实体
*
* @param checkType 删除命令和停止命令不需要检查下载链接和保存路径
* @return {@code false}实体无效
*/
public static boolean checkCmdEntity(AbsTaskWrapper entity, boolean checkType) {
boolean b = false;
if (entity instanceof DTaskWrapper) {
DownloadEntity entity1 = ((DTaskWrapper) entity).getEntity();
if (entity1 == null) {
ALog.e(TAG, "下载实体不能为空");
} else if (checkType && TextUtils.isEmpty(entity1.getUrl())) {
ALog.e(TAG, "下载链接不能为空");
} else if (checkType && TextUtils.isEmpty(entity1.getDownloadPath())) {
ALog.e(TAG, "保存路径不能为空");
} else {
b = true;
}
} else if (entity instanceof UTaskWrapper) {
UploadEntity entity1 = ((UTaskWrapper) entity).getEntity();
if (entity1 == null) {
ALog.e(TAG, "上传实体不能为空");
} else if (TextUtils.isEmpty(entity1.getFilePath())) {
ALog.e(TAG, "上传文件路径不能为空");
} else {
b = true;
}
}
return b;
}
/**
* 检查上传实体是否合法
*/
private static void checkUploadTaskEntity(UploadEntity entity) {
if (entity == null) {
throw new NullPointerException("上传实体不能为空");
} else if (TextUtils.isEmpty(entity.getFilePath())) {
throw new IllegalArgumentException("上传文件路径不能为空");
} else if (TextUtils.isEmpty(entity.getFileName())) {
throw new IllegalArgumentException("上传文件名不能为空");
}
}
/**
* 检测下载实体是否合法
* 合法(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;
}
/**
* 获取组合任务实体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 urls 子任务url列表
*/
public static DownloadGroupEntity getHttpDGEntity(String groupHash, List<String> urls) {
public static DownloadGroupEntity getOrCreateHttpDGEntity(String groupHash, List<String> urls) {
List<DGEntityWrapper> wrapper =
DbEntity.findRelationData(DGEntityWrapper.class, "DownloadGroupEntity.groupHash=?",
groupHash);
@ -96,7 +110,7 @@ public class DbDataHelper {
*
* @param ftpUrl ftp下载地址
*/
public static DownloadGroupEntity getFtpDGEntity(String ftpUrl) {
public static DownloadGroupEntity getOrCreateFtpDGEntity(String ftpUrl) {
List<DGEntityWrapper> wrapper =
DbEntity.findRelationData(DGEntityWrapper.class, "DownloadGroupEntity.groupHash=?",
ftpUrl);

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

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

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

@ -31,6 +31,7 @@ import android.widget.Toast;
import com.arialyy.annotations.Download;
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.DownloadTarget;
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://120.55.95.61:8811/ghcg/zg/武义总规纲要成果.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 下载
//"https://static.donguo.me/video/ip/course/pfys_1.mp4";
//"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";
"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";
//"http://9.9.9.50:5000/download1";
//"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";
//"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
void taskFail(DownloadTask task, Exception e) {
if (task.getKey().equals(DOWNLOAD_URL)) {
Toast.makeText(SingleTaskActivity.this, "下载失败", Toast.LENGTH_SHORT).show();
setBtState(true);
Aria.download(this)
.load(DOWNLOAD_URL)
.updateUrl("http://120.55.95.61:8811/ghcg/zg/武义总规纲要成果.zip")
.start();
//Aria.download(this)
// .load(DOWNLOAD_URL)
// .updateUrl("http://120.55.95.61:8811/ghcg/zg/武义总规纲要成果.zip")
// .start();
//DOWNLOAD_URL = "http://120.55.95.61:8811/ghcg/zg/武义总规纲要成果.zip";
//ALog.d(TAG, ALog.getExceptionString(e));
}
@ -262,9 +259,11 @@ public class SingleTaskActivity extends BaseActivity<ActivitySingleBinding> {
//mCancel.setEnabled(false);
setBtState(true);
getBinding().setSpeed("");
L.d(TAG, "path ==> " + task.getDownloadEntity().getDownloadPath());
L.d(TAG, "md5Code ==> " + CommonUtil.getFileMD5(new File(task.getDownloadPath())));
L.d(TAG, "data ==> " + Aria.download(this).getDownloadEntity(DOWNLOAD_URL));
L.d(TAG, "path = " + task.getDownloadEntity().getDownloadPath());
L.d(TAG, "md5Code = " + CommonUtil.getFileMD5(new File(task.getDownloadPath())));
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);
//install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//File apkFile = new File(task.getDownloadPath());
@ -286,8 +285,6 @@ public class SingleTaskActivity extends BaseActivity<ActivitySingleBinding> {
return R.layout.activity_single;
}
public void onClick(View view) {
switch (view.getId()) {
case R.id.start:
@ -318,9 +315,9 @@ public class SingleTaskActivity extends BaseActivity<ActivitySingleBinding> {
//params.put("filename", "CentOS-7-x86_64-Minimal-1804.iso");
Aria.download(SingleTaskActivity.this)
.load(DOWNLOAD_URL)
//.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8")
//.addHeader("Accept-Encoding", "gzip, deflate")
//.addHeader("DNT", "1")
//.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("DNT", "1")
//.addHeader("Cookie", "BAIDUID=648E5FF020CC69E8DD6F492D1068AAA9:FG=1; BIDUPSID=648E5FF020CC69E8DD6F492D1068AAA9; PSTM=1519099573; BD_UPN=12314753; locale=zh; BDSVRTM=0")
.useServerFileName(true)
.setFilePath(path, true)

@ -48,16 +48,15 @@ public class DownloadGroupActivity extends BaseActivity<ActivityDownloadGroupBin
setTitle("任务组");
mChildList = getBinding().childList;
mUrls = getModule(GroupModule.class).getUrls();
DGTaskWrapper entity = Aria.download(this).getGroupTask(mUrls);
if (entity != null && entity.getEntity() != null) {
DownloadGroupEntity groupEntity = entity.getEntity();
mChildList.addData(groupEntity.getSubEntities());
getBinding().setFileSize(groupEntity.getConvertFileSize());
if (groupEntity.getFileSize() == 0) {
DownloadGroupEntity entity = Aria.download(this).getDownloadGroupEntity(mUrls);
if (entity != null) {
mChildList.addData(entity.getSubEntities());
getBinding().setFileSize(entity.getConvertFileSize());
if (entity.getFileSize() == 0) {
getBinding().setProgress(0);
} else {
getBinding().setProgress(groupEntity.isComplete() ? 100
: (int) (groupEntity.getCurrentProgress() * 100 / groupEntity.getFileSize()));
getBinding().setProgress(entity.isComplete() ? 100
: (int) (entity.getCurrentProgress() * 100 / entity.getFileSize()));
}
}
@ -82,7 +81,8 @@ public class DownloadGroupActivity extends BaseActivity<ActivityDownloadGroupBin
switch (view.getId()) {
case R.id.start:
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)
.loadGroup(mUrls)
.setDirPath(

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

@ -43,8 +43,6 @@ public class AnyRunActivity extends BaseActivity<ActivityTestBinding> {
mBar.setVisibility(View.GONE);
module = new AnyRunnModule(this);
urls = getResources().getStringArray(R.array.group_urls);
DTaskWrapper entity = Aria.download(this).getDownloadTask(URL);
Log.d(TAG, entity + "");
}
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.
buildscript {
ext.kotlin_version = '1.3.11'
repositories {
jcenter()
mavenCentral()
google()
maven { url 'https://jitpack.io' }
}
dependencies {
// classpath 'com.android.tools.build:gradle:2.3.3'
classpath 'com.android.tools.build:gradle:3.3.2'
classpath 'com.novoda:bintray-release:0.9'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlin_version}"
// classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7'
// classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
mavenCentral()
google()
maven { url 'https://jitpack.io' }
}
tasks.withType(Javadoc) {
options {
encoding "UTF-8"
charSet 'UTF-8'
links "http://docs.oracle.com/javase/7/docs/api"
}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
ext {
userOrg = 'arialyy'
groupId = 'com.arialyy.aria'
publishVersion = '3.6.3'
// publishVersion = '1.0.4' //FTP插件
repoName='maven'
desc = 'android 下载框架'
website = 'https://github.com/AriaLyy/Aria'
licences = ['Apache-2.0']
compileSdkVersion = 28
supportLibVersion = "28.0.0"
buildToolsVersion = "28.0.3"
targetSdkVersion = 28
minSdkVersion = 15
}
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.3.11'
repositories {
jcenter()
mavenCentral()
google()
maven { url 'https://jitpack.io' }
}
dependencies {
// classpath 'com.android.tools.build:gradle:2.3.3'
classpath 'com.android.tools.build:gradle:3.3.2'
classpath 'com.novoda:bintray-release:0.9'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlin_version}"
// classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7'
// classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
mavenCentral()
google()
maven { url 'https://jitpack.io' }
}
tasks.withType(Javadoc) {
options {
encoding "UTF-8"
charSet 'UTF-8'
links "http://docs.oracle.com/javase/7/docs/api"
}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
ext {
userOrg = 'arialyy'
groupId = 'com.arialyy.aria'
publishVersion = '3.6.4_dev_1'
// publishVersion = '1.0.4' //FTP插件
repoName='maven'
desc = 'android 下载框架'
website = 'https://github.com/AriaLyy/Aria'
licences = ['Apache-2.0']
compileSdkVersion = 28
supportLibVersion = "28.0.0"
buildToolsVersion = "28.0.3"
targetSdkVersion = 28
minSdkVersion = 15
}

Loading…
Cancel
Save