组合任务增加子队列功能

v3.6.6
laoyuyu 6 years ago
parent dbddf3b4e2
commit b976348ffe
  1. 34
      Aria/src/main/java/com/arialyy/aria/core/command/group/GroupCancelCmd.java
  2. 8
      Aria/src/main/java/com/arialyy/aria/core/command/group/GroupCmdFactory.java
  3. 12
      Aria/src/main/java/com/arialyy/aria/core/common/IUtil.java
  4. 7
      Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTarget.java
  5. 26
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/Downloader.java
  6. 26
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/SimpleDownloadUtil.java
  7. 421
      Aria/src/main/java/com/arialyy/aria/core/download/group/AbsGroupUtil.java
  8. 146
      Aria/src/main/java/com/arialyy/aria/core/download/group/ChildDownloadListener.java
  9. 140
      Aria/src/main/java/com/arialyy/aria/core/download/group/DownloadGroupUtil.java
  10. 28
      Aria/src/main/java/com/arialyy/aria/core/download/group/FtpDirDownloadUtil.java
  11. 169
      Aria/src/main/java/com/arialyy/aria/core/download/group/GroupRunState.java
  12. 12
      Aria/src/main/java/com/arialyy/aria/core/download/group/ISubQueue.java
  13. 150
      Aria/src/main/java/com/arialyy/aria/core/download/group/SimpleSchedulers.java
  14. 40
      Aria/src/main/java/com/arialyy/aria/core/download/group/SimpleSubQueue.java
  15. 113
      Aria/src/main/java/com/arialyy/aria/core/download/group/SubDownloadLoader.java
  16. 2
      Aria/src/main/java/com/arialyy/aria/core/inf/AbsEntity.java
  17. 11
      Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupTask.java
  18. 14
      Aria/src/main/java/com/arialyy/aria/core/manager/SubTaskManager.java
  19. 15
      Aria/src/main/java/com/arialyy/aria/core/scheduler/AbsSchedulers.java
  20. 6
      Aria/src/main/java/com/arialyy/aria/core/scheduler/ISchedulers.java
  21. 20
      Aria/src/main/java/com/arialyy/aria/core/upload/uploader/SimpleUploadUtil.java
  22. 4
      app/src/main/assets/aria_config.xml
  23. 1
      app/src/main/java/com/arialyy/simple/download/group/DownloadGroupActivity.java

@ -1,34 +0,0 @@
/*
* 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.command.group;
import com.arialyy.aria.core.inf.AbsGroupTaskWrapper;
/**
* Created by AriaL on 2017/6/29.
* 删除任务组子任务
*/
class GroupCancelCmd<T extends AbsGroupTaskWrapper> extends AbsGroupCmd<T> {
GroupCancelCmd(T entity) {
super(entity);
}
@Override public void executeCmd() {
if (checkTask()) {
tempTask.cancelSubTask(childUrl);
}
}
}

@ -31,10 +31,6 @@ public class GroupCmdFactory {
* 停止子任务
*/
public static final int SUB_TASK_STOP = 0xa2;
/**
* 取消子任务
*/
public static final int SUB_TASK_CANCEL = 0xa3;
private static volatile GroupCmdFactory INSTANCE = null;
@ -54,7 +50,7 @@ public class GroupCmdFactory {
/**
* @param target 创建任务的对象
* @param wrapper 参数信息
* @param type 命令类型{@link #SUB_TASK_START}{@link #SUB_TASK_STOP}{@link #SUB_TASK_CANCEL}
* @param type 命令类型{@link #SUB_TASK_START}{@link #SUB_TASK_STOP}
* @param childUrl 需要控制的子任务url
*/
public AbsGroupCmd createCmd(String target, AbsGroupTaskWrapper wrapper, int type,
@ -67,8 +63,6 @@ public class GroupCmdFactory {
case SUB_TASK_STOP:
cmd = new GroupStopCmd<>(wrapper);
break;
case SUB_TASK_CANCEL:
cmd = new GroupCancelCmd<>(wrapper);
}
if (cmd != null) {
cmd.childUrl = childUrl;

@ -16,12 +16,24 @@
package com.arialyy.aria.core.common;
import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.download.DownloadGroupEntity;
import com.arialyy.aria.core.upload.UploadEntity;
/**
* Created by lyy on 2016/10/31.
* 任务功能接口
*/
public interface IUtil {
/**
* 获取任务标志
*
* @return {@link DownloadEntity#getKey()}{@link DownloadGroupEntity#getKey()}{@link
* UploadEntity#getKey()}
*/
String getKey();
/**
* 获取文件大小
*/

@ -113,7 +113,7 @@ public class DownloadGroupTarget extends BaseGroupTarget<DownloadGroupTarget> im
* 任务组总任务大小任务组是一个抽象的概念没有真实的数据实体任务组的大小是Aria动态获取子任务大小相加而得到的
* 如果你知道当前任务组总大小你也可以调用该方法给任务组设置大小
*
* 为了更好的用户体验建议直接设置任务组文件大小
* 为了更好的用户体验组合任务必须设置文件大小
*
* @param fileSize 任务组总大小
*/
@ -185,6 +185,11 @@ public class DownloadGroupTarget extends BaseGroupTarget<DownloadGroupTarget> im
return false;
}
if (mTaskWrapper.getEntity().getFileSize() == 0) {
ALog.e(TAG, "组合任务必须设置文件文件大小");
return false;
}
if (mTaskWrapper.asHttp().getRequestEnum() == RequestEnum.POST) {
for (DTaskWrapper subTask : mTaskWrapper.getSubTaskWrapper()) {
subTask.asHttp().setRequestEnum(RequestEnum.POST);

@ -15,7 +15,6 @@
*/
package com.arialyy.aria.core.download.downloader;
import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.core.common.AbsFileer;
import com.arialyy.aria.core.common.AbsThreadTask;
import com.arialyy.aria.core.common.SubThreadConfig;
@ -35,23 +34,22 @@ import java.io.IOException;
public class Downloader extends AbsFileer<DownloadEntity, DTaskWrapper> {
private String TAG = "Downloader";
public Downloader(IDownloadListener listener, DTaskWrapper taskEntity) {
super(listener, taskEntity);
public Downloader(IDownloadListener listener, DTaskWrapper taskWrapper) {
super(listener, taskWrapper);
mTempFile = new File(mEntity.getDownloadPath());
AriaManager manager = AriaManager.getInstance(AriaManager.APP);
setUpdateInterval(manager.getDownloadConfig().getUpdateInterval());
setUpdateInterval(taskWrapper.getConfig().getUpdateInterval());
}
/**
* 小于1m的文件或是任务组的子任务线程数都是1
*/
@Override protected int setNewTaskThreadNum() {
int threadNum = AriaManager.getInstance(mContext).getDownloadConfig().getThreadNum();
return
// 小于1m的文件或是任务组的子任务、使用虚拟文件,线程数都是1
mEntity.getFileSize() <= SUB_LEN
|| mTaskWrapper.getRequestType() == AbsTaskWrapper.D_FTP_DIR
|| mTaskWrapper.getRequestType() == AbsTaskWrapper.DG_HTTP
|| threadNum == 1
? 1
: threadNum;
int threadNum = mTaskWrapper.getConfig().getThreadNum();
return mEntity.getFileSize() <= SUB_LEN
|| mTaskWrapper.isGroupTask()
|| threadNum == 1
? 1
: threadNum;
}
@Override protected boolean handleNewTask() {

@ -33,13 +33,17 @@ public class SimpleDownloadUtil implements IUtil, Runnable {
private String TAG = "SimpleDownloadUtil";
private IDownloadListener mListener;
private Downloader mDownloader;
private DTaskWrapper mTaskEntity;
private DTaskWrapper mTaskWrapper;
private boolean isStop = false, isCancel = false;
public SimpleDownloadUtil(DTaskWrapper entity, IDownloadListener downloadListener) {
mTaskEntity = entity;
public SimpleDownloadUtil(DTaskWrapper wrapper, IDownloadListener downloadListener) {
mTaskWrapper = wrapper;
mListener = downloadListener;
mDownloader = new Downloader(downloadListener, entity);
mDownloader = new Downloader(downloadListener, wrapper);
}
@Override public String getKey() {
return mTaskWrapper.getKey();
}
@Override public long getFileSize() {
@ -103,10 +107,10 @@ public class SimpleDownloadUtil implements IUtil, Runnable {
if (isStop || isCancel) {
return;
}
if (mTaskEntity.getEntity().getFileSize() <= 1
|| mTaskEntity.isRefreshInfo()
|| mTaskEntity.getRequestType() == AbsTaskWrapper.D_FTP
|| mTaskEntity.getState() == IEntity.STATE_FAIL) {
if (mTaskWrapper.getEntity().getFileSize() <= 1
|| mTaskWrapper.isRefreshInfo()
|| mTaskWrapper.getRequestType() == AbsTaskWrapper.D_FTP
|| mTaskWrapper.getState() == IEntity.STATE_FAIL) {
new Thread(createInfoThread()).start();
} else {
mDownloader.start();
@ -117,9 +121,9 @@ public class SimpleDownloadUtil implements IUtil, Runnable {
* 通过链接类型创建不同的获取文件信息的线程
*/
private Runnable createInfoThread() {
switch (mTaskEntity.getRequestType()) {
switch (mTaskWrapper.getRequestType()) {
case AbsTaskWrapper.D_FTP:
return new FtpFileInfoThread(mTaskEntity, new OnFileInfoCallback() {
return new FtpFileInfoThread(mTaskWrapper, new OnFileInfoCallback() {
@Override public void onComplete(String url, CompleteInfo info) {
mDownloader.start();
}
@ -130,7 +134,7 @@ public class SimpleDownloadUtil implements IUtil, Runnable {
}
});
case AbsTaskWrapper.D_HTTP:
return new HttpFileInfoThread(mTaskEntity, new OnFileInfoCallback() {
return new HttpFileInfoThread(mTaskWrapper, new OnFileInfoCallback() {
@Override public void onComplete(String url, CompleteInfo info) {
mDownloader.start();
}

@ -15,23 +15,16 @@
*/
package com.arialyy.aria.core.download.group;
import com.arialyy.aria.core.AriaManager;
import android.os.Handler;
import com.arialyy.aria.core.common.IUtil;
import com.arialyy.aria.core.config.Configuration;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.download.DGTaskWrapper;
import com.arialyy.aria.core.download.downloader.Downloader;
import com.arialyy.aria.core.inf.IDownloadListener;
import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.NetUtils;
import java.io.File;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.WeakHashMap;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@ -40,7 +33,6 @@ import java.util.concurrent.TimeUnit;
* 任务组核心逻辑
*/
public abstract class AbsGroupUtil implements IUtil, Runnable {
private static final Object LOCK = new Object();
private final String TAG = "AbsGroupUtil";
/**
* FTP文件夹
@ -51,53 +43,46 @@ public abstract class AbsGroupUtil implements IUtil, Runnable {
*/
int HTTP_GROUP = 0xa2;
/**
* 任务组所有任务总长度
*/
long mTotalLen = 0;
long mCurrentLocation = 0;
private long mCurrentLocation = 0;
protected IDownloadGroupListener mListener;
DGTaskWrapper mGTWrapper;
private boolean isRunning = false;
private ScheduledThreadPoolExecutor mTimer;
/**
* 保存所有没有下载完成的任务key为下载地址
*/
Map<String, DTaskWrapper> mExeMap = new ConcurrentHashMap<>();
/**
* 下载失败的映射表key为下载地址
*/
Map<String, DTaskWrapper> mFailMap = new ConcurrentHashMap<>();
/**
* 该任务组对应的所有任务
*/
private Map<String, DTaskWrapper> mTasksMap = new ConcurrentHashMap<>();
private long mUpdateInterval;
private boolean isStop = false, isCancel = false;
private Handler mScheduler;
private SimpleSubQueue mSubQueue = SimpleSubQueue.newInstance();
private Map<String, SubDownloadLoader> mExeLoader = new WeakHashMap<>();
private Map<String, DTaskWrapper> mCache = new WeakHashMap<>();
DGTaskWrapper mGTWrapper;
GroupRunState mState;
/**
* 下载器映射表key为下载地址
*/
private Map<String, Downloader> mDownloaderMap = new ConcurrentHashMap<>();
AbsGroupUtil(IDownloadGroupListener listener, DGTaskWrapper groupWrapper) {
mListener = listener;
mGTWrapper = groupWrapper;
mUpdateInterval = Configuration.getInstance().downloadCfg.getUpdateInterval();
mState = new GroupRunState(groupWrapper.getKey(), mListener,
groupWrapper.getSubTaskWrapper().size(), mSubQueue);
mScheduler = new Handler(SimpleSchedulers.newInstance(mState));
initState();
}
/**
* 是否需要读取文件长度{@code true}需要
* 初始化组合任务状态
*/
boolean isNeedLoadFileSize = true;
//已经完成的任务数
int mCompleteNum = 0;
//停止的任务数
private int mStopNum = 0;
//任务组大小
int mGroupSize = 0;
private long mUpdateInterval;
private boolean isStop = false, isCancel = false;
private void initState() {
for (DTaskWrapper wrapper : mGTWrapper.getSubTaskWrapper()) {
if (wrapper.getEntity().getState() == IEntity.STATE_COMPLETE) {
mState.updateCompleteNum();
mCurrentLocation += wrapper.getEntity().getFileSize();
} else {
mCache.put(wrapper.getKey(), wrapper);
mCurrentLocation += wrapper.getEntity().getCurrentProgress();
}
}
mState.updateProgress(mCurrentLocation);
}
AbsGroupUtil(IDownloadGroupListener listener, DGTaskWrapper groupEntity) {
mListener = listener;
mGTWrapper = groupEntity;
mUpdateInterval =
AriaManager.getInstance(AriaManager.APP).getDownloadConfig().getUpdateInterval();
@Override public String getKey() {
return mGTWrapper.getKey();
}
/**
@ -107,16 +92,6 @@ public abstract class AbsGroupUtil implements IUtil, Runnable {
*/
abstract int getTaskType();
/**
* 更新任务组文件大小
*/
void updateFileSize() {
if (isNeedLoadFileSize) {
mGTWrapper.getEntity().setFileSize(mTotalLen);
mGTWrapper.getEntity().update();
}
}
/**
* 启动子任务下载
*
@ -124,13 +99,12 @@ public abstract class AbsGroupUtil implements IUtil, Runnable {
*/
public void startSubTask(String url) {
if (!checkSubTask(url, "开始")) return;
if (!isRunning) {
if (!mState.isRunning) {
startTimer();
}
Downloader d = getDownloader(url, false);
SubDownloadLoader d = getDownloader(url);
if (d != null && !d.isRunning()) {
d.setNewTask(false);
d.start();
mSubQueue.startTask(d);
}
}
@ -141,36 +115,9 @@ public abstract class AbsGroupUtil implements IUtil, Runnable {
*/
public void stopSubTask(String url) {
if (!checkSubTask(url, "停止")) return;
Downloader d = getDownloader(url, false);
SubDownloadLoader d = getDownloader(url);
if (d != null && d.isRunning()) {
d.stop();
}
}
/**
* 删除子任务
*
* @param url 子任务下载地址
*/
public void cancelSubTask(String url) {
Set<String> urls = mTasksMap.keySet();
if (!urls.isEmpty() && urls.contains(url)) {
DTaskWrapper det = mTasksMap.get(url);
if (det != null) {
mTotalLen -= det.getEntity().getFileSize();
mCurrentLocation -= det.getEntity().getCurrentProgress();
mExeMap.remove(det.getKey());
mFailMap.remove(det.getKey());
mGroupSize--;
if (mGroupSize == 0) {
closeTimer();
mListener.onCancel();
}
}
}
Downloader d = getDownloader(url, false);
if (d != null) {
d.cancel();
mSubQueue.stopTask(d);
}
}
@ -182,9 +129,9 @@ public abstract class AbsGroupUtil implements IUtil, Runnable {
* @return {@code true} 任务可以下载
*/
private boolean checkSubTask(String url, String type) {
DTaskWrapper entity = mTasksMap.get(url);
if (entity != null) {
if (entity.getState() == IEntity.STATE_COMPLETE) {
DTaskWrapper wrapper = mCache.get(url);
if (wrapper != null) {
if (wrapper.getState() == IEntity.STATE_COMPLETE) {
ALog.w(TAG, "任务【" + url + "】已完成," + type + "失败");
return false;
}
@ -199,18 +146,17 @@ public abstract class AbsGroupUtil implements IUtil, Runnable {
* 通过地址获取下载器
*
* @param url 子任务下载地址
* @param start 是否启动任务
*/
private Downloader getDownloader(String url, boolean start) {
Downloader d = mDownloaderMap.get(url);
private SubDownloadLoader getDownloader(String url) {
SubDownloadLoader d = mExeLoader.get(url);
if (d == null) {
return createChildDownload(mTasksMap.get(url), start);
return createSubLoader(mCache.get(url));
}
return d;
}
@Override public long getFileSize() {
return mTotalLen;
return mGTWrapper.getEntity().getFileSize();
}
@Override public long getCurrentLocation() {
@ -218,21 +164,22 @@ public abstract class AbsGroupUtil implements IUtil, Runnable {
}
@Override public boolean isRunning() {
return isRunning;
return mState.isRunning;
}
@Override public void cancel() {
isCancel = true;
closeTimer();
onCancel();
Set<String> keys = mDownloaderMap.keySet();
Set<String> keys = mExeLoader.keySet();
mSubQueue.clear();
for (String key : keys) {
Downloader dt = mDownloaderMap.get(key);
if (dt != null) {
dt.cancel();
SubDownloadLoader loader = mExeLoader.get(key);
if (loader != null && loader.isRunning()) {
loader.cancel();
}
}
clearState();
mListener.onCancel();
}
@ -245,14 +192,15 @@ public abstract class AbsGroupUtil implements IUtil, Runnable {
closeTimer();
onStop();
Set<String> keys = mDownloaderMap.keySet();
Set<String> keys = mExeLoader.keySet();
mSubQueue.clear();
for (String key : keys) {
Downloader dt = mDownloaderMap.get(key);
if (dt != null) {
dt.stop();
SubDownloadLoader loader = mExeLoader.get(key);
if (loader != null && loader.isRunning()) {
mSubQueue.stopTask(loader);
}
}
clearState();
mListener.onStop(mCurrentLocation);
}
@ -260,32 +208,6 @@ public abstract class AbsGroupUtil implements IUtil, Runnable {
}
/**
* 预处理操作
* 而FTP文件夹的需要获取完成所有子任务信息才算预处理完成
*/
protected void onPre() {
mListener.onPre();
isRunning = true;
mGroupSize = mGTWrapper.getSubTaskWrapper().size();
mTotalLen = mGTWrapper.getEntity().getFileSize();
isNeedLoadFileSize = mTotalLen <= 10;
for (DTaskWrapper te : mGTWrapper.getSubTaskWrapper()) {
File file = new File(te.getKey());
if (te.getState() == IEntity.STATE_COMPLETE && file.exists()) {
mCompleteNum++;
mCurrentLocation += te.getEntity().getFileSize();
} else {
mExeMap.put(te.getKey(), te);
mCurrentLocation += file.exists() ? te.getEntity().getCurrentProgress() : 0;
}
if (isNeedLoadFileSize) {
mTotalLen += te.getEntity().getFileSize();
}
mTasksMap.put(te.getGroupHash(), te);
}
}
@Override public void start() {
new Thread(this).start();
}
@ -295,8 +217,8 @@ public abstract class AbsGroupUtil implements IUtil, Runnable {
closeTimer();
return;
}
clearState();
onStart();
startRunningFlow();
}
protected void onStart() {
@ -308,20 +230,15 @@ public abstract class AbsGroupUtil implements IUtil, Runnable {
}
@Override public void setMaxSpeed(int speed) {
Set<String> keys = mDownloaderMap.keySet();
Set<String> keys = mSubQueue.getExec().keySet();
for (String key : keys) {
Downloader dt = mDownloaderMap.get(key);
SubDownloadLoader dt = mSubQueue.getExec().get(key);
if (dt != null) {
dt.setMaxSpeed(speed);
}
}
}
private void clearState() {
mDownloaderMap.clear();
mFailMap.clear();
}
synchronized void closeTimer() {
if (mTimer != null && !mTimer.isShutdown()) {
mTimer.shutdown();
@ -331,9 +248,9 @@ public abstract class AbsGroupUtil implements IUtil, Runnable {
/**
* 开始进度流程
*/
void startRunningFlow() {
private void startRunningFlow() {
closeTimer();
mListener.onPostPre(mTotalLen);
mListener.onPostPre(mGTWrapper.getEntity().getFileSize());
if (mCurrentLocation > 0) {
mListener.onResume(mCurrentLocation);
} else {
@ -343,11 +260,11 @@ public abstract class AbsGroupUtil implements IUtil, Runnable {
}
private synchronized void startTimer() {
isRunning = true;
mState.isRunning = true;
mTimer = new ScheduledThreadPoolExecutor(1);
mTimer.scheduleWithFixedDelay(new Runnable() {
@Override public void run() {
if (!isRunning) {
if (!mState.isRunning) {
closeTimer();
} else if (mCurrentLocation >= 0) {
long t = 0;
@ -359,6 +276,7 @@ public abstract class AbsGroupUtil implements IUtil, Runnable {
}
}
mCurrentLocation = t;
mState.updateProgress(mCurrentLocation);
mListener.onProgress(t);
}
}
@ -366,201 +284,12 @@ public abstract class AbsGroupUtil implements IUtil, Runnable {
}
/**
* 创建子任务下载器默认创建完成自动启动
* 创建并启动子任务下载器
*/
void createChildDownload(DTaskWrapper taskEntity) {
createChildDownload(taskEntity, true);
}
/**
* 创建子任务下载器启动子任务下载器
*
* @param start 是否启动下载
*/
private Downloader createChildDownload(DTaskWrapper taskEntity, boolean start) {
ChildDownloadListener listener = new ChildDownloadListener(taskEntity);
Downloader dt = new Downloader(listener, taskEntity);
mDownloaderMap.put(taskEntity.getEntity().getUrl(), dt);
if (start) {
dt.start();
}
return dt;
}
/**
* 子任务事件监听
*/
private class ChildDownloadListener implements IDownloadListener {
private DTaskWrapper subTaskWrapper;
private DownloadEntity subEntity;
private int RUN_SAVE_INTERVAL = 5 * 1000; //5s保存一次下载中的进度
private long lastSaveTime;
private long lastLen;
private ScheduledThreadPoolExecutor timer;
private boolean isNotNetRetry;
ChildDownloadListener(DTaskWrapper entity) {
subTaskWrapper = entity;
subEntity = subTaskWrapper.getEntity();
subEntity.setFailNum(0);
lastLen = subEntity.getCurrentProgress();
lastSaveTime = System.currentTimeMillis();
isNotNetRetry = AriaManager.getInstance(AriaManager.APP).getAppConfig().isNotNetRetry();
}
@Override public void onPre() {
saveData(IEntity.STATE_PRE, -1);
}
@Override public void onPostPre(long fileSize) {
subEntity.setFileSize(fileSize);
subEntity.setConvertFileSize(CommonUtil.formatFileSize(fileSize));
saveData(IEntity.STATE_POST_PRE, -1);
mListener.onSubPre(subEntity);
}
@Override public void onResume(long resumeLocation) {
saveData(IEntity.STATE_POST_PRE, IEntity.STATE_RUNNING);
lastLen = resumeLocation;
mListener.onSubStart(subEntity);
}
@Override public void onStart(long startLocation) {
saveData(IEntity.STATE_POST_PRE, IEntity.STATE_RUNNING);
lastLen = startLocation;
mListener.onSubStart(subEntity);
}
@Override public void onProgress(long currentLocation) {
long speed = currentLocation - lastLen;
//mCurrentLocation += speed;
subEntity.setCurrentProgress(currentLocation);
handleSpeed(speed);
mListener.onSubRunning(subEntity);
if (System.currentTimeMillis() - lastSaveTime >= RUN_SAVE_INTERVAL) {
saveData(IEntity.STATE_RUNNING, currentLocation);
lastSaveTime = System.currentTimeMillis();
}
lastLen = currentLocation;
}
@Override public void onStop(long stopLocation) {
saveData(IEntity.STATE_STOP, stopLocation);
handleSpeed(0);
mListener.onSubStop(subEntity);
synchronized (AbsGroupUtil.LOCK) {
mStopNum++;
if (mStopNum + mCompleteNum + mFailMap.size() == mGroupSize && !isStop) {
closeTimer();
mListener.onStop(mCurrentLocation);
}
}
}
@Override public void onCancel() {
saveData(IEntity.STATE_CANCEL, -1);
handleSpeed(0);
mListener.onSubCancel(subEntity);
}
@Override public void onComplete() {
subEntity.setComplete(true);
saveData(IEntity.STATE_COMPLETE, subEntity.getFileSize());
handleSpeed(0);
mListener.onSubComplete(subEntity);
synchronized (AbsGroupUtil.LOCK) {
mCompleteNum++;
//如果子任务完成的数量和总任务数一致,表示任务组任务已经完成
if (mCompleteNum == mGroupSize) {
closeTimer();
mListener.onComplete();
} else if (mFailMap.size() > 0
&& mStopNum + mCompleteNum + mFailMap.size() >= mGroupSize
&& !isStop) {
//如果子任务完成数量加上失败的数量和总任务数一致,则任务组停止下载
closeTimer();
mListener.onStop(mCurrentLocation);
}
}
}
@Override public void onFail(boolean needRetry, BaseException e) {
subEntity.setFailNum(subEntity.getFailNum() + 1);
saveData(IEntity.STATE_FAIL, lastLen);
handleSpeed(0);
reTry(needRetry);
}
/**
* 重试下载只有全部都下载失败才会执行任务组的整体重试否则只会执行单个子任务的重试
*/
private void reTry(boolean needRetry) {
synchronized (AbsGroupUtil.LOCK) {
Downloader dt = mDownloaderMap.get(subEntity.getUrl());
if (!isCancel && !isStop && dt != null
&& !dt.isBreak()
&& needRetry
&& subEntity.getFailNum() < 3
&& (NetUtils.isConnected(AriaManager.APP) || isNotNetRetry)) {
ALog.d(TAG, "downloader retry");
reStartTask(dt);
} else {
mFailMap.put(subTaskWrapper.getKey(), subTaskWrapper);
mListener.onSubFail(subEntity, new TaskException(TAG,
String.format("任务组子任务【%s】下载失败,下载地址【%s】", subEntity.getFileName(),
subEntity.getUrl())));
if (mFailMap.size() == mExeMap.size() || mFailMap.size() + mCompleteNum == mGroupSize) {
closeTimer();
}
if (mFailMap.size() == mGroupSize) {
mListener.onFail(true, new TaskException(TAG,
String.format("任务组【%s】下载失败", mGTWrapper.getEntity().getGroupHash())));
} else if (mFailMap.size() + mCompleteNum >= mExeMap.size()) {
mListener.onStop(mCurrentLocation);
}
}
}
}
private void reStartTask(final Downloader dt) {
if (timer == null || timer.isShutdown()) {
timer = new ScheduledThreadPoolExecutor(1);
}
timer.schedule(new Runnable() {
@Override public void run() {
if (dt != null) {
dt.retryTask();
}
}
}, 5, TimeUnit.SECONDS);
}
private void handleSpeed(long speed) {
subEntity.setSpeed(speed);
subEntity.setConvertSpeed(
speed <= 0 ? "" : String.format("%s/s", CommonUtil.formatFileSize(speed)));
subEntity.setPercent((int) (subEntity.getFileSize() <= 0 ? 0
: subEntity.getCurrentProgress() * 100 / subEntity.getFileSize()));
}
private void saveData(int state, long location) {
subTaskWrapper.setState(state);
subEntity.setState(state);
subEntity.setComplete(state == IEntity.STATE_COMPLETE);
if (state == IEntity.STATE_CANCEL) {
subEntity.deleteData();
} else if (state == IEntity.STATE_STOP) {
subEntity.setStopTime(System.currentTimeMillis());
} else if (subEntity.isComplete()) {
subEntity.setCompleteTime(System.currentTimeMillis());
subEntity.setCurrentProgress(subEntity.getFileSize());
} else if (location > 0) {
subEntity.setCurrentProgress(location);
}
}
@Override public void supportBreakpoint(boolean support) {
}
SubDownloadLoader createSubLoader(DTaskWrapper taskWrapper) {
SubDownloadLoader loader = new SubDownloadLoader(mScheduler, taskWrapper);
mExeLoader.put(loader.getKey(), loader);
mSubQueue.startTask(loader);
return loader;
}
}

@ -0,0 +1,146 @@
/*
* 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.group;
import android.os.Handler;
import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.inf.IDownloadListener;
import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.core.scheduler.ISchedulers;
import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.util.CommonUtil;
/**
* 子任务事件监听
*/
class ChildDownloadListener implements IDownloadListener {
private DownloadEntity subEntity;
private int RUN_SAVE_INTERVAL = 5 * 1000; //5s保存一次下载中的进度
private long lastSaveTime;
private long lastLen;
private Handler schedulers;
private SubDownloadLoader loader;
ChildDownloadListener(Handler schedulers, SubDownloadLoader loader) {
this.loader = loader;
this.schedulers = schedulers;
subEntity = loader.getEntity();
subEntity.setFailNum(0);
lastLen = subEntity.getCurrentProgress();
lastSaveTime = System.currentTimeMillis();
}
@Override public void supportBreakpoint(boolean support) {
}
@Override public void onPre() {
saveData(IEntity.STATE_PRE, -1);
}
@Override public void onPostPre(long fileSize) {
subEntity.setFileSize(fileSize);
subEntity.setConvertFileSize(CommonUtil.formatFileSize(fileSize));
saveData(IEntity.STATE_POST_PRE, -1);
sendToTarget(ISchedulers.POST_PRE, loader);
}
@Override public void onResume(long resumeLocation) {
lastLen = resumeLocation;
saveData(IEntity.STATE_POST_PRE, IEntity.STATE_RUNNING);
sendToTarget(ISchedulers.START, loader);
}
@Override public void onStart(long startLocation) {
lastLen = startLocation;
saveData(IEntity.STATE_POST_PRE, IEntity.STATE_RUNNING);
sendToTarget(ISchedulers.START, loader);
}
@Override public void onProgress(long currentLocation) {
long diff = currentLocation - lastLen;
//mCurrentLocation += speed;
subEntity.setCurrentProgress(currentLocation);
handleSpeed(diff);
sendToTarget(ISchedulers.RUNNING, loader);
if (System.currentTimeMillis() - lastSaveTime >= RUN_SAVE_INTERVAL) {
saveData(IEntity.STATE_RUNNING, currentLocation);
lastSaveTime = System.currentTimeMillis();
}
lastLen = currentLocation;
}
@Override public void onStop(long stopLocation) {
handleSpeed(0);
saveData(IEntity.STATE_STOP, stopLocation);
sendToTarget(ISchedulers.STOP, loader);
}
/**
* 组合任务子任务不允许删除
*/
@Deprecated
@Override public void onCancel() {
}
@Override public void onComplete() {
subEntity.setComplete(true);
saveData(IEntity.STATE_COMPLETE, subEntity.getFileSize());
handleSpeed(0);
sendToTarget(ISchedulers.COMPLETE, loader);
}
@Override public void onFail(boolean needRetry, BaseException e) {
subEntity.setFailNum(subEntity.getFailNum() + 1);
saveData(IEntity.STATE_FAIL, lastLen);
handleSpeed(0);
sendToTarget(ISchedulers.FAIL, loader);
}
private void handleSpeed(long speed) {
subEntity.setSpeed(speed);
subEntity.setConvertSpeed(
speed <= 0 ? "" : String.format("%s/s", CommonUtil.formatFileSize(speed)));
subEntity.setPercent((int) (subEntity.getFileSize() <= 0 ? 0
: subEntity.getCurrentProgress() * 100 / subEntity.getFileSize()));
}
private void saveData(int state, long location) {
loader.getWrapper().setState(state);
subEntity.setState(state);
subEntity.setComplete(state == IEntity.STATE_COMPLETE);
if (state == IEntity.STATE_CANCEL) {
subEntity.deleteData();
} else if (state == IEntity.STATE_STOP) {
subEntity.setStopTime(System.currentTimeMillis());
} else if (subEntity.isComplete()) {
subEntity.setCompleteTime(System.currentTimeMillis());
subEntity.setCurrentProgress(subEntity.getFileSize());
} else if (location > 0) {
subEntity.setCurrentProgress(location);
}
}
/**
* 发送状态到子任务调度器{@link SimpleSchedulers}让调度器处理任务调度
*
* @param state {@link ISchedulers}
*/
private void sendToTarget(int state, SubDownloadLoader util) {
schedulers.obtainMessage(state, util).sendToTarget();
}
}

@ -15,48 +15,19 @@
*/
package com.arialyy.aria.core.download.group;
import android.util.SparseArray;
import com.arialyy.aria.core.common.CompleteInfo;
import com.arialyy.aria.core.common.IUtil;
import com.arialyy.aria.core.common.OnFileInfoCallback;
import com.arialyy.aria.core.download.DGTaskWrapper;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.download.downloader.HttpFileInfoThread;
import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.util.ALog;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Created by AriaL on 2017/6/30.
* 任务组下载工具
*/
public class DownloadGroupUtil extends AbsGroupUtil implements IUtil {
private final String TAG = "DownloadGroupUtil";
private ExecutorService mInfoPool;
/**
* 初始化完成的任务数
*/
private int mInitCompleteNum;
/**
* 初始化失败的任务数
*/
private int mInitFailNum;
private boolean isStop = false;
private boolean isStart = false;
private int mExeNum;
/**
* 文件信息回调组
*/
private SparseArray<OnFileInfoCallback> mFileInfoCallbacks = new SparseArray<>();
public DownloadGroupUtil(IDownloadGroupListener listener, DGTaskWrapper taskWrapper) {
super(listener, taskWrapper);
mInfoPool = Executors.newCachedThreadPool();
}
@Override int getTaskType() {
@ -65,121 +36,22 @@ public class DownloadGroupUtil extends AbsGroupUtil implements IUtil {
@Override public void onCancel() {
super.onCancel();
isStop = true;
if (!mInfoPool.isShutdown()) {
mInfoPool.shutdown();
}
}
@Override protected void onStop() {
super.onStop();
isStop = true;
if (!mInfoPool.isShutdown()) {
mInfoPool.shutdown();
}
}
@Override protected void onStart() {
onPre();
isStop = false;
if (mCompleteNum == mGroupSize) {
super.onStart();
if (mState.getCompleteNum() == mState.getSubSize()) {
mListener.onComplete();
return;
}
if (mExeMap.size() == 0) {
mListener.onFail(false, new TaskException(TAG,
String.format("任务组【%s】无可执行任务", mGTWrapper.getEntity().getGroupHash())));
return;
}
Set<String> keys = mExeMap.keySet();
mExeNum = mExeMap.size();
for (String key : keys) {
DTaskWrapper taskEntity = mExeMap.get(key);
if (taskEntity != null) {
if (taskEntity.getState() != IEntity.STATE_FAIL
&& taskEntity.getState() != IEntity.STATE_WAIT) {
mInitCompleteNum++;
createChildDownload(taskEntity);
checkStartFlow();
} else {
mInfoPool.execute(createFileInfoThread(taskEntity));
} else {
for (DTaskWrapper wrapper : mGTWrapper.getSubTaskWrapper()) {
if (wrapper.getState() != IEntity.STATE_COMPLETE) {
createSubLoader(wrapper);
}
}
}
if (mCurrentLocation == mTotalLen) {
mListener.onComplete();
}
}
/**
* 创建文件信息获取线程
*/
private HttpFileInfoThread createFileInfoThread(DTaskWrapper taskEntity) {
OnFileInfoCallback callback = mFileInfoCallbacks.get(taskEntity.hashCode());
if (callback == null) {
callback = new OnFileInfoCallback() {
int failNum = 0;
@Override public void onComplete(String url, CompleteInfo info) {
if (isStop) return;
DTaskWrapper te = mExeMap.get(url);
if (te != null) {
if (isNeedLoadFileSize) {
mTotalLen += te.getEntity().getFileSize();
}
createChildDownload(te);
}
mInitCompleteNum++;
checkStartFlow();
}
@Override public void onFail(String url, BaseException e, boolean needRetry) {
if (isStop) return;
ALog.e(TAG, String.format("任务【%s】初始化失败", url));
DTaskWrapper te = mExeMap.get(url);
if (te != null) {
mFailMap.put(url, te);
mFileInfoCallbacks.put(te.hashCode(), this);
mExeMap.remove(url);
}
//404链接不重试下载
//if (failNum < 3 && !errorMsg.contains("错误码:404") && !errorMsg.contains(
// "UnknownHostException")) {
// mInfoPool.execute(createFileInfoThread(te));
//} else {
// mInitFailNum++;
//}
//failNum++;
mInitFailNum++;
checkStartFlow();
}
};
}
return new HttpFileInfoThread(taskEntity, callback);
}
/**
* 检查能否启动下载流程
*/
private void checkStartFlow() {
synchronized (DownloadGroupUtil.class) {
if (isStop) {
closeTimer();
return;
}
if (mInitFailNum == mExeNum) {
closeTimer();
mListener.onFail(true, new TaskException(TAG,
String.format("任务组【%s】初始化失败", mGTWrapper.getEntity().getGroupHash())));
}
if (!isStart && mInitCompleteNum + mInitFailNum == mExeNum || !isNeedLoadFileSize) {
startRunningFlow();
updateFileSize();
isStart = true;
}
}
}
}

@ -19,8 +19,8 @@ import com.arialyy.aria.core.common.CompleteInfo;
import com.arialyy.aria.core.common.OnFileInfoCallback;
import com.arialyy.aria.core.download.DGTaskWrapper;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.exception.BaseException;
import java.util.Set;
/**
* Created by Aria.Lao on 2017/7/27. ftp文件夹下载工具
@ -39,23 +39,16 @@ public class FtpDirDownloadUtil extends AbsGroupUtil {
@Override protected void onStart() {
super.onStart();
if (mGTWrapper.getEntity().getFileSize() > 1) {
onPre();
startDownload();
} else {
new FtpDirInfoThread(mGTWrapper, new OnFileInfoCallback() {
@Override public void onComplete(String url, CompleteInfo info) {
if (info.code >= 200 && info.code < 300) {
onPre();
startDownload();
}
}
@Override public void onFail(String url, BaseException e, boolean needRetry) {
DTaskWrapper te = mExeMap.get(url);
if (te != null) {
mFailMap.put(url, te);
mExeMap.remove(url);
}
mListener.onFail(needRetry, e);
}
}).start();
@ -63,23 +56,10 @@ public class FtpDirDownloadUtil extends AbsGroupUtil {
}
private void startDownload() {
if (mCompleteNum == mGroupSize) {
mListener.onComplete();
return;
}
int i = 0;
Set<String> keys = mExeMap.keySet();
for (String key : keys) {
DTaskWrapper taskEntity = mExeMap.get(key);
if (taskEntity != null) {
createChildDownload(taskEntity);
i++;
for (DTaskWrapper wrapper : mGTWrapper.getSubTaskWrapper()) {
if (wrapper.getState() != IEntity.STATE_COMPLETE) {
createSubLoader(wrapper);
}
}
if (mExeMap.size() == 0) {
mListener.onComplete();
} else if (i == mExeMap.size()) {
startRunningFlow();
}
}
}

@ -0,0 +1,169 @@
/*
* 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.group;
import com.arialyy.aria.core.inf.AbsTaskWrapper;
import java.util.HashSet;
import java.util.Set;
/**
* 组合任务执行中的状态信息
*/
class GroupRunState {
/**
* 子任务数
*/
private int mSubSize;
/**
* 已经完成的任务数
*/
private int mCompleteNum;
/**
* 失败的任务数
*/
private int mFailNum;
/**
* 停止的任务数
*/
private int mStopNum;
/**
* 当前进度
*/
private long mProgress;
/**
* 组合任务监听
*/
IDownloadGroupListener listener;
/**
* 子任务队列
*/
SimpleSubQueue queue;
/**
* 是否在执行
*/
boolean isRunning = false;
/**
* 子任务失败停止记录用于当子任务失败重新被用户点击开始时更新{@link #mStopNum}{@link #mFailNum}
* 保存的数据为子任务key
*/
private Set<String> mFailTemp = new HashSet<>(), mStopTemp = new HashSet<>();
private String mGroupHash;
GroupRunState(String groupHash, IDownloadGroupListener listener, int subSize,
SimpleSubQueue queue) {
this.listener = listener;
this.queue = queue;
mSubSize = subSize;
mGroupHash = groupHash;
}
String getGroupHash() {
return mGroupHash;
}
/**
* 获取组合任务子任务数
*/
int getSubSize() {
return mSubSize;
}
/**
* 获取失败的数量
*/
int getFailNum() {
return mFailNum;
}
/**
* 获取停止的数量
*/
int getStopNum() {
return mStopNum;
}
/**
* 获取完成的数量
*/
int getCompleteNum() {
return mCompleteNum;
}
/**
* 获取当前组合任务总进度
*/
long getProgress() {
return mProgress;
}
/**
* 更新完成的数量mCompleteNum + 1
*/
void updateCompleteNum() {
mCompleteNum++;
}
/**
* 更新任务进度
*/
void updateProgress(long newProgress) {
this.mProgress = newProgress;
}
/**
* 当子任务开始时更新停止\失败的任务数
*
* @param key {@link AbsTaskWrapper#getKey()}
*/
void updateCount(String key) {
if (mFailTemp.contains(key)) {
mFailTemp.remove(key);
mFailNum--;
} else if (mStopTemp.contains(key)) {
mStopTemp.remove(key);
mStopNum--;
}
}
/**
* 统计子任务停止的数量
*
* @param key {@link AbsTaskWrapper#getKey()}
*/
void countStopNum(String key) {
mStopTemp.add(key);
mStopNum++;
}
/**
* 统计子任务失败的数量
*
* @param key {@link AbsTaskWrapper#getKey()}
*/
void countFailNum(String key) {
mFailTemp.add(key);
mFailNum++;
}
}

@ -16,13 +16,15 @@
package com.arialyy.aria.core.download.group;
import com.arialyy.aria.core.common.AbsFileer;
import com.arialyy.aria.core.common.IUtil;
import com.arialyy.aria.core.config.DGroupConfig;
/**
* 组合任务子任务队列
*
* @param <Fileer> {@link AbsFileer}下载器
*/
interface ISubQueue<Fileer extends AbsFileer> {
interface ISubQueue<Fileer extends IUtil> {
/**
* 添加任务
@ -32,6 +34,9 @@ interface ISubQueue<Fileer extends AbsFileer> {
/**
* 开始任务
* 如果执行队列没有达到上限则启动任务
* 如果执行队列已经到达上限则将任务添加到等待队列总
* 队列上限配置{@link DGroupConfig#setSubMaxTaskNum(int)}
*/
void startTask(Fileer fileer);
@ -61,4 +66,9 @@ interface ISubQueue<Fileer extends AbsFileer> {
* 获取下一个任务
*/
Fileer getNextTask();
/**
* 清空缓存队列和执行队列
*/
void clear();
}

@ -16,100 +16,164 @@
package com.arialyy.aria.core.download.group;
import android.os.CountDownTimer;
import android.os.Message;
import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.core.config.Configuration;
import com.arialyy.aria.core.download.DownloadTask;
import com.arialyy.aria.core.download.downloader.Downloader;
import com.arialyy.aria.core.inf.AbsEntity;
import com.arialyy.aria.core.scheduler.ISchedulers;
import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.NetUtils;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 组合任务子任务调度器用于调度任务的开始停止失败完成等情况
* 该调度器生命周期和{@link AbsGroupUtil}生命周期一致
*/
public class SimpleSchedulers implements ISchedulers<DownloadTask> {
class SimpleSchedulers implements ISchedulers {
private static final String TAG = "SimpleSchedulers";
private SimpleSubQueue mQueue = SimpleSubQueue.newInstance();
private SimpleSchedulers() {
private SimpleSubQueue mQueue;
private GroupRunState mGState;
private SimpleSchedulers(GroupRunState state) {
mQueue = state.queue;
mGState = state;
}
public static SimpleSchedulers newInstance() {
public static SimpleSchedulers newInstance(GroupRunState state) {
return new SimpleSchedulers();
return new SimpleSchedulers(state);
}
@Override public boolean handleMessage(Message msg) {
Downloader loader = (Downloader) msg.obj;
SubDownloadLoader loader = (SubDownloadLoader) msg.obj;
switch (msg.what) {
case ADD:
mQueue.addTask(loader);
case RUNNING:
mGState.listener.onSubRunning(loader.getEntity());
break;
case PRE:
mGState.listener.onSubPre(loader.getEntity());
mGState.updateCount(loader.getKey());
break;
case START:
mQueue.startTask(loader);
mGState.listener.onSubStart(loader.getEntity());
break;
case STOP:
mQueue.stopTask(loader);
startNext();
handleStop(loader);
break;
case COMPLETE:
mQueue.removeTaskFromExecQ(loader);
startNext();
handleComplete(loader);
break;
case FAIL:
handleFail(loader);
break;
}
return true;
}
/**
* 如果有等待中的任务则启动下一任务
* 处理子任务失败的情况
* 1子任务失败次数大于等于配置的重试次数才能认为子任务停止
* 2stopNum + failNum + completeNum == subSize则认为组合任务停止
* 3failNum == subSize只有全部的子任务都失败了才能任务组合任务失败
*/
private void startNext() {
Downloader next = mQueue.getNextTask();
if (next != null) {
mQueue.startTask(next);
} else {
ALog.i(TAG, "没有下一任务");
}
}
/**
* 处理失败的任务
*/
private void handleFail(final Downloader loader) {
private synchronized void handleFail(final SubDownloadLoader loader) {
Configuration config = Configuration.getInstance();
long interval = config.downloadCfg.getReTryInterval();
int num = config.downloadCfg.getReTryNum();
long interval = config.dGroupCfg.getSubReTryInterval();
int num = config.dGroupCfg.getSubReTryNum();
boolean isNotNetRetry = config.appCfg.isNotNetRetry();
final int reTryNum = num;
if ((!NetUtils.isConnected(AriaManager.APP) && !isNotNetRetry)
|| loader.getEntity().getFailNum() > reTryNum) {
startNext();
mQueue.removeTaskFromExecQ(loader);
mGState.listener.onSubFail(loader.getEntity(), new TaskException(TAG,
String.format("任务组子任务【%s】下载失败,下载地址【%s】", loader.getEntity().getFileName(),
loader.getEntity().getUrl())));
mGState.countFailNum(loader.getKey());
if (mGState.getFailNum() == mGState.getSubSize()
|| mGState.getStopNum() + mGState.getFailNum() + mGState.getCompleteNum()
== mGState.getSubSize()) {
mGState.isRunning = false;
mGState.listener.onFail(true, new TaskException(TAG,
String.format("任务组【%s】下载失败", mGState.getGroupHash())));
} else {
startNext();
}
return;
}
final ScheduledThreadPoolExecutor timer = new ScheduledThreadPoolExecutor(1);
CountDownTimer timer = new CountDownTimer(interval, 1000) {
@Override public void onTick(long millisUntilFinished) {
}
@Override public void onFinish() {
timer.schedule(new Runnable() {
@Override public void run() {
AbsEntity entity = loader.getEntity();
if (entity.getFailNum() <= reTryNum) {
ALog.d(TAG, String.format("任务【%s】开始重试", loader.getEntity().getFileName()));
loader.retryTask();
loader.reStart();
} else {
startNext();
}
}
};
timer.start();
}, interval, TimeUnit.MILLISECONDS);
}
/**
* 处理子任务停止的情况
* 1所有的子任务已经停止则认为组合任务停止
* 2completeNum + failNum + stopNum = subSize则认为组合任务停止
*/
private synchronized void handleStop(SubDownloadLoader loader) {
mGState.listener.onSubStop(loader.getEntity());
mGState.countStopNum(loader.getKey());
if (mGState.getStopNum() == mGState.getSubSize()
|| mGState.getStopNum() + mGState.getCompleteNum() + mGState.getFailNum()
== mGState.getSubSize()) {
mGState.isRunning = false;
mGState.listener.onStop(mGState.getProgress());
} else {
startNext();
}
}
/**
* 处理子任务完成的情况有以下三种情况
* 1已经没有缓存的子任务并且停止的子任务是数{@link GroupRunState#getStopNum()} ()}为0失败的子任数{@link
* GroupRunState#getFailNum()}为0则认为组合任务已经完成
* 2已经没有缓存的子任务并且停止的子任务是数{@link GroupRunState#getCompleteNum()}不为0或者失败的子任数{@link
* GroupRunState#getFailNum()}不为0则认为组合任务被停止
* 3只有有缓存的子任务则任务组合任务没有完成
*/
private synchronized void handleComplete(SubDownloadLoader loader) {
ALog.d(TAG, String.format("子任务【%s】完成", loader.getEntity().getFileName()));
mGState.listener.onSubComplete(loader.getEntity());
mQueue.removeTaskFromExecQ(loader);
mGState.updateCompleteNum();
ALog.d(TAG, String.format("总任务数:%s,完成的任务数:%s,失败的任务数:%s,停止的任务数:%s", mGState.getSubSize(),
mGState.getCompleteNum(), mGState.getFailNum(), mGState.getStopNum()));
if (mGState.getCompleteNum() == mGState.getSubSize()) {
if (mGState.getStopNum() == 0 && mGState.getFailNum() == 0) {
mGState.listener.onComplete();
} else {
mGState.listener.onStop(mGState.getProgress());
}
mGState.isRunning = false;
} else {
startNext();
}
}
/**
* 如果有等待中的任务则启动下一任务
*/
private void startNext() {
SubDownloadLoader next = mQueue.getNextTask();
if (next != null) {
ALog.d(TAG, String.format("启动任务:%s", next.getEntity().getFileName()));
mQueue.startTask(next);
} else {
ALog.i(TAG, "没有下一子任务");
}
}
}

@ -16,7 +16,6 @@
package com.arialyy.aria.core.download.group;
import com.arialyy.aria.core.config.Configuration;
import com.arialyy.aria.core.download.downloader.Downloader;
import com.arialyy.aria.util.ALog;
import java.util.ArrayList;
import java.util.Collection;
@ -29,16 +28,16 @@ import java.util.Set;
/**
* 组合任务队列该队列生命周期和{@link AbsGroupUtil}生命周期一致
*/
class SimpleSubQueue implements ISubQueue<Downloader> {
class SimpleSubQueue implements ISubQueue<SubDownloadLoader> {
private static final String TAG = "SimpleSubQueue";
/**
* 缓存下载器
*/
private Map<String, Downloader> mCache = new LinkedHashMap<>();
private Map<String, SubDownloadLoader> mCache = new LinkedHashMap<>();
/**
* 执行中的下载器
*/
private Map<String, Downloader> mExec = new LinkedHashMap<>();
private Map<String, SubDownloadLoader> mExec = new LinkedHashMap<>();
/**
* 最大执行任务数
@ -53,22 +52,26 @@ class SimpleSubQueue implements ISubQueue<Downloader> {
return new SimpleSubQueue();
}
@Override public void addTask(Downloader fileer) {
public Map<String, SubDownloadLoader> getExec() {
return mExec;
}
@Override public void addTask(SubDownloadLoader fileer) {
mCache.put(fileer.getKey(), fileer);
}
@Override public void startTask(Downloader fileer) {
@Override public void startTask(SubDownloadLoader fileer) {
if (mExec.size() < mExecSize) {
mCache.remove(fileer.getKey());
mExec.put(fileer.getKey(), fileer);
fileer.start();
} else {
ALog.d(TAG, String.format("执行队列已满,任务见缓冲到缓存器中,key: %s", fileer.getKey()));
ALog.d(TAG, String.format("执行队列已满,任务进入缓存器中,key: %s", fileer.getKey()));
addTask(fileer);
}
}
@Override public void stopTask(Downloader fileer) {
@Override public void stopTask(SubDownloadLoader fileer) {
fileer.stop();
mExec.remove(fileer.getKey());
}
@ -89,7 +92,7 @@ class SimpleSubQueue implements ISubQueue<Downloader> {
if (oldSize < num) { // 处理队列变小的情况,该情况下将停止队尾任务,并将这些任务添加到缓存队列中
if (mExec.size() > num) {
Set<String> keys = mExec.keySet();
List<Downloader> caches = new ArrayList<>();
List<SubDownloadLoader> caches = new ArrayList<>();
int i = 0;
for (String key : keys) {
if (i > num) {
@ -97,20 +100,20 @@ class SimpleSubQueue implements ISubQueue<Downloader> {
}
i++;
}
Collection<Downloader> temp = mCache.values();
Collection<SubDownloadLoader> temp = mCache.values();
mCache.clear();
ALog.d(TAG, String.format("测试, map size: %s", mCache.size()));
for (Downloader cache : caches) {
for (SubDownloadLoader cache : caches) {
addTask(cache);
}
for (Downloader t : temp) {
for (SubDownloadLoader t : temp) {
addTask(t);
}
}
} else { // 处理队列变大的情况,该情况下将增加任务
if (mExec.size() < num) {
for (int i = 0; i < diff; i++) {
Downloader next = getNextTask();
SubDownloadLoader next = getNextTask();
if (next != null) {
startTask(next);
} else {
@ -121,7 +124,7 @@ class SimpleSubQueue implements ISubQueue<Downloader> {
}
}
@Override public void removeTaskFromExecQ(Downloader fileer) {
@Override public void removeTaskFromExecQ(SubDownloadLoader fileer) {
if (mExec.containsKey(fileer.getKey())) {
if (fileer.isRunning()) {
fileer.stop();
@ -130,16 +133,21 @@ class SimpleSubQueue implements ISubQueue<Downloader> {
}
}
@Override public void removeTask(Downloader fileer) {
@Override public void removeTask(SubDownloadLoader fileer) {
removeTaskFromExecQ(fileer);
mCache.remove(fileer.getKey());
}
@Override public Downloader getNextTask() {
@Override public SubDownloadLoader getNextTask() {
Iterator<String> keys = mCache.keySet().iterator();
if (keys.hasNext()) {
return mCache.get(keys.next());
}
return null;
}
@Override public void clear() {
mCache.clear();
mExec.clear();
}
}

@ -0,0 +1,113 @@
/*
* 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.group;
import android.os.Handler;
import com.arialyy.aria.core.common.CompleteInfo;
import com.arialyy.aria.core.common.IUtil;
import com.arialyy.aria.core.common.OnFileInfoCallback;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.download.downloader.Downloader;
import com.arialyy.aria.core.download.downloader.HttpFileInfoThread;
import com.arialyy.aria.core.scheduler.ISchedulers;
import com.arialyy.aria.exception.BaseException;
/**
* 子任务下载工具负责创建{@link Downloader}
*/
class SubDownloadLoader implements IUtil {
private Downloader mDownloader;
private DTaskWrapper mWrapper;
private Handler mSchedulers;
private ChildDownloadListener mListener;
SubDownloadLoader(Handler schedulers, DTaskWrapper taskWrapper) {
mWrapper = taskWrapper;
mSchedulers = schedulers;
mListener = new ChildDownloadListener(mSchedulers, SubDownloadLoader.this);
}
@Override public String getKey() {
return mWrapper.getKey();
}
public DTaskWrapper getWrapper() {
return mWrapper;
}
public DownloadEntity getEntity() {
return mWrapper.getEntity();
}
/**
* 重新开始任务
*/
void reStart() {
if (mDownloader != null) {
mDownloader.retryTask();
}
}
@Override public long getFileSize() {
return mDownloader == null ? -1 : mDownloader.getFileSize();
}
@Override public long getCurrentLocation() {
return mDownloader == null ? -1 : mDownloader.getCurrentLocation();
}
@Override public boolean isRunning() {
return mDownloader != null && mDownloader.isRunning();
}
@Override public void cancel() {
if (mDownloader != null) {
mDownloader.cancel();
}
}
@Override public void stop() {
if (mDownloader != null) {
mDownloader.stop();
}
}
@Override public void start() {
new Thread(new HttpFileInfoThread(mWrapper, new OnFileInfoCallback() {
@Override public void onComplete(String url, CompleteInfo info) {
mDownloader = new Downloader(mListener, mWrapper);
mDownloader.start();
}
@Override public void onFail(String url, BaseException e, boolean needRetry) {
mSchedulers.obtainMessage(ISchedulers.FAIL, SubDownloadLoader.this);
}
})).start();
}
@Override public void resume() {
start();
}
@Override public void setMaxSpeed(int speed) {
if (mDownloader != null) {
mDownloader.setMaxSpeed(speed);
}
}
}

@ -44,7 +44,7 @@ public abstract class AbsEntity extends DbEntity implements IEntity, Parcelable,
/**
* 文件大小
*/
private long fileSize = 1;
private long fileSize = 0;
/**
* 转换后的文件大小
*/

@ -49,15 +49,4 @@ public abstract class AbsGroupTask<ENTITY extends AbsEntity, TASK_ENTITY extends
((AbsGroupUtil) mUtil).stopSubTask(url);
}
}
/**
* 删除子任务组中的子任务
*
* @param url 子任务下载地址
*/
public void cancelSubTask(String url) {
if (mUtil != null) {
((AbsGroupUtil) mUtil).cancelSubTask(url);
}
}
}

@ -65,20 +65,6 @@ public class SubTaskManager {
}
}
/**
* 删除子任务组中的子任务
*
* @param url 子任务下载地址
*/
public void cancelSubTask(String url) {
if (checkUrl(url)) {
AriaManager.getInstance(AriaManager.APP)
.setCmd(
CommonUtil.createGroupCmd(mTargetName, mEntity, GroupCmdFactory.SUB_TASK_CANCEL, url))
.exe();
}
}
/**
* 检查任务地址
*

@ -39,12 +39,14 @@ import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* Created by lyy on 2017/6/4. 事件调度器用于处理任务状态的调度
*/
abstract class AbsSchedulers<TASK_ENTITY extends AbsTaskWrapper, TASK extends AbsTask,
QUEUE extends ITaskQueue<TASK, TASK_ENTITY>> implements ISchedulers<TASK> {
QUEUE extends ITaskQueue<TASK, TASK_ENTITY>> implements ISchedulers {
private final String TAG = "AbsSchedulers";
protected QUEUE mQueue;
@ -344,12 +346,10 @@ abstract class AbsSchedulers<TASK_ENTITY extends AbsTaskWrapper, TASK extends Ab
return;
}
CountDownTimer timer = new CountDownTimer(interval, 1000) {
@Override public void onTick(long millisUntilFinished) {
final ScheduledThreadPoolExecutor timer = new ScheduledThreadPoolExecutor(1);
}
@Override public void onFinish() {
timer.schedule(new Runnable() {
@Override public void run() {
AbsEntity entity = task.getTaskWrapper().getEntity();
if (entity.getFailNum() <= reTryNum) {
ALog.d(TAG, String.format("任务【%s】开始重试", task.getTaskName()));
@ -361,8 +361,7 @@ abstract class AbsSchedulers<TASK_ENTITY extends AbsTaskWrapper, TASK extends Ab
TaskWrapperManager.getInstance().removeTaskWrapper(task.getKey());
}
}
};
timer.start();
}, interval, TimeUnit.MILLISECONDS);
}
/**

@ -26,7 +26,7 @@ import com.arialyy.aria.core.upload.UploadEntity;
/**
* Created by lyy on 2016/11/2. 调度器功能接口
*/
public interface ISchedulers<Task extends AbsTask> extends Handler.Callback {
public interface ISchedulers extends Handler.Callback {
String ARIA_TASK_INFO_ACTION = "ARIA_TASK_INFO_ACTION";
/**
@ -113,10 +113,6 @@ public interface ISchedulers<Task extends AbsTask> extends Handler.Callback {
* 等待
*/
int WAIT = 10;
/**
* 添加任务
*/
int ADD = 11;
/**
* 组合任务子任务预处理

@ -33,26 +33,26 @@ public class SimpleUploadUtil implements IUtil, Runnable {
private static final String TAG = "SimpleUploadUtil";
private UploadEntity mUploadEntity;
private UTaskWrapper mTaskEntity;
private UTaskWrapper mTaskWrapper;
private IUploadListener mListener;
private Uploader mUploader;
public SimpleUploadUtil(UTaskWrapper taskEntity, IUploadListener listener) {
mTaskEntity = taskEntity;
CheckUtil.checkTaskEntity(taskEntity);
mUploadEntity = taskEntity.getEntity();
public SimpleUploadUtil(UTaskWrapper taskWrapper, IUploadListener listener) {
mTaskWrapper = taskWrapper;
CheckUtil.checkTaskEntity(taskWrapper);
mUploadEntity = taskWrapper.getEntity();
if (listener == null) {
throw new IllegalArgumentException("上传监听不能为空");
}
mListener = listener;
mUploader = new Uploader(mListener, taskEntity);
mUploader = new Uploader(mListener, taskWrapper);
}
@Override public void run() {
mListener.onPre();
switch (mTaskEntity.getRequestType()) {
switch (mTaskWrapper.getRequestType()) {
case AbsTaskWrapper.U_FTP:
new FtpFileInfoThread(mTaskEntity, new OnFileInfoCallback() {
new FtpFileInfoThread(mTaskWrapper, new OnFileInfoCallback() {
@Override public void onComplete(String url, CompleteInfo info) {
if (info.code == FtpFileInfoThread.CODE_COMPLETE) {
mListener.onComplete();
@ -72,6 +72,10 @@ public class SimpleUploadUtil implements IUtil, Runnable {
}
}
@Override public String getKey() {
return mTaskWrapper.getKey();
}
@Override public long getFileSize() {
return mUploader.getFileSize();
}

@ -131,7 +131,7 @@
<!-- =============================以下为子任务的配置====================================-->
<!--能同时下载的子任务最大任务数,默认3-->
<subMaxTaskNum value="3"/>
<subMaxTaskNum value="1"/>
<!--子任务下载失败时的重试次数,默认为5-->
<subReTryNum value="5"/>
@ -155,7 +155,7 @@
<convertSpeed value="true"/>
<!--子任务的最大下载速度,0表示不限速,单位为:kb; -->
<maxSpeed value="128"/>
<maxSpeed value="0"/>
</dGroup>
</aria>

@ -91,6 +91,7 @@ public class DownloadGroupActivity extends BaseActivity<ActivityDownloadGroupBin
.setGroupAlias("任务组测试")
//.setSubFileName(getModule(GroupModule.class).getSubName2())
.setSubFileName(getModule(GroupModule.class).getSubName())
.setFileSize(114981416)
//.updateUrls(temp)
.start();
break;

Loading…
Cancel
Save