v3.6.6
laoyuyu 6 years ago
commit 5c6770bf5b
  1. 2
      Aria/build.gradle
  2. 12
      Aria/src/main/java/com/arialyy/aria/core/AriaManager.java
  3. 3
      Aria/src/main/java/com/arialyy/aria/core/ConfigHelper.java
  4. 34
      Aria/src/main/java/com/arialyy/aria/core/FtpUrlEntity.java
  5. 1
      Aria/src/main/java/com/arialyy/aria/core/command/AbsCmd.java
  6. 3
      Aria/src/main/java/com/arialyy/aria/core/command/AbsCmdFactory.java
  7. 8
      Aria/src/main/java/com/arialyy/aria/core/command/group/AbsGroupCmd.java
  8. 7
      Aria/src/main/java/com/arialyy/aria/core/command/group/GroupCancelCmd.java
  9. 6
      Aria/src/main/java/com/arialyy/aria/core/command/group/GroupCmdFactory.java
  10. 7
      Aria/src/main/java/com/arialyy/aria/core/command/group/GroupStartCmd.java
  11. 7
      Aria/src/main/java/com/arialyy/aria/core/command/group/GroupStopCmd.java
  12. 10
      Aria/src/main/java/com/arialyy/aria/core/command/normal/AbsNormalCmd.java
  13. 4
      Aria/src/main/java/com/arialyy/aria/core/command/normal/AddCmd.java
  14. 7
      Aria/src/main/java/com/arialyy/aria/core/command/normal/CancelAllCmd.java
  15. 8
      Aria/src/main/java/com/arialyy/aria/core/command/normal/CancelCmd.java
  16. 10
      Aria/src/main/java/com/arialyy/aria/core/command/normal/HighestPriorityCmd.java
  17. 19
      Aria/src/main/java/com/arialyy/aria/core/command/normal/NormalCmdFactory.java
  18. 11
      Aria/src/main/java/com/arialyy/aria/core/command/normal/ResumeAllCmd.java
  19. 15
      Aria/src/main/java/com/arialyy/aria/core/command/normal/StartCmd.java
  20. 7
      Aria/src/main/java/com/arialyy/aria/core/command/normal/StopAllCmd.java
  21. 10
      Aria/src/main/java/com/arialyy/aria/core/command/normal/StopCmd.java
  22. 26
      Aria/src/main/java/com/arialyy/aria/core/common/AEvent.java
  23. 309
      Aria/src/main/java/com/arialyy/aria/core/common/AbsFileer.java
  24. 244
      Aria/src/main/java/com/arialyy/aria/core/common/AbsThreadTask.java
  25. 104
      Aria/src/main/java/com/arialyy/aria/core/common/BandwidthLimiter.java
  26. 145
      Aria/src/main/java/com/arialyy/aria/core/common/BaseListener.java
  27. 6
      Aria/src/main/java/com/arialyy/aria/core/common/IUtil.java
  28. 6
      Aria/src/main/java/com/arialyy/aria/core/common/OnFileInfoCallback.java
  29. 39
      Aria/src/main/java/com/arialyy/aria/core/common/ProtocolType.java
  30. 86
      Aria/src/main/java/com/arialyy/aria/core/common/ProxyHelper.java
  31. 11
      Aria/src/main/java/com/arialyy/aria/core/common/StateConstance.java
  32. 4
      Aria/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java
  33. 5
      Aria/src/main/java/com/arialyy/aria/core/common/ThreadRecord.java
  34. 78
      Aria/src/main/java/com/arialyy/aria/core/common/ThreadTaskManager.java
  35. 128
      Aria/src/main/java/com/arialyy/aria/core/common/ftp/AbsFtpInfoThread.java
  36. 68
      Aria/src/main/java/com/arialyy/aria/core/common/ftp/AbsFtpThreadTask.java
  37. 104
      Aria/src/main/java/com/arialyy/aria/core/common/ftp/FTPSConfig.java
  38. 38
      Aria/src/main/java/com/arialyy/aria/core/common/ftp/FtpDelegate.java
  39. 52
      Aria/src/main/java/com/arialyy/aria/core/common/ftp/SSLSessionReuseFTPSClient.java
  40. 39
      Aria/src/main/java/com/arialyy/aria/core/common/http/HttpHeaderDelegate.java
  41. 86
      Aria/src/main/java/com/arialyy/aria/core/common/http/PostDelegate.java
  42. 11
      Aria/src/main/java/com/arialyy/aria/core/download/AbsDownloadTarget.java
  43. 119
      Aria/src/main/java/com/arialyy/aria/core/download/BaseDListener.java
  44. 2
      Aria/src/main/java/com/arialyy/aria/core/download/BaseGroupTarget.java
  45. 12
      Aria/src/main/java/com/arialyy/aria/core/download/BaseNormalTarget.java
  46. 45
      Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupListener.java
  47. 73
      Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTarget.java
  48. 55
      Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTask.java
  49. 45
      Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java
  50. 63
      Aria/src/main/java/com/arialyy/aria/core/download/DownloadTarget.java
  51. 91
      Aria/src/main/java/com/arialyy/aria/core/download/DownloadTask.java
  52. 5
      Aria/src/main/java/com/arialyy/aria/core/download/DownloadTaskEntity.java
  53. 55
      Aria/src/main/java/com/arialyy/aria/core/download/FtpDirDownloadTarget.java
  54. 40
      Aria/src/main/java/com/arialyy/aria/core/download/FtpDownloadTarget.java
  55. 96
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/AbsGroupUtil.java
  56. 31
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/ConnectionHelp.java
  57. 15
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/DownloadGroupUtil.java
  58. 30
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/Downloader.java
  59. 7
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpDirDownloadUtil.java
  60. 6
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpDirInfoThread.java
  61. 16
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpFileInfoThread.java
  62. 82
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpThreadTask.java
  63. 102
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpFileInfoThread.java
  64. 103
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpThreadTask.java
  65. 3
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/IDownloadGroupListener.java
  66. 24
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/SimpleDownloadUtil.java
  67. 3
      Aria/src/main/java/com/arialyy/aria/core/inf/AbsEntity.java
  68. 12
      Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupTask.java
  69. 11
      Aria/src/main/java/com/arialyy/aria/core/inf/AbsNormalTask.java
  70. 72
      Aria/src/main/java/com/arialyy/aria/core/inf/AbsTarget.java
  71. 115
      Aria/src/main/java/com/arialyy/aria/core/inf/AbsTask.java
  72. 27
      Aria/src/main/java/com/arialyy/aria/core/inf/AbsTaskEntity.java
  73. 7
      Aria/src/main/java/com/arialyy/aria/core/inf/IEventListener.java
  74. 9
      Aria/src/main/java/com/arialyy/aria/core/inf/IFtpTarget.java
  75. 10
      Aria/src/main/java/com/arialyy/aria/core/inf/IHttpHeaderDelegate.java
  76. 31
      Aria/src/main/java/com/arialyy/aria/core/inf/IPostDelegate.java
  77. 43
      Aria/src/main/java/com/arialyy/aria/core/inf/ITarget.java
  78. 16
      Aria/src/main/java/com/arialyy/aria/core/inf/ITask.java
  79. 22
      Aria/src/main/java/com/arialyy/aria/core/inf/TaskSchedulerType.java
  80. 6
      Aria/src/main/java/com/arialyy/aria/core/manager/DGTEFactory.java
  81. 135
      Aria/src/main/java/com/arialyy/aria/core/manager/ThreadTaskManager.java
  82. 32
      Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java
  83. 9
      Aria/src/main/java/com/arialyy/aria/core/queue/DownloadGroupTaskQueue.java
  84. 25
      Aria/src/main/java/com/arialyy/aria/core/queue/DownloadTaskQueue.java
  85. 3
      Aria/src/main/java/com/arialyy/aria/core/queue/ITaskQueue.java
  86. 25
      Aria/src/main/java/com/arialyy/aria/core/queue/TaskFactory.java
  87. 9
      Aria/src/main/java/com/arialyy/aria/core/queue/UploadTaskQueue.java
  88. 9
      Aria/src/main/java/com/arialyy/aria/core/scheduler/AbsSchedulerListener.java
  89. 29
      Aria/src/main/java/com/arialyy/aria/core/scheduler/AbsSchedulers.java
  90. 10
      Aria/src/main/java/com/arialyy/aria/core/scheduler/ISchedulerListener.java
  91. 10
      Aria/src/main/java/com/arialyy/aria/core/upload/BaseNormalTarget.java
  92. 117
      Aria/src/main/java/com/arialyy/aria/core/upload/BaseUListener.java
  93. 41
      Aria/src/main/java/com/arialyy/aria/core/upload/FtpUploadTarget.java
  94. 3
      Aria/src/main/java/com/arialyy/aria/core/upload/UploadListener.java
  95. 43
      Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java
  96. 25
      Aria/src/main/java/com/arialyy/aria/core/upload/UploadTarget.java
  97. 4
      Aria/src/main/java/com/arialyy/aria/core/upload/uploader/FtpFileInfoThread.java
  98. 70
      Aria/src/main/java/com/arialyy/aria/core/upload/uploader/FtpThreadTask.java
  99. 39
      Aria/src/main/java/com/arialyy/aria/core/upload/uploader/HttpThreadTask.java
  100. 10
      Aria/src/main/java/com/arialyy/aria/core/upload/uploader/SimpleUploadUtil.java
  101. Some files were not shown because too many files have changed in this diff Show More

@ -26,7 +26,7 @@ dependencies {
testCompile 'junit:junit:4.12' testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.1.1' compile 'com.android.support:appcompat-v7:23.1.1'
compile project(':AriaAnnotations') compile project(':AriaAnnotations')
compile 'com.arialyy.aria:aria-ftp-plug:1.0.3' compile 'com.arialyy.aria:aria-ftp-plug:1.0.4'
// compile project(':AriaFtpPlug') // compile project(':AriaFtpPlug')
} }

@ -26,7 +26,6 @@ import android.os.Bundle;
import android.support.v4.app.DialogFragment; import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.widget.PopupWindow; import android.widget.PopupWindow;
import android.widget.Toast;
import com.arialyy.aria.core.command.ICmd; import com.arialyy.aria.core.command.ICmd;
import com.arialyy.aria.core.common.QueueMod; import com.arialyy.aria.core.common.QueueMod;
import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadEntity;
@ -71,7 +70,6 @@ import org.xml.sax.SAXException;
* activity 和其DialogFragment的映射表 * activity 和其DialogFragment的映射表
*/ */
private Map<String, List<String>> mSubClass = new ConcurrentHashMap<>(); private Map<String, List<String>> mSubClass = new ConcurrentHashMap<>();
private List<String> mActivityCount = new ArrayList<>();
public static Context APP; public static Context APP;
private List<ICmd> mCommands = new ArrayList<>(); private List<ICmd> mCommands = new ArrayList<>();
private Configuration.DownloadConfig mDConfig; private Configuration.DownloadConfig mDConfig;
@ -470,9 +468,6 @@ import org.xml.sax.SAXException;
private class LifeCallback implements Application.ActivityLifecycleCallbacks { private class LifeCallback implements Application.ActivityLifecycleCallbacks {
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
mActivityCount.add(
String.format("%s_%s", activity.getClass().getName(), activity.hashCode()));
ALog.w(TAG, "size = " + mActivityCount.size());
} }
@Override public void onActivityStarted(Activity activity) { @Override public void onActivityStarted(Activity activity) {
@ -497,13 +492,6 @@ import org.xml.sax.SAXException;
@Override public void onActivityDestroyed(Activity activity) { @Override public void onActivityDestroyed(Activity activity) {
removeReceiver(activity); removeReceiver(activity);
mActivityCount.remove(
String.format("%s_%s", activity.getClass().getName(), activity.hashCode()));
ALog.w(TAG, "size = " + mActivityCount.size());
if (mActivityCount.size() == 0) {
ALog.e(TAG, "gggg");
//Toast.makeText(activity, "destroy", Toast.LENGTH_SHORT).show();
}
} }
} }
} }

@ -184,6 +184,9 @@ class ConfigHelper extends DefaultHandler {
if (mType == ConfigType.DOWNLOAD) { if (mType == ConfigType.DOWNLOAD) {
mDownloadConfig.maxSpeed = maxSpeed; mDownloadConfig.maxSpeed = maxSpeed;
} }
if (mType == ConfigType.UPLOAD) {
mUploadConfig.maxSpeed = maxSpeed;
}
} }
private void loadConvertSpeed(String value) { private void loadConvertSpeed(String value) {

@ -17,6 +17,8 @@
*/ */
package com.arialyy.aria.core; package com.arialyy.aria.core;
import com.arialyy.aria.core.common.ProtocolType;
import com.arialyy.aria.orm.annotation.Ignore;
import java.net.InetAddress; import java.net.InetAddress;
/** /**
@ -32,6 +34,12 @@ public class FtpUrlEntity implements Cloneable {
public String account; public String account;
/**
* 是否是ftps
* {@code true}ftps协议的地址{@code false}不是ftps协议的地址
*/
public boolean isFtps = false;
/** /**
* 原始url * 原始url
*/ */
@ -40,15 +48,17 @@ public class FtpUrlEntity implements Cloneable {
/** /**
* ftp协议ftp * ftp协议ftp
*/ */
public String protocol; public String scheme;
/** /**
* 登录的用户名 * 登录的用户名
*/ */
public String user; public String user;
/** /**
* 密码 * 密码
*/ */
@Ignore
public String password; public String password;
/** /**
@ -71,6 +81,28 @@ public class FtpUrlEntity implements Cloneable {
*/ */
public InetAddress validAddr; public InetAddress validAddr;
/**
* 证书路径
*/
public String storePath;
/**
* 证书密码
*/
public String storePass;
/**
* 连接协议
* {@link ProtocolType}
*/
@ProtocolType
public String protocol;
/**
* 私钥别名
*/
public String keyAlias;
@Override public FtpUrlEntity clone() { @Override public FtpUrlEntity clone() {
FtpUrlEntity entity = null; FtpUrlEntity entity = null;
try { try {

@ -26,7 +26,6 @@ public abstract class AbsCmd<T extends AbsTaskEntity> implements ICmd {
protected ITaskQueue mQueue; protected ITaskQueue mQueue;
protected T mTaskEntity; protected T mTaskEntity;
protected String TAG; protected String TAG;
protected String mTargetName;
/** /**
* 是否是下载任务的命令 * 是否是下载任务的命令

@ -24,10 +24,9 @@ import com.arialyy.aria.core.inf.AbsTaskEntity;
public abstract class AbsCmdFactory<TASK_ENTITY extends AbsTaskEntity, CMD extends AbsCmd> { public abstract class AbsCmdFactory<TASK_ENTITY extends AbsTaskEntity, CMD extends AbsCmd> {
/** /**
* @param target 创建任务的对象
* @param entity 下载实体 * @param entity 下载实体
* @param taskType {@link ICmd#TASK_TYPE_DOWNLOAD}{@link ICmd#TASK_TYPE_DOWNLOAD_GROUP}{@link * @param taskType {@link ICmd#TASK_TYPE_DOWNLOAD}{@link ICmd#TASK_TYPE_DOWNLOAD_GROUP}{@link
* ICmd#TASK_TYPE_UPLOAD} * ICmd#TASK_TYPE_UPLOAD}
*/ */
public abstract CMD createCmd(String target, TASK_ENTITY entity, int type, int taskType); public abstract CMD createCmd(TASK_ENTITY entity, int type, int taskType);
} }

@ -36,11 +36,7 @@ public abstract class AbsGroupCmd<T extends AbsGroupTaskEntity> extends AbsCmd<T
AbsGroupTask tempTask; AbsGroupTask tempTask;
/** AbsGroupCmd(T entity) {
* @param targetName 创建任务的对象名
*/
AbsGroupCmd(String targetName, T entity) {
mTargetName = targetName;
mTaskEntity = entity; mTaskEntity = entity;
TAG = CommonUtil.getClassName(this); TAG = CommonUtil.getClassName(this);
if (entity instanceof DownloadGroupTaskEntity) { if (entity instanceof DownloadGroupTaskEntity) {
@ -55,7 +51,7 @@ public abstract class AbsGroupCmd<T extends AbsGroupTaskEntity> extends AbsCmd<T
* @return 创建的任务 * @return 创建的任务
*/ */
AbsTask createTask() { AbsTask createTask() {
tempTask = (AbsGroupTask) mQueue.createTask(mTargetName, mTaskEntity); tempTask = (AbsGroupTask) mQueue.createTask(mTaskEntity);
return tempTask; return tempTask;
} }

@ -22,11 +22,8 @@ import com.arialyy.aria.core.inf.AbsGroupTaskEntity;
* 删除任务组子任务 * 删除任务组子任务
*/ */
class GroupCancelCmd<T extends AbsGroupTaskEntity> extends AbsGroupCmd<T> { class GroupCancelCmd<T extends AbsGroupTaskEntity> extends AbsGroupCmd<T> {
/** GroupCancelCmd(T entity) {
* @param targetName 创建任务的对象名 super(entity);
*/
GroupCancelCmd(String targetName, T entity) {
super(targetName, entity);
} }
@Override public void executeCmd() { @Override public void executeCmd() {

@ -62,13 +62,13 @@ public class GroupCmdFactory {
AbsGroupCmd cmd = null; AbsGroupCmd cmd = null;
switch (type) { switch (type) {
case SUB_TASK_START: case SUB_TASK_START:
cmd = new GroupStartCmd<>(target, entity); cmd = new GroupStartCmd<>(entity);
break; break;
case SUB_TASK_STOP: case SUB_TASK_STOP:
cmd = new GroupStopCmd<>(target, entity); cmd = new GroupStopCmd<>(entity);
break; break;
case SUB_TASK_CANCEL: case SUB_TASK_CANCEL:
cmd = new GroupCancelCmd<>(target, entity); cmd = new GroupCancelCmd<>(entity);
} }
if (cmd != null) { if (cmd != null) {
cmd.childUrl = childUrl; cmd.childUrl = childUrl;

@ -22,11 +22,8 @@ import com.arialyy.aria.core.inf.AbsGroupTaskEntity;
* 任务组开始命令该命令负责处理任务组子任务的开始\恢复等工作 * 任务组开始命令该命令负责处理任务组子任务的开始\恢复等工作
*/ */
class GroupStartCmd<T extends AbsGroupTaskEntity> extends AbsGroupCmd<T> { class GroupStartCmd<T extends AbsGroupTaskEntity> extends AbsGroupCmd<T> {
/** GroupStartCmd( T entity) {
* @param targetName 创建任务的对象名 super( entity);
*/
GroupStartCmd(String targetName, T entity) {
super(targetName, entity);
} }
@Override public void executeCmd() { @Override public void executeCmd() {

@ -22,11 +22,8 @@ import com.arialyy.aria.core.inf.AbsGroupTaskEntity;
* 停止任务组的命令 * 停止任务组的命令
*/ */
class GroupStopCmd<T extends AbsGroupTaskEntity> extends AbsGroupCmd<T> { class GroupStopCmd<T extends AbsGroupTaskEntity> extends AbsGroupCmd<T> {
/** GroupStopCmd(T entity) {
* @param targetName 创建任务的对象名 super(entity);
*/
GroupStopCmd(String targetName, T entity) {
super(targetName, entity);
} }
@Override public void executeCmd() { @Override public void executeCmd() {

@ -46,13 +46,11 @@ public abstract class AbsNormalCmd<T extends AbsTaskEntity> extends AbsCmd<T> {
int taskType; int taskType;
/** /**
* @param targetName 产生任务的对象名
* @param taskType 下载任务类型{@link ICmd#TASK_TYPE_DOWNLOAD}{@link ICmd#TASK_TYPE_DOWNLOAD_GROUP}{@link * @param taskType 下载任务类型{@link ICmd#TASK_TYPE_DOWNLOAD}{@link ICmd#TASK_TYPE_DOWNLOAD_GROUP}{@link
* ICmd#TASK_TYPE_UPLOAD} * ICmd#TASK_TYPE_UPLOAD}
*/ */
AbsNormalCmd(String targetName, T entity, int taskType) { AbsNormalCmd(T entity, int taskType) {
this.taskType = taskType; this.taskType = taskType;
mTargetName = targetName;
mTaskEntity = entity; mTaskEntity = entity;
TAG = CommonUtil.getClassName(this); TAG = CommonUtil.getClassName(this);
if (taskType == ICmd.TASK_TYPE_DOWNLOAD) { if (taskType == ICmd.TASK_TYPE_DOWNLOAD) {
@ -126,7 +124,7 @@ public abstract class AbsNormalCmd<T extends AbsTaskEntity> extends AbsCmd<T> {
*/ */
void removeTask(AbsTaskEntity taskEntity) { void removeTask(AbsTaskEntity taskEntity) {
AbsTask tempTask = getTask(taskEntity.getEntity()); AbsTask tempTask = getTask(taskEntity.getEntity());
if (tempTask == null){ if (tempTask == null) {
tempTask = createTask(taskEntity); tempTask = createTask(taskEntity);
} }
mQueue.cancelTask(tempTask); mQueue.cancelTask(tempTask);
@ -181,7 +179,7 @@ public abstract class AbsNormalCmd<T extends AbsTaskEntity> extends AbsCmd<T> {
* @return 创建的任务 * @return 创建的任务
*/ */
AbsTask createTask() { AbsTask createTask() {
tempTask = mQueue.createTask(mTargetName, mTaskEntity); tempTask = mQueue.createTask(mTaskEntity);
return tempTask; return tempTask;
} }
@ -193,6 +191,6 @@ public abstract class AbsNormalCmd<T extends AbsTaskEntity> extends AbsCmd<T> {
*/ */
AbsTask createTask(AbsTaskEntity taskEntity) { AbsTask createTask(AbsTaskEntity taskEntity) {
TEManager.getInstance().addTEntity(taskEntity); TEManager.getInstance().addTEntity(taskEntity);
return mQueue.createTask(mTargetName, taskEntity); return mQueue.createTask(taskEntity);
} }
} }

@ -27,8 +27,8 @@ import com.arialyy.aria.util.ALog;
*/ */
class AddCmd<T extends AbsTaskEntity> extends AbsNormalCmd<T> { class AddCmd<T extends AbsTaskEntity> extends AbsNormalCmd<T> {
AddCmd(String targetName, T entity, int taskType) { AddCmd(T entity, int taskType) {
super(targetName, entity, taskType); super(entity, taskType);
} }
@Override public void executeCmd() { @Override public void executeCmd() {

@ -42,11 +42,8 @@ public class CancelAllCmd<T extends AbsTaskEntity> extends AbsNormalCmd<T> {
*/ */
public boolean removeFile = false; public boolean removeFile = false;
/** CancelAllCmd(T entity, int taskType) {
* @param targetName 产生任务的对象名 super(entity, taskType);
*/
CancelAllCmd(String targetName, T entity, int taskType) {
super(targetName, entity, taskType);
} }
@Override public void executeCmd() { @Override public void executeCmd() {

@ -16,7 +16,6 @@
package com.arialyy.aria.core.command.normal; package com.arialyy.aria.core.command.normal;
import android.text.TextUtils;
import com.arialyy.aria.core.inf.AbsTask; import com.arialyy.aria.core.inf.AbsTask;
import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.inf.AbsTaskEntity;
@ -31,8 +30,8 @@ public class CancelCmd<T extends AbsTaskEntity> extends AbsNormalCmd<T> {
*/ */
public boolean removeFile = false; public boolean removeFile = false;
CancelCmd(String targetName, T entity, int taskType) { CancelCmd(T entity, int taskType) {
super(targetName, entity, taskType); super(entity, taskType);
} }
@Override public void executeCmd() { @Override public void executeCmd() {
@ -43,9 +42,6 @@ public class CancelCmd<T extends AbsTaskEntity> extends AbsNormalCmd<T> {
} }
if (task != null) { if (task != null) {
mTaskEntity.setRemoveFile(removeFile); mTaskEntity.setRemoveFile(removeFile);
if (!TextUtils.isEmpty(mTargetName)) {
task.setTargetName(mTargetName);
}
removeTask(); removeTask();
} }
} }

@ -36,11 +36,8 @@ import com.arialyy.aria.util.NetUtils;
* 目前只只支持单下载任务的最高优先级任务 * 目前只只支持单下载任务的最高优先级任务
*/ */
final class HighestPriorityCmd<T extends AbsTaskEntity> extends AbsNormalCmd<T> { final class HighestPriorityCmd<T extends AbsTaskEntity> extends AbsNormalCmd<T> {
/** HighestPriorityCmd(T entity, int taskType) {
* @param targetName 产生任务的对象名 super(entity, taskType);
*/
HighestPriorityCmd(String targetName, T entity, int taskType) {
super(targetName, entity, taskType);
} }
@Override public void executeCmd() { @Override public void executeCmd() {
@ -54,9 +51,6 @@ final class HighestPriorityCmd<T extends AbsTaskEntity> extends AbsNormalCmd<T>
task = (DownloadTask) createTask(); task = (DownloadTask) createTask();
} }
if (task != null) { if (task != null) {
if (!TextUtils.isEmpty(mTargetName)) {
task.setTargetName(mTargetName);
}
((DownloadTaskQueue) mQueue).setTaskHighestPriority(task); ((DownloadTaskQueue) mQueue).setTaskHighestPriority(task);
} }
} }

@ -78,32 +78,31 @@ public class NormalCmdFactory extends AbsCmdFactory<AbsTaskEntity, AbsNormalCmd>
} }
/** /**
* @param target 创建任务的对象
* @param entity 下载实体 * @param entity 下载实体
* @param type 命令类型{@link #TASK_CREATE}{@link #TASK_START}{@link #TASK_CANCEL}{@link * @param type 命令类型{@link #TASK_CREATE}{@link #TASK_START}{@link #TASK_CANCEL}{@link
* #TASK_STOP}{@link #TASK_HIGHEST_PRIORITY}{@link #TASK_STOP_ALL}{@link #TASK_RESUME_ALL} * #TASK_STOP}{@link #TASK_HIGHEST_PRIORITY}{@link #TASK_STOP_ALL}{@link #TASK_RESUME_ALL}
* @param taskType {@link ICmd#TASK_TYPE_DOWNLOAD}{@link ICmd#TASK_TYPE_DOWNLOAD_GROUP}{@link * @param taskType {@link ICmd#TASK_TYPE_DOWNLOAD}{@link ICmd#TASK_TYPE_DOWNLOAD_GROUP}{@link
* ICmd#TASK_TYPE_UPLOAD} * ICmd#TASK_TYPE_UPLOAD}
*/ */
public AbsNormalCmd createCmd(String target, AbsTaskEntity entity, int type, int taskType) { public AbsNormalCmd createCmd(AbsTaskEntity entity, int type, int taskType) {
switch (type) { switch (type) {
case TASK_CREATE: case TASK_CREATE:
return new AddCmd<>(target, entity, taskType); return new AddCmd<>(entity, taskType);
case TASK_RESUME: case TASK_RESUME:
case TASK_START: case TASK_START:
return new StartCmd<>(target, entity, taskType); return new StartCmd<>(entity, taskType);
case TASK_CANCEL: case TASK_CANCEL:
return new CancelCmd<>(target, entity, taskType); return new CancelCmd<>(entity, taskType);
case TASK_STOP: case TASK_STOP:
return new StopCmd<>(target, entity, taskType); return new StopCmd<>(entity, taskType);
case TASK_HIGHEST_PRIORITY: case TASK_HIGHEST_PRIORITY:
return new HighestPriorityCmd<>(target, entity, taskType); return new HighestPriorityCmd<>(entity, taskType);
case TASK_STOP_ALL: case TASK_STOP_ALL:
return new StopAllCmd<>(target, entity, taskType); return new StopAllCmd<>(entity, taskType);
case TASK_RESUME_ALL: case TASK_RESUME_ALL:
return new ResumeAllCmd<>(target, entity, taskType); return new ResumeAllCmd<>(entity, taskType);
case TASK_CANCEL_ALL: case TASK_CANCEL_ALL:
return new CancelAllCmd<>(target, entity, taskType); return new CancelAllCmd<>(entity, taskType);
default: default:
return null; return null;
} }

@ -1,5 +1,6 @@
package com.arialyy.aria.core.command.normal; package com.arialyy.aria.core.command.normal;
import android.text.TextUtils;
import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.download.DownloadGroupEntity;
@ -32,11 +33,8 @@ import java.util.List;
final class ResumeAllCmd<T extends AbsTaskEntity> extends AbsNormalCmd<T> { final class ResumeAllCmd<T extends AbsTaskEntity> extends AbsNormalCmd<T> {
private List<AbsTaskEntity> mWaitList = new ArrayList<>(); private List<AbsTaskEntity> mWaitList = new ArrayList<>();
/** ResumeAllCmd(T entity, int taskType) {
* @param targetName 产生任务的对象名 super(entity, taskType);
*/
ResumeAllCmd(String targetName, T entity, int taskType) {
super(targetName, entity, taskType);
} }
@Override public void executeCmd() { @Override public void executeCmd() {
@ -65,6 +63,9 @@ final class ResumeAllCmd<T extends AbsTaskEntity> extends AbsNormalCmd<T> {
"isGroupChild=? AND state!=? ORDER BY stopTime DESC", "false", "1"); "isGroupChild=? AND state!=? ORDER BY stopTime DESC", "false", "1");
if (entities != null && !entities.isEmpty()) { if (entities != null && !entities.isEmpty()) {
for (DownloadEntity entity : entities) { for (DownloadEntity entity : entities) {
//if (TextUtils.isEmpty(entity.getDownloadPath())){
// continue;
//}
resumeTask(TEManager.getInstance().getTEntity(DownloadTaskEntity.class, entity.getKey())); resumeTask(TEManager.getInstance().getTEntity(DownloadTaskEntity.class, entity.getKey()));
} }
} }

@ -16,7 +16,6 @@
package com.arialyy.aria.core.command.normal; package com.arialyy.aria.core.command.normal;
import android.text.TextUtils;
import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.core.common.QueueMod; import com.arialyy.aria.core.common.QueueMod;
import com.arialyy.aria.core.download.DownloadGroupTaskEntity; import com.arialyy.aria.core.download.DownloadGroupTaskEntity;
@ -45,8 +44,8 @@ import java.util.List;
*/ */
class StartCmd<T extends AbsTaskEntity> extends AbsNormalCmd<T> { class StartCmd<T extends AbsTaskEntity> extends AbsNormalCmd<T> {
StartCmd(String targetName, T entity, int taskType) { StartCmd(T entity, int taskType) {
super(targetName, entity, taskType); super(entity, taskType);
} }
@Override public void executeCmd() { @Override public void executeCmd() {
@ -67,9 +66,6 @@ class StartCmd<T extends AbsTaskEntity> extends AbsNormalCmd<T> {
AbsTask task = getTask(); AbsTask task = getTask();
if (task == null) { if (task == null) {
task = createTask(); task = createTask();
if (!TextUtils.isEmpty(mTargetName)) {
task.setTargetName(mTargetName);
}
// 任务不存在时,根据配置不同,对任务执行操作 // 任务不存在时,根据配置不同,对任务执行操作
if (mod.equals(QueueMod.NOW.getTag())) { if (mod.equals(QueueMod.NOW.getTag())) {
startTask(); startTask();
@ -82,14 +78,14 @@ class StartCmd<T extends AbsTaskEntity> extends AbsNormalCmd<T> {
|| task.getState() == IEntity.STATE_COMPLETE) { || task.getState() == IEntity.STATE_COMPLETE) {
resumeTask(); resumeTask();
} else { } else {
sendWaitState(); sendWaitState(task);
} }
} }
} else { } else {
//任务没执行并且执行队列中没有该任务,才认为任务没有运行中 //任务没执行并且执行队列中没有该任务,才认为任务没有运行中
if (!task.isRunning() && !mQueue.taskIsRunning(task.getKey())) { if (!task.isRunning() && !mQueue.taskIsRunning(task.getKey())) {
resumeTask(); resumeTask();
}else { } else {
ALog.w(TAG, String.format("任务【%s】已经在运行", task.getTaskName())); ALog.w(TAG, String.format("任务【%s】已经在运行", task.getTaskName()));
} }
} }
@ -152,7 +148,8 @@ class StartCmd<T extends AbsTaskEntity> extends AbsNormalCmd<T> {
AbsTask task = getTask(te.getEntity()); AbsTask task = getTask(te.getEntity());
if (task != null) continue; if (task != null) continue;
if (te instanceof DownloadTaskEntity) { if (te instanceof DownloadTaskEntity) {
if (te.getRequestType() == AbsTaskEntity.D_FTP || te.getRequestType() == AbsTaskEntity.U_FTP) { if (te.getRequestType() == AbsTaskEntity.D_FTP
|| te.getRequestType() == AbsTaskEntity.U_FTP) {
te.setUrlEntity(CommonUtil.getFtpUrlInfo(te.getEntity().getKey())); te.setUrlEntity(CommonUtil.getFtpUrlInfo(te.getEntity().getKey()));
} }
mQueue = DownloadTaskQueue.getInstance(); mQueue = DownloadTaskQueue.getInstance();

@ -7,11 +7,8 @@ import com.arialyy.aria.core.inf.AbsTaskEntity;
* 停止所有任务的命令并清空所有等待队列 * 停止所有任务的命令并清空所有等待队列
*/ */
final class StopAllCmd<T extends AbsTaskEntity> extends AbsNormalCmd<T> { final class StopAllCmd<T extends AbsTaskEntity> extends AbsNormalCmd<T> {
/** StopAllCmd(T entity, int taskType) {
* @param targetName 产生任务的对象名 super(entity, taskType);
*/
StopAllCmd(String targetName, T entity, int taskType) {
super(targetName, entity, taskType);
} }
@Override public void executeCmd() { @Override public void executeCmd() {

@ -16,10 +16,9 @@
package com.arialyy.aria.core.command.normal; package com.arialyy.aria.core.command.normal;
import android.text.TextUtils;
import com.arialyy.aria.core.inf.AbsTask; import com.arialyy.aria.core.inf.AbsTask;
import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.inf.AbsTaskEntity;
import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
/** /**
@ -28,8 +27,8 @@ import com.arialyy.aria.util.ALog;
*/ */
class StopCmd<T extends AbsTaskEntity> extends AbsNormalCmd<T> { class StopCmd<T extends AbsTaskEntity> extends AbsNormalCmd<T> {
StopCmd(String targetName, T entity, int taskType) { StopCmd(T entity, int taskType) {
super(targetName, entity, taskType); super(entity, taskType);
} }
@Override public void executeCmd() { @Override public void executeCmd() {
@ -42,9 +41,6 @@ class StopCmd<T extends AbsTaskEntity> extends AbsNormalCmd<T> {
ALog.w(TAG, "停止命令执行失败,【调度器中没有该任务】"); ALog.w(TAG, "停止命令执行失败,【调度器中没有该任务】");
} }
} else { } else {
if (!TextUtils.isEmpty(mTargetName)) {
task.setTargetName(mTargetName);
}
stopTask(); stopTask();
} }
} }

@ -13,23 +13,25 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.arialyy.aria.core.download; package com.arialyy.aria.core.common;
import android.os.Handler;
import com.arialyy.aria.core.inf.IDownloadListener;
/** /**
* Created by Aria.Lao on 2017/7/20. * Aria事件总线
* 普通任务下载的事件监听器
*/ */
class DownloadListener extends BaseDListener<DownloadEntity, DownloadTaskEntity, DownloadTask> public class AEvent {
implements IDownloadListener { private static final Object LOCK = new Object();
DownloadListener(DownloadTask task, Handler outHandler) { private static volatile AEvent INSTANCE = null;
super(task, outHandler);
}
@Override public void supportBreakpoint(boolean support) { private AEvent() {
} }
public static AEvent getInstance() {
if (INSTANCE == null) {
synchronized (LOCK) {
INSTANCE = new AEvent();
}
}
return INSTANCE;
}
} }

@ -16,36 +16,33 @@
package com.arialyy.aria.core.common; package com.arialyy.aria.core.common;
import android.content.Context; import android.content.Context;
import android.util.SparseArray;
import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.download.DownloadTaskEntity;
import com.arialyy.aria.core.inf.AbsNormalEntity; import com.arialyy.aria.core.inf.AbsNormalEntity;
import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.inf.AbsTaskEntity;
import com.arialyy.aria.core.inf.IDownloadListener;
import com.arialyy.aria.core.inf.IEventListener; import com.arialyy.aria.core.inf.IEventListener;
import com.arialyy.aria.core.manager.ThreadTaskManager;
import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.orm.DbEntity;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.DbHelper; import com.arialyy.aria.util.DbHelper;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
import java.util.Timer; import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.TimerTask; import java.util.concurrent.TimeUnit;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/** /**
* Created by AriaL on 2017/7/1. * Created by AriaL on 2017/7/1.
* 任务处理器 * 任务处理器
*/ */
public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY extends AbsTaskEntity<ENTITY>> public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY extends AbsTaskEntity<ENTITY>>
implements Runnable, IUtil { implements Runnable {
private static final String STATE = "_state_"; private static final String STATE = "_state_";
private static final String RECORD = "_record_"; private static final String RECORD = "_record_";
/** /**
@ -64,16 +61,13 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte
protected Context mContext; protected Context mContext;
protected File mTempFile; //文件 protected File mTempFile; //文件
protected StateConstance mConstance; protected StateConstance mConstance;
private ExecutorService mFixedThreadPool;
//总线程数 //总线程数
protected int mTotalThreadNum; protected int mTotalThreadNum;
//启动线程数
private int mStartThreadNum;
//已完成的线程数 //已完成的线程数
private int mCompleteThreadNum; private int mCompleteThreadNum;
private Map<Integer, AbsThreadTask> mTask = new HashMap<>(); private SparseArray<AbsThreadTask> mTask = new SparseArray<>();
private Timer mTimer; private ScheduledThreadPoolExecutor mTimer;
@Deprecated private File mConfigFile; @Deprecated private File mConfigFile;
/** /**
* 进度刷新间隔 * 进度刷新间隔
@ -93,7 +87,12 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte
mTaskEntity.setNewTask(newTask); mTaskEntity.setNewTask(newTask);
} }
@Override public void setMaxSpeed(double maxSpeed) { /**
* 设置最大下载/上传速度
*
* @param maxSpeed 单位为kb
*/
public void setMaxSpeed(int maxSpeed) {
for (int i = 0; i < mTotalThreadNum; i++) { for (int i = 0; i < mTotalThreadNum; i++) {
AbsThreadTask task = mTask.get(i); AbsThreadTask task = mTask.get(i);
if (task != null) { if (task != null) {
@ -102,11 +101,22 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte
} }
} }
@Override public void run() { /**
if (mConstance.isRunning) { * 重置任务状态
return; */
private void resetState() {
closeTimer();
mCompleteThreadNum = 0;
mTotalThreadNum = 0;
if (mTask != null && mTask.size() != 0) {
for (int i = 0; i < mTask.size(); i++) {
AbsThreadTask task = mTask.get(i);
if (task != null && !task.isRunning()) {
task.breakTask();
}
}
mTask.clear();
} }
startFlow();
} }
/** /**
@ -116,18 +126,16 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte
if (isBreak()) { if (isBreak()) {
return; return;
} }
resetState();
mConstance.resetState(); mConstance.resetState();
checkTask(); checkRecord();
mConstance.isRunning = true;
mConstance.TASK_RECORD = mRecord; mConstance.TASK_RECORD = mRecord;
if (mListener instanceof IDownloadListener) {
((IDownloadListener) mListener).onPostPre(mEntity.getFileSize());
}
if (!mTaskEntity.isSupportBP()) { if (!mTaskEntity.isSupportBP()) {
mTotalThreadNum = 1; mTotalThreadNum = 1;
mStartThreadNum = 1;
} else { } else {
mTotalThreadNum = mTotalThreadNum =
mTaskEntity.isNewTask() ? (mStartThreadNum = setNewTaskThreadNum()) : mTotalThreadNum; mTaskEntity.isNewTask() ? setNewTaskThreadNum() : mTotalThreadNum;
} }
mConstance.START_THREAD_NUM = mTotalThreadNum; mConstance.START_THREAD_NUM = mTotalThreadNum;
@ -149,15 +157,30 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte
mRecord.isOpenDynamicFile = true; mRecord.isOpenDynamicFile = true;
} }
onPostPre();
if (!mTaskEntity.isSupportBP()) { if (!mTaskEntity.isSupportBP()) {
handleNoSupportBP(); handleNoSupportBP();
} else { } else {
handleBreakpoint(); handleBreakpoint();
} }
startTimer(); startTimer();
} }
@Override public void run() {
if (mConstance.isRunning) {
return;
}
startFlow();
}
/**
* 预处理完成
*/
protected void onPostPre() {
}
/** /**
* 设置新任务的最大线程数 * 设置新任务的最大线程数
*/ */
@ -166,9 +189,9 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte
/** /**
* 启动进度获取定时器 * 启动进度获取定时器
*/ */
private void startTimer() { private synchronized void startTimer() {
mTimer = new Timer(true); mTimer = new ScheduledThreadPoolExecutor(1);
mTimer.schedule(new TimerTask() { mTimer.scheduleWithFixedDelay(new Runnable() {
@Override public void run() { @Override public void run() {
if (mConstance.isComplete() if (mConstance.isComplete()
|| mConstance.isStop() || mConstance.isStop()
@ -179,14 +202,12 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte
mListener.onProgress(mConstance.CURRENT_LOCATION); mListener.onProgress(mConstance.CURRENT_LOCATION);
} }
} }
}, 0, mUpdateInterval); }, 0, mUpdateInterval, TimeUnit.MILLISECONDS);
} }
protected void closeTimer() { public synchronized void closeTimer() {
if (mTimer != null) { if (mTimer != null && !mTimer.isShutdown()) {
mTimer.purge(); mTimer.shutdown();
mTimer.cancel();
mTimer = null;
} }
} }
@ -203,72 +224,86 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte
mUpdateInterval = interval; mUpdateInterval = interval;
} }
@Override public long getFileSize() { public long getFileSize() {
return mEntity.getFileSize(); return mEntity.getFileSize();
} }
/** /**
* 获取当前任务位置 * 获取当前任务位置
*/ */
@Override public long getCurrentLocation() { public long getCurrentLocation() {
return mConstance.CURRENT_LOCATION; return mConstance.CURRENT_LOCATION;
} }
@Override public boolean isRunning() { public synchronized boolean isRunning() {
return mConstance.isRunning; return mConstance.isRunning;
} }
@Override public void cancel() { public synchronized void cancel() {
if (mConstance.isCancel) {
return;
}
closeTimer(); closeTimer();
mConstance.isRunning = false; mConstance.isRunning = false;
mConstance.isCancel = true; mConstance.isCancel = true;
if (mFixedThreadPool != null) { new Thread(new Runnable() {
mFixedThreadPool.shutdown(); @Override public void run() {
}
for (int i = 0; i < mStartThreadNum; i++) { for (int i = 0; i < mTask.size(); i++) {
AbsThreadTask task = mTask.get(i); AbsThreadTask task = mTask.get(i);
if (task != null) { if (task != null) {
task.cancel(); task.cancel();
} }
} }
ThreadTaskManager.getInstance().stopTaskThread(mTaskEntity.getKey());
}
}).start();
} }
@Override public void stop() { public synchronized void stop() {
if (mConstance.isStop) {
return;
}
closeTimer(); closeTimer();
mConstance.isRunning = false; mConstance.isRunning = false;
mConstance.isStop = true; mConstance.isStop = true;
if (mConstance.isComplete()) return; if (mConstance.isComplete()) return;
if (mFixedThreadPool != null) { new Thread(new Runnable() {
mFixedThreadPool.shutdown(); @Override public void run() {
}
for (int i = 0; i < mStartThreadNum; i++) { for (int i = 0; i < mTask.size(); i++) {
AbsThreadTask task = mTask.get(i); AbsThreadTask task = mTask.get(i);
if (task != null) { if (task != null && !task.isThreadComplete()) {
task.stop(); task.stop();
} }
} }
ThreadTaskManager.getInstance().stopTaskThread(mTaskEntity.getKey());
}
}).start();
} }
/** /**
* 直接调用的时候会自动启动线程执行 * 直接调用的时候会自动启动线程执行
*/ */
@Override public void start() { public synchronized void start() {
if (mConstance.isRunning) {
return;
}
new Thread(this).start(); new Thread(this).start();
} }
@Override public void resume() { public void resume() {
start(); start();
} }
/** /**
* 检查任务检查线程数 * 检查记录
* 新任务条件 * 对于分块任务
* 1文件不存在 * 子分块不存在或被删除子线程将重新下载
* 2任务记录文件缺失或不匹配 * 对于普通任务
* 3数据库记录不存在 * 预下载文件不存在则任务任务呗删除
* 4不支持断点则是新任务
*/ */
private void checkTask() { private void checkRecord() {
mConfigFile = new File(CommonUtil.getFileConfigPath(false, mEntity.getFileName())); mConfigFile = new File(CommonUtil.getFileConfigPath(false, mEntity.getFileName()));
if (mConfigFile.exists()) { if (mConfigFile.exists()) {
convertDb(); convertDb();
@ -279,24 +314,104 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte
} else { } else {
if (mRecord.threadRecords == null || mRecord.threadRecords.isEmpty()) { if (mRecord.threadRecords == null || mRecord.threadRecords.isEmpty()) {
initRecord(true); initRecord(true);
} else if (mRecord.isBlock) {
handleBlockRecord();
} else {
handleNormalRecord();
}
}
}
}
/**
* 处理普通任务的记录
*/
private void handleNormalRecord() {
File file = new File(mRecord.filePath);
if (!file.exists()) {
ALog.w(TAG, String.format("文件【%s】不存在,任务将重新开始", file.getPath()));
mRecord.deleteData();
initRecord(true);
return;
}
if (mRecord.isOpenDynamicFile) {
ThreadRecord tr = mRecord.threadRecords.get(0);
if (tr == null) {
mTaskEntity.setNewTask(true);
mTotalThreadNum = 1;
return;
}
if (file.length() > mEntity.getFileSize()) {
ALog.i(TAG, String.format("文件【%s】错误,任务重新开始", file.getPath()));
file.delete();
tr.startLocation = 0;
tr.isComplete = false;
tr.endLocation = mEntity.getFileSize();
} else if (file.length() == mEntity.getFileSize()) {
tr.isComplete = true;
mCompleteThreadNum++;
} else {
if (file.length() != tr.startLocation) {
ALog.i(TAG, String.format("修正【%s】的进度记录为:%s", file.getPath(), file.length()));
tr.startLocation = file.length();
tr.isComplete = false;
} }
//else if (mTempFile.length() == 0) { }
// mRecord.deleteData(); } else {
// initRecord(true);
//}
else {
for (ThreadRecord tr : mRecord.threadRecords) { for (ThreadRecord tr : mRecord.threadRecords) {
if (tr.isComplete) { if (tr.isComplete) {
mCompleteThreadNum++; mCompleteThreadNum++;
} else { }
mStartThreadNum++;
} }
} }
mTotalThreadNum = mRecord.threadRecords.size(); mTotalThreadNum = mRecord.threadRecords.size();
mTaskEntity.setNewTask(false); mTaskEntity.setNewTask(false);
} }
/**
* 处理分块任务的记录
*/
private void handleBlockRecord() {
final int threadNum = mRecord.threadRecords.size();
final long blockLen = mEntity.getFileSize() / threadNum;
int i = 0;
for (ThreadRecord tr : mRecord.threadRecords) {
File temp = new File(String.format(SUB_PATH, mRecord.filePath, tr.threadId));
if (!temp.exists()) {
ALog.i(TAG, String.format("分块文件【%s】不存在,该分块将重新开始", temp.getPath()));
tr.isComplete = false;
tr.startLocation = -1;
} else {
if (tr.isComplete) {
mCompleteThreadNum++;
} else {
long realLocation = i * blockLen + temp.length();
ALog.i(TAG, String.format(
"startLocation = %s; endLocation = %s; block = %s; tempLen = %s; i = %s",
tr.startLocation, tr.endLocation, blockLen, temp.length(), i));
if (tr.endLocation == realLocation) {
ALog.d(TAG, String.format("分块【%s】已完成,更新记录", temp.getPath()));
tr.startLocation = realLocation;
tr.isComplete = true;
mCompleteThreadNum++;
} else {
tr.isComplete = false;
if (realLocation != tr.startLocation) {
ALog.i(TAG, String.format("修正分块【%s】的进度记录为:%s", temp.getPath(), realLocation));
tr.startLocation = realLocation;
} else if (realLocation > tr.endLocation) {
ALog.i(TAG, String.format("分块【%s】错误,将重新开始该分块", temp.getPath()));
temp.delete();
tr.startLocation = i * blockLen;
}
}
} }
} }
i++;
}
mTotalThreadNum = mRecord.threadRecords.size();
mTaskEntity.setNewTask(false);
} }
/** /**
@ -342,7 +457,6 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte
tRecord.isComplete = true; tRecord.isComplete = true;
continue; continue;
} }
mStartThreadNum++;
if (record != null) { if (record != null) {
Long temp = Long.parseLong(String.valueOf(record)); Long temp = Long.parseLong(String.valueOf(record));
tRecord.startLocation = temp > 0 ? temp : 0; tRecord.startLocation = temp > 0 ? temp : 0;
@ -418,7 +532,7 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte
private AbsThreadTask createSingThreadTask(int i, long startL, long endL, long fileLength, private AbsThreadTask createSingThreadTask(int i, long startL, long endL, long fileLength,
ThreadRecord record) { ThreadRecord record) {
SubThreadConfig<TASK_ENTITY> config = new SubThreadConfig<>(); SubThreadConfig<TASK_ENTITY> config = new SubThreadConfig<>();
config.FILE_SIZE = fileLength; config.TOTAL_FILE_SIZE = fileLength;
config.URL = mEntity.isRedirect() ? mEntity.getRedirectUrl() : mEntity.getUrl(); config.URL = mEntity.isRedirect() ? mEntity.getRedirectUrl() : mEntity.getUrl();
config.TEMP_FILE = config.TEMP_FILE =
mRecord.isBlock ? new File(String.format(SUB_PATH, mTempFile.getPath(), i)) : mTempFile; mRecord.isBlock ? new File(String.format(SUB_PATH, mTempFile.getPath(), i)) : mTempFile;
@ -456,28 +570,35 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte
} else { } else {
tr = mRecord.threadRecords.get(i); tr = mRecord.threadRecords.get(i);
} }
if (tr.blockLen == 0) {
tr.blockLen = i == mTotalThreadNum - 1 ? (fileLength - i * blockSize) : blockSize;
}
if (tr.isComplete) {//该线程已经完成 if (tr.isComplete) {//该线程已经完成
if (resumeRecordLocation(i, startL, endL)) return; if (resumeRecordLocation(i, startL, endL)) return;
continue; continue;
} }
//如果有记录,则恢复任务 //如果有记录,则恢复任务
if (tr.startLocation >= 0) { if (tr.startLocation > 0) {
Long r = tr.startLocation; long r = tr.startLocation;
//记录的位置需要在线程区间中 //记录的位置需要在线程区间中
if (startL < r && r < (i == (mTotalThreadNum - 1) ? fileLength : endL)) { if (startL < r && r <= (i == (mTotalThreadNum - 1) ? fileLength : endL)) {
mConstance.CURRENT_LOCATION += r - startL; mConstance.CURRENT_LOCATION += r - startL;
startL = r; startL = r;
} }
ALog.d(TAG, String.format("任务【%s】线程__%s__恢复任务", mEntity.getFileName(), i)); ALog.d(TAG, String.format("任务【%s】线程__%s__恢复任务", mEntity.getFileName(), i));
} else {
tr.startLocation = startL;
} }
//最后一个线程的结束位置即为文件的总长度 //最后一个线程的结束位置即为文件的总长度
if (i == (mTotalThreadNum - 1)) { if (i == (mTotalThreadNum - 1)) {
endL = fileLength; endL = fileLength;
} }
// 更新记录 if (tr.endLocation <= 0) {
tr.startLocation = startL;
tr.endLocation = endL; tr.endLocation = endL;
}
if (isNewTr) { if (isNewTr) {
mRecord.threadRecords.add(tr); mRecord.threadRecords.add(tr);
} }
@ -508,45 +629,18 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte
} else { } else {
mListener.onStart(mConstance.CURRENT_LOCATION); mListener.onStart(mConstance.CURRENT_LOCATION);
} }
mFixedThreadPool = Executors.newFixedThreadPool(recordL.length);
for (int l : recordL) { for (int l : recordL) {
if (l == -1) continue; if (l == -1) continue;
Runnable task = mTask.get(l); ThreadTaskManager.getInstance().startThread(mTaskEntity.getKey(), mTask.get(l));
if (task != null) {
mFixedThreadPool.execute(task);
}
} }
} }
/** /**
* 重试线程任务只有线程创建成功才能重试 * 重试任务
*/ */
public void retryThreadTask() { public void retryTask() {
if (isBreak()) { ALog.w(TAG, String.format("任务【%s】开始重试", mEntity.getFileName()));
return; startFlow();
}
if (mTask == null || mTask.size() == 0) {
ALog.w(TAG, "没有线程任务");
return;
}
Set<Integer> keys = mTask.keySet();
for (Integer key : keys) {
AbsThreadTask task = mTask.get(key);
if (task != null && !task.isThreadComplete()) {
task.getConfig().START_LOCATION = task.getConfig().THREAD_RECORD.startLocation;
mConstance.resetState();
startTimer();
ALog.d(TAG, String.format("任务【%s】开始重试,线程__%s__【开始位置:%s,结束位置:%s】", mEntity.getFileName(),
key, task.getConfig().START_LOCATION, task.getConfig().END_LOCATION));
if (!mFixedThreadPool.isShutdown()) {
mFixedThreadPool.execute(task);
} else {
ALog.w(TAG, "线程池已关闭");
mListener.onFail(true);
}
}
}
} }
/** /**
@ -561,24 +655,23 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte
*/ */
private void handleNoSupportBP() { private void handleNoSupportBP() {
SubThreadConfig<TASK_ENTITY> config = new SubThreadConfig<>(); SubThreadConfig<TASK_ENTITY> config = new SubThreadConfig<>();
config.FILE_SIZE = mEntity.getFileSize(); config.TOTAL_FILE_SIZE = mEntity.getFileSize();
config.URL = mEntity.isRedirect() ? mEntity.getRedirectUrl() : mEntity.getUrl(); config.URL = mEntity.isRedirect() ? mEntity.getRedirectUrl() : mEntity.getUrl();
config.TEMP_FILE = mTempFile; config.TEMP_FILE = mTempFile;
config.THREAD_ID = 0; config.THREAD_ID = 0;
config.START_LOCATION = 0; config.START_LOCATION = 0;
config.END_LOCATION = config.FILE_SIZE; config.END_LOCATION = config.TOTAL_FILE_SIZE;
config.SUPPORT_BP = mTaskEntity.isSupportBP(); config.SUPPORT_BP = mTaskEntity.isSupportBP();
config.TASK_ENTITY = mTaskEntity; config.TASK_ENTITY = mTaskEntity;
ThreadRecord record = new ThreadRecord(); ThreadRecord record = new ThreadRecord();
record.startLocation = 0; record.startLocation = 0;
record.endLocation = config.FILE_SIZE; record.endLocation = config.TOTAL_FILE_SIZE;
record.key = mTempFile.getPath(); record.key = mTempFile.getPath();
config.THREAD_RECORD = record; config.THREAD_RECORD = record;
AbsThreadTask task = selectThreadTask(config); AbsThreadTask task = selectThreadTask(config);
if (task == null) return; if (task == null) return;
mTask.put(0, task); mTask.put(0, task);
mFixedThreadPool = Executors.newFixedThreadPool(1); ThreadTaskManager.getInstance().startThread(mTaskEntity.getKey(), task);
mFixedThreadPool.execute(task);
mListener.onStart(0); mListener.onStart(0);
} }

@ -15,22 +15,24 @@
*/ */
package com.arialyy.aria.core.common; package com.arialyy.aria.core.common;
import android.os.Build; import android.os.Process;
import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.core.inf.AbsNormalEntity; import com.arialyy.aria.core.inf.AbsNormalEntity;
import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.inf.AbsTaskEntity;
import com.arialyy.aria.core.inf.IEventListener; import com.arialyy.aria.core.inf.IEventListener;
import com.arialyy.aria.core.manager.ThreadTaskManager;
import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.upload.UploadEntity;
import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.exception.FileException;
import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.ErrorHelp; import com.arialyy.aria.util.ErrorHelp;
import com.arialyy.aria.util.FileUtil; import com.arialyy.aria.util.FileUtil;
import com.arialyy.aria.util.NetUtils; import com.arialyy.aria.util.NetUtils;
import java.io.File; import java.io.File;
import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Timer; import java.util.concurrent.Callable;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@ -39,20 +41,17 @@ import java.util.concurrent.Executors;
* 任务线程 * 任务线程
*/ */
public abstract class AbsThreadTask<ENTITY extends AbsNormalEntity, TASK_ENTITY extends AbsTaskEntity<ENTITY>> public abstract class AbsThreadTask<ENTITY extends AbsNormalEntity, TASK_ENTITY extends AbsTaskEntity<ENTITY>>
implements Runnable { implements Callable<AbsThreadTask> {
/** /**
* 线程重试次数 * 线程重试次数
*/ */
private final int RETRY_NUM = 2; private final int RETRY_NUM = 2;
/**
* 线程重试间隔
*/
private final int RETRY_INTERVAL = 5000;
private final String TAG = "AbsThreadTask"; private final String TAG = "AbsThreadTask";
/** /**
* 当前子线程的下载位置 * 当前子线程的下载位置
*/ */
protected long mChildCurrentLocation = 0, mSleepTime = 0; protected long mChildCurrentLocation = 0;
protected int mBufSize; protected int mBufSize;
protected IEventListener mListener; protected IEventListener mListener;
protected StateConstance STATE; protected StateConstance STATE;
@ -60,16 +59,23 @@ public abstract class AbsThreadTask<ENTITY extends AbsNormalEntity, TASK_ENTITY
protected ENTITY mEntity; protected ENTITY mEntity;
protected TASK_ENTITY mTaskEntity; protected TASK_ENTITY mTaskEntity;
private int mFailTimes = 0; private int mFailTimes = 0;
private Timer mFailTimer;
private long mLastSaveTime; private long mLastSaveTime;
private ExecutorService mConfigThreadPool; private ExecutorService mConfigThreadPool;
protected int mConnectTimeOut; //连接超时时间 protected int mConnectTimeOut; //连接超时时间
protected int mReadTimeOut; //流读取的超时时间 protected int mReadTimeOut; //流读取的超时时间
protected boolean isNotNetRetry = false; //断网情况是否重试 protected boolean isNotNetRetry = false; //断网情况是否重试
private boolean taskBreak = false; //任务中断 private boolean taskBreak = false; //任务跳出
protected int mThreadNum;
/**
* 速度限制工具
*/
protected BandwidthLimiter mSpeedBandUtil;
protected AriaManager mAridManager;
protected boolean isInterrupted = false;
private Thread mConfigThread = new Thread(new Runnable() { private Thread mConfigThread = new Thread(new Runnable() {
@Override public void run() { @Override public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
final long currentTemp = mChildCurrentLocation; final long currentTemp = mChildCurrentLocation;
writeConfig(false, currentTemp); writeConfig(false, currentTemp);
} }
@ -84,16 +90,30 @@ public abstract class AbsThreadTask<ENTITY extends AbsNormalEntity, TASK_ENTITY
mEntity = mTaskEntity.getEntity(); mEntity = mTaskEntity.getEntity();
mLastSaveTime = System.currentTimeMillis(); mLastSaveTime = System.currentTimeMillis();
mConfigThreadPool = Executors.newCachedThreadPool(); mConfigThreadPool = Executors.newCachedThreadPool();
mThreadNum = STATE.TASK_RECORD.threadRecords.size();
mAridManager = AriaManager.getInstance(AriaManager.APP);
if (getMaxSpeed() > 0) {
mSpeedBandUtil = new BandwidthLimiter(getMaxSpeed(), mThreadNum);
}
} }
protected void setMaxSpeed(double maxSpeed) { /**
if (-0.9999 < maxSpeed && maxSpeed < 0.00001) { * 设置线程是否中断
mSleepTime = 0; *
} else { * @param isInterrupted {@code true} 中断
BigDecimal db = new BigDecimal( */
((mBufSize / 1024) * (filterVersion() ? 1 : STATE.START_THREAD_NUM) / maxSpeed) * 1000); public void setInterrupted(boolean isInterrupted) {
mSleepTime = db.setScale(0, BigDecimal.ROUND_HALF_UP).longValue(); this.isInterrupted = isInterrupted;
} }
/**
* 线程是否存活
*
* @return {@code true}存活
*/
protected boolean isLive() {
Thread t = Thread.currentThread();
return !t.isInterrupted() && !isInterrupted;
} }
/** /**
@ -104,6 +124,74 @@ public abstract class AbsThreadTask<ENTITY extends AbsNormalEntity, TASK_ENTITY
return mConfig.THREAD_RECORD.isComplete; return mConfig.THREAD_RECORD.isComplete;
} }
/**
* 获取任务记录
*/
public TaskRecord getTaskRecord() {
return STATE.TASK_RECORD;
}
/**
* 获取线程记录
*/
public ThreadRecord getThreadRecord() {
return mConfig.THREAD_RECORD;
}
/**
* 获取配置的最大上传/下载速度
*
* @return 单位为kb
*/
public abstract int getMaxSpeed();
/**
* 设置最大下载速度
*
* @param speed 单位为kb
*/
public void setMaxSpeed(int speed) {
if (mSpeedBandUtil != null) {
mSpeedBandUtil.setMaxRate(speed / mThreadNum);
}
}
/**
* 中断任务
*/
public void breakTask() {
synchronized (AriaManager.LOCK) {
taskBreak = true;
if (mConfig.SUPPORT_BP) {
final long currentTemp = mChildCurrentLocation;
STATE.STOP_NUM++;
ALog.d(TAG, String.format("任务【%s】thread__%s__中断【停止位置:%s】", mConfig.TEMP_FILE.getName(),
mConfig.THREAD_ID, currentTemp));
writeConfig(false, currentTemp);
if (STATE.isStop()) {
ALog.i(TAG, String.format("任务【%s】已中断", mConfig.TEMP_FILE.getName()));
STATE.isRunning = false;
}
} else {
ALog.i(TAG, String.format("任务【%s】已中断", mConfig.TEMP_FILE.getName()));
STATE.isRunning = false;
}
}
}
/**
* 是否在运行
*
* @return {@code true}正在运行
*/
public boolean isRunning() {
return STATE.isRunning;
}
public boolean isInterrupted() {
return Thread.currentThread().isInterrupted();
}
/** /**
* 获取线程配置信息 * 获取线程配置信息
*/ */
@ -111,10 +199,6 @@ public abstract class AbsThreadTask<ENTITY extends AbsNormalEntity, TASK_ENTITY
return mConfig; return mConfig;
} }
private boolean filterVersion() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
}
@Override protected void finalize() throws Throwable { @Override protected void finalize() throws Throwable {
super.finalize(); super.finalize();
if (mConfigThreadPool != null) { if (mConfigThreadPool != null) {
@ -188,10 +272,11 @@ public abstract class AbsThreadTask<ENTITY extends AbsNormalEntity, TASK_ENTITY
protected void progress(long len) { protected void progress(long len) {
synchronized (AriaManager.LOCK) { synchronized (AriaManager.LOCK) {
if (STATE.CURRENT_LOCATION > mEntity.getFileSize()) { if (STATE.CURRENT_LOCATION > mEntity.getFileSize()) {
ALog.d(TAG, String.format("currentLocation=%s, fileSize=%s", STATE.CURRENT_LOCATION, String errorMsg =
mEntity.getFileSize())); String.format("下载失败,下载长度超出文件大;currentLocation=%s, fileSize=%s", STATE.CURRENT_LOCATION,
mEntity.getFileSize());
taskBreak = true; taskBreak = true;
fail(mChildCurrentLocation, "下载失败,下载长度超出文件大小", null, false); fail(mChildCurrentLocation, new FileException(TAG, errorMsg), false);
return; return;
} }
mChildCurrentLocation += len; mChildCurrentLocation += len;
@ -235,11 +320,10 @@ public abstract class AbsThreadTask<ENTITY extends AbsNormalEntity, TASK_ENTITY
* 线程任务失败 * 线程任务失败
* *
* @param subCurrentLocation 当前线程下载进度 * @param subCurrentLocation 当前线程下载进度
* @param msg 自定义信息
* @param ex 异常信息 * @param ex 异常信息
*/ */
protected void fail(final long subCurrentLocation, String msg, Exception ex) { protected void fail(final long subCurrentLocation, BaseException ex) {
fail(subCurrentLocation, msg, ex, true); fail(subCurrentLocation, ex, true);
} }
/** /**
@ -247,19 +331,17 @@ public abstract class AbsThreadTask<ENTITY extends AbsNormalEntity, TASK_ENTITY
* *
* @param subCurrentLocation 当前子线程进度 * @param subCurrentLocation 当前子线程进度
*/ */
protected void fail(final long subCurrentLocation, String msg, Exception ex, boolean needRetry) { protected void fail(final long subCurrentLocation, BaseException ex, boolean needRetry) {
synchronized (AriaManager.LOCK) { synchronized (AriaManager.LOCK) {
if (ex != null) { if (ex != null) {
ALog.e(TAG, msg + "\n" + ALog.getExceptionString(ex)); ALog.e(TAG, ALog.getExceptionString(ex));
} else {
ALog.e(TAG, msg);
} }
if (mConfig.SUPPORT_BP) { if (mConfig.SUPPORT_BP) {
writeConfig(false, subCurrentLocation); writeConfig(false, subCurrentLocation);
retryThis(needRetry && STATE.START_THREAD_NUM != 1); retryThis(needRetry && STATE.START_THREAD_NUM != 1);
} else { } else {
ALog.e(TAG, String.format("任务【%s】执行失败", mConfig.TEMP_FILE.getName())); ALog.e(TAG, String.format("任务【%s】执行失败", mConfig.TEMP_FILE.getName()));
mListener.onFail(true); mListener.onFail(true, ex);
ErrorHelp.saveError(TAG, "", ALog.getExceptionString(ex)); ErrorHelp.saveError(TAG, "", ALog.getExceptionString(ex));
} }
} }
@ -271,34 +353,68 @@ public abstract class AbsThreadTask<ENTITY extends AbsNormalEntity, TASK_ENTITY
* @param needRetry 是否可以重试 * @param needRetry 是否可以重试
*/ */
private void retryThis(boolean needRetry) { private void retryThis(boolean needRetry) {
if (mFailTimer != null) {
mFailTimer.purge();
mFailTimer.cancel();
}
if (!NetUtils.isConnected(AriaManager.APP) && !isNotNetRetry) { if (!NetUtils.isConnected(AriaManager.APP) && !isNotNetRetry) {
ALog.w(TAG, String.format("任务【%s】thread__%s__重试失败,网络未连接", mConfig.TEMP_FILE.getName(), ALog.w(TAG, String.format("任务【%s】thread__%s__重试失败,网络未连接", mConfig.TEMP_FILE.getName(),
mConfig.THREAD_ID)); mConfig.THREAD_ID));
} }
if (mFailTimes < RETRY_NUM && needRetry && (NetUtils.isConnected(AriaManager.APP) if (mFailTimes < RETRY_NUM && needRetry && (NetUtils.isConnected(AriaManager.APP)
|| isNotNetRetry) && isBreak()) { || isNotNetRetry) && !isBreak()) {
mFailTimer = new Timer(true); ALog.w(TAG,
mFailTimer.schedule(new TimerTask() { String.format("任务【%s】thread__%s__正在重试", mConfig.TEMP_FILE.getName(), mConfig.THREAD_ID));
@Override public void run() {
if (isBreak()) {
handleFailState(false);
return;
}
mFailTimes++; mFailTimes++;
ALog.w(TAG, String.format("任务【%s】thread__%s__正在重试", mConfig.TEMP_FILE.getName(), handleRetryRecord();
mConfig.THREAD_ID)); ThreadTaskManager.getInstance().retryThread(AbsThreadTask.this);
} else {
handleFailState(!isBreak());
}
}
/**
* 处理线程重试的记录只有多线程任务才会执行
*/
private void handleRetryRecord() {
if (getTaskRecord().isBlock) {
ThreadRecord tr = getThreadRecord();
long block = mEntity.getFileSize() / getTaskRecord().threadRecords.size();
File file = mConfig.TEMP_FILE;
if (file.length() > tr.endLocation) {
ALog.i(TAG, String.format("分块【%s】错误,将重新下载该分块", file.getPath()));
boolean b = file.delete();
ALog.w(TAG, "删除:" + b);
tr.startLocation = block * tr.threadId;
tr.isComplete = false;
mConfig.START_LOCATION = tr.startLocation;
} else if (file.length() == tr.endLocation) {
STATE.COMPLETE_THREAD_NUM++;
tr.isComplete = true;
} else {
tr.startLocation = block * tr.threadId + file.length();
mConfig.START_LOCATION = tr.startLocation;
tr.isComplete = false;
STATE.CURRENT_LOCATION = getBlockRealTotalSize();
ALog.i(TAG, String.format("修正分块【%s】进度,开始位置:%s,当前进度:%s", file.getPath(), tr.startLocation,
STATE.CURRENT_LOCATION));
}
} else {
mConfig.START_LOCATION = mChildCurrentLocation == 0 ? mConfig.START_LOCATION mConfig.START_LOCATION = mChildCurrentLocation == 0 ? mConfig.START_LOCATION
: mConfig.THREAD_RECORD.startLocation; : mConfig.THREAD_RECORD.startLocation;
AbsThreadTask.this.run();
} }
}, RETRY_INTERVAL);
} else {
handleFailState(!isBreak());
} }
/**
* 获取分块任务真实的进度
*
* @return 进度
*/
private long getBlockRealTotalSize() {
long size = 0;
for (int i = 0, len = getTaskRecord().threadRecords.size(); i < len; i++) {
File temp = new File(String.format(AbsFileer.SUB_PATH, getTaskRecord().filePath, i));
if (temp.exists()) {
size += temp.length();
}
}
return size;
} }
/** /**
@ -313,8 +429,9 @@ public abstract class AbsThreadTask<ENTITY extends AbsNormalEntity, TASK_ENTITY
STATE.isRunning = false; STATE.isRunning = false;
// 手动停止不进行fail回调 // 手动停止不进行fail回调
if (!STATE.isStop) { if (!STATE.isStop) {
ALog.e(TAG, String.format("任务【%s】执行失败", mConfig.TEMP_FILE.getName())); String errorMsg = String.format("任务【%s】执行失败", mConfig.TEMP_FILE.getName());
mListener.onFail(taskNeedReTry); //ALog.e(TAG, errorMsg);
mListener.onFail(taskNeedReTry, new TaskException(TAG, errorMsg));
} }
} }
} }
@ -328,13 +445,24 @@ public abstract class AbsThreadTask<ENTITY extends AbsNormalEntity, TASK_ENTITY
*/ */
protected void writeConfig(boolean isComplete, final long record) { protected void writeConfig(boolean isComplete, final long record) {
synchronized (AriaManager.LOCK) { synchronized (AriaManager.LOCK) {
if (mConfig.THREAD_RECORD != null) { ThreadRecord tr = getThreadRecord();
mConfig.THREAD_RECORD.isComplete = isComplete; if (tr != null) {
tr.isComplete = isComplete;
if (getTaskRecord().isBlock || getTaskRecord().isOpenDynamicFile) {
tr.startLocation = mConfig.TEMP_FILE.length();
} else {
if (0 < record && record < mConfig.END_LOCATION) { if (0 < record && record < mConfig.END_LOCATION) {
mConfig.THREAD_RECORD.startLocation = record; tr.startLocation = record;
}
} }
mConfig.THREAD_RECORD.update(); tr.update();
} }
} }
} }
@Override public AbsThreadTask call() throws Exception {
isInterrupted = false;
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
return this;
}
} }

@ -0,0 +1,104 @@
/*
* 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.common;
/**
* 速度限制
*/
public class BandwidthLimiter {
public static int maxBandWith = 2 * 1024; //KB
/* KB */
private static Long KB = 1024L;
/* The smallest count chunk length in bytes */
private static Long CHUNK_LENGTH = 1024L;
/* How many bytes will be sent or receive */
private int bytesWillBeSentOrReceive = 0;
/* When the last piece was sent or receive */
private long lastPieceSentOrReceiveTick = System.nanoTime();
/* Default rate is 1024KB/s */
private int maxRate = 1024;
/* Time cost for sending CHUNK_LENGTH bytes in nanoseconds */
private long timeCostPerChunk = (1000000000L * CHUNK_LENGTH)
/ (this.maxRate * KB);
/**
* Initialize a BandwidthLimiter object with a certain rate.
*
* @param maxRate the download or upload speed in KBytes
*/
public BandwidthLimiter(int maxRate, int threadNum) {
if (threadNum > 1) {
maxRate = maxRate / threadNum;
}
this.setMaxRate(maxRate);
}
/**
* Set the max upload or download rate in KB/s. maxRate must be grater than
* 0. If maxRate is zero, it means there is no bandwidth limit.
*
* @param maxRate If maxRate is zero, it means there is no bandwidth limit.
* @throws IllegalArgumentException
*/
public synchronized void setMaxRate(int maxRate)
throws IllegalArgumentException {
if (maxRate < 0) {
throw new IllegalArgumentException("maxRate can not less than 0");
}
this.maxRate = maxRate;
if (maxRate == 0) {
this.timeCostPerChunk = 0;
} else {
this.timeCostPerChunk = (1000000000L * CHUNK_LENGTH)
/ (this.maxRate * KB);
}
}
/**
* Next 1 byte should do bandwidth limit.
*/
public synchronized void limitNextBytes() {
this.limitNextBytes(1);
}
/**
* Next len bytes should do bandwidth limit
*/
public synchronized void limitNextBytes(int len) {
this.bytesWillBeSentOrReceive += len;
/* We have sent CHUNK_LENGTH bytes */
while (!Thread.currentThread().isInterrupted() && this.bytesWillBeSentOrReceive > CHUNK_LENGTH) {
long nowTick = System.nanoTime();
long missedTime = this.timeCostPerChunk
- (nowTick - this.lastPieceSentOrReceiveTick);
if (missedTime > 0) {
try {
Thread.currentThread().sleep(missedTime / 1000000,
(int) (missedTime % 1000000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.bytesWillBeSentOrReceive -= CHUNK_LENGTH;
this.lastPieceSentOrReceiveTick = nowTick
+ (missedTime > 0 ? missedTime : 0);
}
}
}

@ -0,0 +1,145 @@
/*
* 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.common;
import android.os.Handler;
import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.core.inf.AbsEntity;
import com.arialyy.aria.core.inf.AbsTask;
import com.arialyy.aria.core.inf.AbsTaskEntity;
import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.core.inf.IEventListener;
import com.arialyy.aria.core.inf.TaskSchedulerType;
import com.arialyy.aria.core.scheduler.ISchedulers;
import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.ErrorHelp;
import java.lang.ref.WeakReference;
public abstract class BaseListener<ENTITY extends AbsEntity, TASK_ENTITY extends AbsTaskEntity<ENTITY>, TASK extends AbsTask<ENTITY, TASK_ENTITY>>
implements IEventListener {
private static final String TAG = "BaseListener";
protected WeakReference<Handler> outHandler;
private int RUN_SAVE_INTERVAL = 5 * 1000; //5s保存一次下载中的进度
private long mLastLen; //上一次发送长度
private boolean isFirst = true;
private TASK mTask;
private long mLastSaveTime;
protected ENTITY mEntity;
protected TASK_ENTITY mTaskEntity;
protected boolean isConvertSpeed;
protected long mUpdateInterval;
protected AriaManager manager;
protected BaseListener(TASK task, Handler outHandler) {
this.outHandler = new WeakReference<>(outHandler);
this.mTask = new WeakReference<>(task).get();
this.mEntity = mTask.getTaskEntity().getEntity();
this.mTaskEntity = mTask.getTaskEntity();
manager = AriaManager.getInstance(AriaManager.APP);
mLastLen = mEntity.getCurrentProgress();
mLastSaveTime = System.currentTimeMillis();
}
@Override public void onPre() {
saveData(IEntity.STATE_PRE, -1);
sendInState2Target(ISchedulers.PRE);
}
@Override public void onStart(long startLocation) {
saveData(IEntity.STATE_RUNNING, startLocation);
sendInState2Target(ISchedulers.START);
}
@Override public void onResume(long resumeLocation) {
saveData(IEntity.STATE_RUNNING, resumeLocation);
sendInState2Target(ISchedulers.RESUME);
}
@Override public void onProgress(long currentLocation) {
mEntity.setCurrentProgress(currentLocation);
long speed = currentLocation - mLastLen;
if (isFirst) {
speed = 0;
isFirst = false;
}
handleSpeed(speed);
sendInState2Target(ISchedulers.RUNNING);
if (System.currentTimeMillis() - mLastSaveTime >= RUN_SAVE_INTERVAL) {
saveData(IEntity.STATE_RUNNING, currentLocation);
mLastSaveTime = System.currentTimeMillis();
}
mLastLen = currentLocation;
}
@Override public void onStop(long stopLocation) {
saveData(mTask.getSchedulerType() == TaskSchedulerType.TYPE_STOP_AND_WAIT ? IEntity.STATE_WAIT
: IEntity.STATE_STOP, stopLocation);
handleSpeed(0);
sendInState2Target(ISchedulers.STOP);
}
@Override public void onComplete() {
saveData(IEntity.STATE_COMPLETE, mEntity.getFileSize());
handleSpeed(0);
sendInState2Target(ISchedulers.COMPLETE);
}
@Override public void onCancel() {
saveData(IEntity.STATE_CANCEL, -1);
handleSpeed(0);
sendInState2Target(ISchedulers.CANCEL);
}
@Override public void onFail(boolean needRetry, BaseException e) {
mEntity.setFailNum(mEntity.getFailNum() + 1);
saveData(IEntity.STATE_FAIL, mEntity.getCurrentProgress());
handleSpeed(0);
mTask.needRetry = needRetry;
mTask.putExpand(AbsTask.ERROR_INFO_KEY, e);
sendInState2Target(ISchedulers.FAIL);
e.printStackTrace();
ErrorHelp.saveError(e.getTag(), "", ALog.getExceptionString(e));
}
private void handleSpeed(long speed) {
if (mUpdateInterval != 1000) {
speed = speed * 1000 / mUpdateInterval;
}
if (isConvertSpeed) {
mEntity.setConvertSpeed(CommonUtil.formatFileSize(speed < 0 ? 0 : speed) + "/s");
}
mEntity.setSpeed(speed < 0 ? 0 : speed);
mEntity.setPercent((int) (mEntity.getFileSize() <= 0 ? 0
: mEntity.getCurrentProgress() * 100 / mEntity.getFileSize()));
}
/**
* 将任务状态发送给下载器
*
* @param state {@link ISchedulers#START}
*/
protected void sendInState2Target(int state) {
if (outHandler.get() != null) {
outHandler.get().obtainMessage(state, mTask).sendToTarget();
}
}
protected abstract void saveData(int state, long location);
}

@ -60,7 +60,9 @@ public interface IUtil {
void resume(); void resume();
/** /**
* 设置最大速度 * 设置最大下载/上传速度
*
* @param speed 单位kb
*/ */
void setMaxSpeed(double maxSpeed); void setMaxSpeed(int speed);
} }

@ -15,6 +15,8 @@
*/ */
package com.arialyy.aria.core.common; package com.arialyy.aria.core.common;
import com.arialyy.aria.exception.BaseException;
public interface OnFileInfoCallback { public interface OnFileInfoCallback {
/** /**
* 处理完成 * 处理完成
@ -26,7 +28,7 @@ public interface OnFileInfoCallback {
/** /**
* 请求失败 * 请求失败
* *
* @param errorMsg 错误信息 * @param e 错误信息
*/ */
void onFail(String url, String errorMsg, boolean needRetry); void onFail(String url, BaseException e, boolean needRetry);
} }

@ -0,0 +1,39 @@
/*
* 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.common;
import android.support.annotation.StringDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@StringDef({
ProtocolType.Default,
ProtocolType.SSL,
ProtocolType.SSLv3,
ProtocolType.TLS,
ProtocolType.TLSv1,
ProtocolType.TLSv1_1,
ProtocolType.TLSv1_2
})
@Retention(RetentionPolicy.SOURCE) public @interface ProtocolType {
String Default = "TLS";
String SSL = "SSL";
String SSLv3 = "SSLv3";
String TLS = "TLS";
String TLSv1 = "TLSv1";
String TLSv1_1 = "TLSv1.1";
String TLSv1_2 = "TLSv1.2";
}

@ -15,26 +15,47 @@
*/ */
package com.arialyy.aria.core.common; package com.arialyy.aria.core.common;
import android.os.Build;
import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.CommonUtil;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* Created by Aria.Lao on 2017/7/10. * Created by Aria.Lao on 2017/7/10.
* 代理参数获取 * 代理参数获取
*/ */
public class ProxyHelper { public class ProxyHelper {
/**
* 普通下载任务类型
*/
public static int PROXY_TYPE_DOWNLOAD = 0x01;
/**
* 组合下载任务类型
*/
public static int PROXY_TYPE_DOWNLOAD_GROUP = 0x02;
///**
// * 组合任务子任务类型
// */
//public static int PROXY_TYPE_DOWNLOAD_GROUP_SUB = 0x03;
/**
* 普通上传任务类型
*/
public static int PROXY_TYPE_UPLOAD = 0x04;
public Set<String> downloadCounter = new HashSet<>(), uploadCounter = new HashSet<>(), public Set<String> downloadCounter = new HashSet<>(), uploadCounter = new HashSet<>(),
downloadGroupCounter = new HashSet<>(), downloadGroupSubCounter = new HashSet<>(); downloadGroupCounter = new HashSet<>(), downloadGroupSubCounter = new HashSet<>();
public Map<String, Set<Integer>> mProxyCache = new ConcurrentHashMap<>();
public static volatile ProxyHelper INSTANCE = null; public static volatile ProxyHelper INSTANCE = null;
private boolean canLoadClass = false;
private ProxyHelper() { private ProxyHelper() {
init(); //init();
} }
public static ProxyHelper getInstance() { public static ProxyHelper getInstance() {
@ -46,14 +67,75 @@ public class ProxyHelper {
return INSTANCE; return INSTANCE;
} }
/**
* @since 3.4.6 版本开始已经在ElementHandler中关闭了ProxyClassCounter对象的生成
*/
@Deprecated
private void init() { private void init() {
List<String> classes = CommonUtil.getClassName(AriaManager.APP, List<String> classes = CommonUtil.getPkgClassNames(AriaManager.APP,
"com.arialyy.aria.ProxyClassCounter"); "com.arialyy.aria.ProxyClassCounter");
canLoadClass = classes != null
&& !classes.isEmpty()
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
if (canLoadClass) {
for (String className : classes) { for (String className : classes) {
count(className); count(className);
} }
} }
}
/**
* 是否能读取到代理计数文件
*
* @return {@code true} 可以读取{@code false} 不能读取
*/
public boolean isCanLoadCountClass() {
return canLoadClass;
}
/**
* 检查观察者对象的代理文件类型
*
* @param clazz 观察者对象
* @return {@link #PROXY_TYPE_DOWNLOAD}如果没有实体对象则返回空的list
*/
public Set<Integer> checkProxyType(Class clazz) {
final String className = clazz.getName();
Set<Integer> result = mProxyCache.get(clazz.getName());
if (result != null) {
return result;
}
result = new HashSet<>();
try {
if (Class.forName(className.concat("$$DownloadGroupListenerProxy")) != null) {
result.add(PROXY_TYPE_DOWNLOAD_GROUP);
}
} catch (ClassNotFoundException e) {
//e.printStackTrace();
}
try {
if (Class.forName(className.concat("$$DownloadListenerProxy")) != null) {
result.add(PROXY_TYPE_DOWNLOAD);
}
} catch (ClassNotFoundException e) {
//e.printStackTrace();
}
try {
if (Class.forName(className.concat("$$UploadListenerProxy")) != null) {
result.add(PROXY_TYPE_UPLOAD);
}
} catch (ClassNotFoundException e) {
//e.printStackTrace();
}
if (!result.isEmpty()) {
mProxyCache.put(clazz.getName(), result);
}
return result;
}
@Deprecated
private void count(String className) { private void count(String className) {
try { try {
Class clazz = Class.forName(className); Class clazz = Class.forName(className);

@ -40,24 +40,29 @@ public class StateConstance {
public void resetState() { public void resetState() {
isCancel = false; isCancel = false;
isStop = false; isStop = false;
isRunning = true; isRunning = false;
CANCEL_NUM = 0; CANCEL_NUM = 0;
STOP_NUM = 0; STOP_NUM = 0;
FAIL_NUM = 0; FAIL_NUM = 0;
COMPLETE_THREAD_NUM = 0;
START_THREAD_NUM = 0;
CURRENT_LOCATION = 0;
} }
/** /**
* 所有子线程是否都已经停止 * 所有子线程是否都已经停止
*/ */
public boolean isStop() { public boolean isStop() {
return STOP_NUM == START_THREAD_NUM; //ALog.d(TAG, String.format("stop_num=%s; start_thread_num=%s; complete_num=%s", STOP_NUM,
// START_THREAD_NUM, COMPLETE_THREAD_NUM));
return STOP_NUM == START_THREAD_NUM || STOP_NUM + COMPLETE_THREAD_NUM == START_THREAD_NUM;
} }
/** /**
* 所有子线程是否都已经失败 * 所有子线程是否都已经失败
*/ */
public boolean isFail() { public boolean isFail() {
ALog.d(TAG, String.format("fail_num=%s; start_thread_num=%s, complete_num=%s", FAIL_NUM, ALog.d(TAG, String.format("fail_num=%s; start_thread_num=%s; complete_num=%s", FAIL_NUM,
START_THREAD_NUM, COMPLETE_THREAD_NUM)); START_THREAD_NUM, COMPLETE_THREAD_NUM));
return COMPLETE_THREAD_NUM != START_THREAD_NUM return COMPLETE_THREAD_NUM != START_THREAD_NUM
&& (FAIL_NUM == START_THREAD_NUM || FAIL_NUM + COMPLETE_THREAD_NUM == START_THREAD_NUM); && (FAIL_NUM == START_THREAD_NUM || FAIL_NUM + COMPLETE_THREAD_NUM == START_THREAD_NUM);

@ -9,8 +9,8 @@ import java.io.File;
public class SubThreadConfig<TASK_ENTITY extends AbsTaskEntity> { public class SubThreadConfig<TASK_ENTITY extends AbsTaskEntity> {
//线程Id //线程Id
public int THREAD_ID; public int THREAD_ID;
//下载文件大小 //文件总长度
public long FILE_SIZE; public long TOTAL_FILE_SIZE;
//子线程启动下载位置 //子线程启动下载位置
public long START_LOCATION; public long START_LOCATION;
//子线程结束下载位置 //子线程结束下载位置

@ -47,4 +47,9 @@ public class ThreadRecord extends DbEntity {
* 线程id * 线程id
*/ */
public int threadId = -1; public int threadId = -1;
/**
* 分块长度
*/
public long blockLen = 0;
} }

@ -1,78 +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.common;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 线程任务管理器
*/
class ThreadTaskManager {
private static volatile ThreadTaskManager INSTANCE = null;
private Map<String, List<AbsThreadTask>> mThreadTasks = new ConcurrentHashMap<>();
private ThreadTaskManager() {
}
public static ThreadTaskManager getInstance() {
if (INSTANCE == null) {
synchronized (ThreadTaskManager.class) {
INSTANCE = new ThreadTaskManager();
}
}
return INSTANCE;
}
/**
* 添加单条线程记录
*
* @param key 任务对应的key
* @param threadTask 线程任务
*/
public void addTask(String key, AbsThreadTask threadTask) {
if (mThreadTasks.get(key) == null) {
mThreadTasks.put(key, new ArrayList<AbsThreadTask>());
}
mThreadTasks.get(key).add(threadTask);
}
/**
* 删除对应的任务的线程记录
*
* @param key 任务对应的key
*/
public void removeTask(String key) {
for (Iterator<Map.Entry<String, List<AbsThreadTask>>> iter = mThreadTasks.entrySet().iterator();
iter.hasNext(); ) {
Map.Entry<String, List<AbsThreadTask>> entry = iter.next();
if (key.equals(entry.getKey())) {
List<AbsThreadTask> list = mThreadTasks.get(key);
if (list != null && !list.isEmpty()) {
list.clear();
}
iter.remove();
}
}
}
}

@ -13,26 +13,36 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.arialyy.aria.core.common; package com.arialyy.aria.core.common.ftp;
import android.os.Process;
import android.text.TextUtils; import android.text.TextUtils;
import aria.apache.commons.net.ftp.FTP;
import aria.apache.commons.net.ftp.FTPClient;
import aria.apache.commons.net.ftp.FTPFile;
import aria.apache.commons.net.ftp.FTPReply;
import aria.apache.commons.net.ftp.FTPSClient;
import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.core.FtpUrlEntity; import com.arialyy.aria.core.FtpUrlEntity;
import com.arialyy.aria.core.common.OnFileInfoCallback;
import com.arialyy.aria.core.common.ProtocolType;
import com.arialyy.aria.core.inf.AbsEntity; import com.arialyy.aria.core.inf.AbsEntity;
import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.inf.AbsTaskEntity;
import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.upload.UploadEntity;
import com.arialyy.aria.exception.AriaIOException;
import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.exception.FileException;
import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.Regular; import com.arialyy.aria.util.Regular;
import com.arialyy.aria.util.SSLContextUtil;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.apache.commons.net.ftp.FTP; import javax.net.ssl.SSLContext;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
/** /**
* Created by Aria.Lao on 2017/7/25. * Created by Aria.Lao on 2017/7/25.
@ -69,6 +79,7 @@ public abstract class AbsFtpInfoThread<ENTITY extends AbsEntity, TASK_ENTITY ext
protected abstract String setRemotePath(); protected abstract String setRemotePath();
@Override public void run() { @Override public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
FTPClient client = null; FTPClient client = null;
try { try {
client = createFtpClient(); client = createFtpClient();
@ -78,13 +89,16 @@ public abstract class AbsFtpInfoThread<ENTITY extends AbsEntity, TASK_ENTITY ext
} }
String remotePath = String remotePath =
new String(setRemotePath().getBytes(charSet), AbsFtpThreadTask.SERVER_CHARSET); new String(setRemotePath().getBytes(charSet), AbsFtpThreadTask.SERVER_CHARSET);
if (mTaskEntity.getUrlEntity().isFtps) {
((FTPSClient) client).execPROT("P");
//((FTPSClient) client).enterLocalActiveMode();
}
FTPFile[] files = client.listFiles(remotePath); FTPFile[] files = client.listFiles(remotePath);
String s = client.getReplyString();
ALog.i(TAG, s);
boolean isExist = files.length != 0; boolean isExist = files.length != 0;
if (!isExist && !isUpload) { if (!isExist && !isUpload) {
failDownload(String.format("文件不存在,任务链接【%s】,remotePath:%s", mTaskEntity.getUrlEntity().url, failDownload(new FileException(TAG,
remotePath), false); String.format("文件不存在,url: %s, remotePath:%s", mTaskEntity.getUrlEntity().url,
remotePath)), false);
int i = remotePath.lastIndexOf(File.separator); int i = remotePath.lastIndexOf(File.separator);
FTPFile[] files1; FTPFile[] files1;
if (i == -1) { if (i == -1) {
@ -101,8 +115,7 @@ public abstract class AbsFtpInfoThread<ENTITY extends AbsEntity, TASK_ENTITY ext
ALog.i(TAG, ALog.i(TAG,
"================================= --end-- ==================================="); "================================= --end-- ===================================");
} else { } else {
String msg = client.getReplyString(); ALog.w(TAG, String.format("获取文件列表失败,msg:%s", client.getReplyString()));
ALog.w(TAG, msg);
} }
client.disconnect(); client.disconnect();
return; return;
@ -116,8 +129,9 @@ public abstract class AbsFtpInfoThread<ENTITY extends AbsEntity, TASK_ENTITY ext
mTaskEntity.setNewTask(true); mTaskEntity.setNewTask(true);
} else { } else {
client.disconnect(); client.disconnect();
failDownload(String.format("获取文件信息错误,错误码为:%s,msg:%s", reply, client.getReplyString()), failDownload(new AriaIOException(TAG,
true); String.format("获取文件信息错误,url: %s, errorCode:%s, errorMsg:%s",
mTaskEntity.getUrlEntity().url, reply, client.getReplyString())), true);
return; return;
} }
} }
@ -128,15 +142,11 @@ public abstract class AbsFtpInfoThread<ENTITY extends AbsEntity, TASK_ENTITY ext
mTaskEntity.update(); mTaskEntity.update();
onPreComplete(reply); onPreComplete(reply);
} catch (IOException e) { } catch (IOException e) {
failDownload(e.getMessage(), true); failDownload(new AriaIOException(TAG,
String.format("FTP错误信息,code:%s,msg:%s", client.getReplyCode(), client.getReplyString()),
e), true);
} finally { } finally {
if (client != null) { closeClient(client);
try {
client.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
} }
} }
@ -172,7 +182,7 @@ public abstract class AbsFtpInfoThread<ENTITY extends AbsEntity, TASK_ENTITY ext
Pattern p = Pattern.compile(Regular.REG_IP_V4); Pattern p = Pattern.compile(Regular.REG_IP_V4);
Matcher m = p.matcher(urlEntity.hostName); Matcher m = p.matcher(urlEntity.hostName);
if (m.find() && m.groupCount() > 0) { if (m.find() && m.groupCount() > 0) {
client = new FTPClient(); client = newInstanceClient(urlEntity);
InetAddress ip = InetAddress.getByName(urlEntity.hostName); InetAddress ip = InetAddress.getByName(urlEntity.hostName);
client.setConnectTimeout(mConnectTimeOut); // 连接10s超时 client.setConnectTimeout(mConnectTimeOut); // 连接10s超时
client.connect(ip, Integer.parseInt(urlEntity.port)); client.connect(ip, Integer.parseInt(urlEntity.port));
@ -182,11 +192,18 @@ public abstract class AbsFtpInfoThread<ENTITY extends AbsEntity, TASK_ENTITY ext
dnsThread.start(); dnsThread.start();
dnsThread.join(mConnectTimeOut); dnsThread.join(mConnectTimeOut);
InetAddress[] ips = dnsThread.getIps(); InetAddress[] ips = dnsThread.getIps();
client = connect(new FTPClient(), ips, 0, Integer.parseInt(urlEntity.port)); client = connect(newInstanceClient(urlEntity), ips, 0, Integer.parseInt(urlEntity.port));
}
if (urlEntity.isFtps) {
int code = ((FTPSClient) client).execAUTH(
TextUtils.isEmpty(urlEntity.protocol) ? ProtocolType.TLS : urlEntity.protocol);
ALog.d(TAG, String.format("cod:%s,msg:%s", code, client.getReplyString()));
} }
if (client == null) { if (client == null) {
failDownload("链接失败", false); failDownload(new AriaIOException(TAG,
String.format("链接失败, url: %s", mTaskEntity.getUrlEntity().url)), false);
return null; return null;
} }
@ -199,43 +216,89 @@ public abstract class AbsFtpInfoThread<ENTITY extends AbsEntity, TASK_ENTITY ext
loginSuccess = client.login(urlEntity.user, urlEntity.password, urlEntity.account); loginSuccess = client.login(urlEntity.user, urlEntity.password, urlEntity.account);
} }
} catch (IOException e) { } catch (IOException e) {
ALog.e(TAG, client.getReplyString()); ALog.e(TAG,
new TaskException(TAG, String.format("登录失败,错误码为:%s, msg:%s", client.getReplyCode(),
client.getReplyString()), e));
return null; return null;
} }
} }
if (!loginSuccess) { if (!loginSuccess) {
failDownload("登录失败", false); failDownload(
new TaskException(TAG, String.format("登录失败,错误码为:%s, msg:%s", client.getReplyCode(),
client.getReplyString())),
false);
client.disconnect();
return null; return null;
} }
int reply = client.getReplyCode(); int reply = client.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) { if (!FTPReply.isPositiveCompletion(reply)) {
client.disconnect(); client.disconnect();
failDownload(String.format("无法连接到ftp服务器,错误码为:%s,msg:%s", reply, client.getReplyString()), failDownload(new AriaIOException(TAG,
String.format("无法连接到ftp服务器,filePath: %s, url: %s, errorCode: %s, errorMsg:%s",
mEntity.getKey(), mTaskEntity.getUrlEntity().url, reply, client.getReplyString())),
true); true);
return null; return null;
} }
// 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码 // 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码
charSet = "UTF-8"; charSet = "UTF-8";
reply = client.sendCommand("OPTS UTF8", "ON"); reply = client.sendCommand("OPTS UTF8", "ON");
if (!TextUtils.isEmpty(mTaskEntity.getCharSet()) || (!FTPReply.isPositiveCompletion(reply) if (reply != FTPReply.COMMAND_IS_SUPERFLUOUS) {
&& reply != FTPReply.COMMAND_OK)) {
ALog.i(TAG, "FTP 服务器不支持开启UTF8编码,尝试使用Aria手动设置的编码"); ALog.i(TAG, "FTP 服务器不支持开启UTF8编码,尝试使用Aria手动设置的编码");
if (!TextUtils.isEmpty(mTaskEntity.getCharSet())) {
charSet = mTaskEntity.getCharSet(); charSet = mTaskEntity.getCharSet();
} }
}
client.setControlEncoding(charSet); client.setControlEncoding(charSet);
client.setDataTimeout(10 * 1000); client.setDataTimeout(10 * 1000);
client.enterLocalPassiveMode(); client.enterLocalPassiveMode();
client.setFileType(FTP.BINARY_FILE_TYPE); client.setFileType(FTP.BINARY_FILE_TYPE);
} catch (IOException e) { } catch (IOException e) {
closeClient(client);
e.printStackTrace(); e.printStackTrace();
} catch (InterruptedException e) { } catch (InterruptedException e) {
closeClient(client);
e.printStackTrace(); e.printStackTrace();
} }
return client; return client;
} }
private void closeClient(FTPClient client) {
try {
if (client != null && client.isConnected()) {
client.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 创建FTP/FTPS客户端
*/
private FTPClient newInstanceClient(FtpUrlEntity urlEntity) {
FTPClient temp = null;
if (urlEntity.isFtps) {
SSLContext sslContext =
SSLContextUtil.getSSLContext(urlEntity.keyAlias, urlEntity.storePath, urlEntity.protocol);
if (sslContext == null) {
sslContext = SSLContextUtil.getDefaultSLLContext(urlEntity.protocol);
}
//System.setProperty("jdk.tls.useExtendedMasterSecret", "false");
//FTPSClient client = new SSLSessionReuseFTPSClient(true, sslContext);
FTPSClient client = new FTPSClient(true, sslContext);
//Log.d(TAG, "session = " + client.getEnableSessionCreation());
temp = client;
} else {
temp = new FTPClient();
}
return temp;
}
/** /**
* 连接到ftp服务器 * 连接到ftp服务器
*/ */
@ -268,7 +331,7 @@ public abstract class AbsFtpInfoThread<ENTITY extends AbsEntity, TASK_ENTITY ext
e1.printStackTrace(); e1.printStackTrace();
} }
ALog.w(TAG, "遇到[ECONNREFUSED-连接被服务器拒绝]错误,正在尝试下一个地址"); ALog.w(TAG, "遇到[ECONNREFUSED-连接被服务器拒绝]错误,正在尝试下一个地址");
return connect(new FTPClient(), ips, index + 1, port); return connect(newInstanceClient(mTaskEntity.getUrlEntity()), ips, index + 1, port);
} }
} }
@ -302,10 +365,9 @@ public abstract class AbsFtpInfoThread<ENTITY extends AbsEntity, TASK_ENTITY ext
protected void handleFile(String remotePath, FTPFile ftpFile) { protected void handleFile(String remotePath, FTPFile ftpFile) {
} }
private void failDownload(String errorMsg, boolean needRetry) { private void failDownload(BaseException e, boolean needRetry) {
ALog.e(TAG, errorMsg);
if (mCallback != null) { if (mCallback != null) {
mCallback.onFail(mEntity.getKey(), errorMsg, needRetry); mCallback.onFail(mEntity.getKey(), e, needRetry);
} }
} }

@ -13,20 +13,27 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.arialyy.aria.core.common; package com.arialyy.aria.core.common.ftp;
import android.text.TextUtils; import android.text.TextUtils;
import aria.apache.commons.net.ftp.FTP;
import aria.apache.commons.net.ftp.FTPClient;
import aria.apache.commons.net.ftp.FTPReply;
import aria.apache.commons.net.ftp.FTPSClient;
import com.arialyy.aria.core.FtpUrlEntity; import com.arialyy.aria.core.FtpUrlEntity;
import com.arialyy.aria.core.common.AbsThreadTask;
import com.arialyy.aria.core.common.ProtocolType;
import com.arialyy.aria.core.common.StateConstance;
import com.arialyy.aria.core.common.SubThreadConfig;
import com.arialyy.aria.core.inf.AbsNormalEntity; import com.arialyy.aria.core.inf.AbsNormalEntity;
import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.inf.AbsTaskEntity;
import com.arialyy.aria.core.inf.IEventListener; import com.arialyy.aria.core.inf.IEventListener;
import com.arialyy.aria.exception.AriaIOException;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import java.io.IOException; import com.arialyy.aria.util.SSLContextUtil;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import org.apache.commons.net.ftp.FTP; import javax.net.ssl.SSLContext;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
/** /**
* Created by lyy on 2017/9/26. * Created by lyy on 2017/9/26.
@ -35,7 +42,7 @@ import org.apache.commons.net.ftp.FTPReply;
public abstract class AbsFtpThreadTask<ENTITY extends AbsNormalEntity, TASK_ENTITY extends AbsTaskEntity<ENTITY>> public abstract class AbsFtpThreadTask<ENTITY extends AbsNormalEntity, TASK_ENTITY extends AbsTaskEntity<ENTITY>>
extends AbsThreadTask<ENTITY, TASK_ENTITY> { extends AbsThreadTask<ENTITY, TASK_ENTITY> {
private final String TAG = "AbsFtpThreadTask"; private final String TAG = "AbsFtpThreadTask";
protected String charSet, port; protected String charSet;
/** /**
* D_FTP 服务器编码 * D_FTP 服务器编码
*/ */
@ -55,7 +62,7 @@ public abstract class AbsFtpThreadTask<ENTITY extends AbsNormalEntity, TASK_ENTI
if (urlEntity.validAddr == null) { if (urlEntity.validAddr == null) {
try { try {
InetAddress[] ips = InetAddress.getAllByName(urlEntity.hostName); InetAddress[] ips = InetAddress.getAllByName(urlEntity.hostName);
client = connect(new FTPClient(), ips, 0, Integer.parseInt(urlEntity.port)); client = connect(newInstanceClient(urlEntity), ips, 0, Integer.parseInt(urlEntity.port));
if (client == null) { if (client == null) {
return null; return null;
} }
@ -63,10 +70,10 @@ public abstract class AbsFtpThreadTask<ENTITY extends AbsNormalEntity, TASK_ENTI
e.printStackTrace(); e.printStackTrace();
} }
} else { } else {
client = new FTPClient(); client = newInstanceClient(urlEntity);
try { try {
client.connect(urlEntity.validAddr, Integer.parseInt(urlEntity.port)); client.connect(urlEntity.validAddr, Integer.parseInt(urlEntity.port));
} catch (IOException e) { } catch (java.io.IOException e) {
ALog.e(TAG, ALog.getExceptionString(e)); ALog.e(TAG, ALog.getExceptionString(e));
return null; return null;
} }
@ -77,6 +84,12 @@ public abstract class AbsFtpThreadTask<ENTITY extends AbsNormalEntity, TASK_ENTI
} }
try { try {
if (urlEntity.isFtps) {
int code = ((FTPSClient) client).execAUTH(
TextUtils.isEmpty(urlEntity.protocol) ? ProtocolType.TLS : urlEntity.protocol);
ALog.d(TAG, String.format("cod:%s,msg:%s", code, client.getReplyString()));
}
if (urlEntity.needLogin) { if (urlEntity.needLogin) {
if (TextUtils.isEmpty(urlEntity.account)) { if (TextUtils.isEmpty(urlEntity.account)) {
client.login(urlEntity.user, urlEntity.password); client.login(urlEntity.user, urlEntity.password);
@ -87,28 +100,51 @@ public abstract class AbsFtpThreadTask<ENTITY extends AbsNormalEntity, TASK_ENTI
int reply = client.getReplyCode(); int reply = client.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) { if (!FTPReply.isPositiveCompletion(reply)) {
client.disconnect(); client.disconnect();
fail(mChildCurrentLocation, fail(mChildCurrentLocation, new AriaIOException(TAG,
String.format("无法连接到ftp服务器,错误码为:%s,msg:%s", reply, client.getReplyString()), null); String.format("无法连接到ftp服务器,错误码为:%s,msg:%s", reply, client.getReplyString())), false);
return null; return null;
} }
// 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码 // 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码
charSet = "UTF-8"; charSet = "UTF-8";
if (!TextUtils.isEmpty(mTaskEntity.getCharSet()) || !FTPReply.isPositiveCompletion( if (reply != FTPReply.COMMAND_IS_SUPERFLUOUS) {
client.sendCommand("OPTS UTF8", "ON"))) { if (!TextUtils.isEmpty(mTaskEntity.getCharSet())) {
charSet = mTaskEntity.getCharSet(); charSet = mTaskEntity.getCharSet();
} }
}
client.setControlEncoding(charSet); client.setControlEncoding(charSet);
client.setDataTimeout(mReadTimeOut); client.setDataTimeout(mReadTimeOut);
client.setConnectTimeout(mConnectTimeOut); client.setConnectTimeout(mConnectTimeOut);
client.enterLocalPassiveMode(); client.enterLocalPassiveMode();
client.setFileType(FTP.BINARY_FILE_TYPE); client.setFileType(FTP.BINARY_FILE_TYPE);
client.setControlKeepAliveTimeout(5000); client.setControlKeepAliveTimeout(5000);
} catch (IOException e) { if (mTaskEntity.getUrlEntity().isFtps) {
((FTPSClient) client).execPROT("P");
}
} catch (java.io.IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
return client; return client;
} }
/**
* 创建FTP/FTPS客户端
*/
private FTPClient newInstanceClient(FtpUrlEntity urlEntity) {
FTPClient temp;
if (urlEntity.isFtps) {
SSLContext sslContext =
SSLContextUtil.getSSLContext(urlEntity.keyAlias, urlEntity.storePath, urlEntity.protocol);
if (sslContext == null) {
sslContext = SSLContextUtil.getDefaultSLLContext(urlEntity.protocol);
}
temp = new FTPSClient(true, sslContext);
} else {
temp = new FTPClient();
}
return temp;
}
/** /**
* 连接到ftp服务器 * 连接到ftp服务器
*/ */
@ -117,12 +153,12 @@ public abstract class AbsFtpThreadTask<ENTITY extends AbsNormalEntity, TASK_ENTI
client.connect(ips[index], port); client.connect(ips[index], port);
mTaskEntity.getUrlEntity().validAddr = ips[index]; mTaskEntity.getUrlEntity().validAddr = ips[index];
return client; return client;
} catch (IOException e) { } catch (java.io.IOException e) {
try { try {
if (client.isConnected()) { if (client.isConnected()) {
client.disconnect(); client.disconnect();
} }
} catch (IOException e1) { } catch (java.io.IOException e1) {
e1.printStackTrace(); e1.printStackTrace();
} }
if (index + 1 >= ips.length) { if (index + 1 >= ips.length) {

@ -0,0 +1,104 @@
/*
* 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.common.ftp;
import android.text.TextUtils;
import com.arialyy.aria.core.FtpUrlEntity;
import com.arialyy.aria.core.common.ProtocolType;
import com.arialyy.aria.core.inf.AbsTarget;
import com.arialyy.aria.core.inf.ITarget;
/**
* FTP SSL/TSL配置
*/
public class FTPSConfig<TARGET extends AbsTarget> implements ITarget {
private final String TAG = "FTPSConfig";
private TARGET mTarget;
private FtpUrlEntity mUrlEntity;
public FTPSConfig(TARGET target) {
mTarget = target;
mUrlEntity = mTarget.getTaskEntity().getUrlEntity();
}
/**
* 设置协议类型
*
* @param protocol {@link ProtocolType}
*/
public FTPSConfig setProtocol(@ProtocolType String protocol) {
if (TextUtils.isEmpty(protocol)) {
throw new NullPointerException("协议为空");
}
mUrlEntity.protocol = protocol;
return this;
}
/**
* 设置证书别名
*
* @param keyAlias 别名
*/
public FTPSConfig setAlias(String keyAlias) {
if (TextUtils.isEmpty(keyAlias)) {
throw new NullPointerException("别名为空");
}
mUrlEntity.keyAlias = keyAlias;
return this;
}
/**
* 设置证书密码
*
* @param storePass 私钥密码
*/
public FTPSConfig setStorePass(String storePass) {
if (TextUtils.isEmpty(storePass)) {
throw new NullPointerException("证书密码为空");
}
mUrlEntity.storePass = storePass;
return this;
}
/**
* 设置证书路径
*
* @param storePath 证书路径
*/
public FTPSConfig setStorePath(String storePath) {
if (TextUtils.isEmpty(storePath)) {
throw new NullPointerException("证书路径为空");
}
mUrlEntity.storePath = storePath;
return this;
}
@Override public void start() {
mTarget.start();
}
@Override public void stop() {
mTarget.stop();
}
@Override public void resume() {
mTarget.resume();
}
@Override public void cancel() {
mTarget.cancel();
}
}

@ -13,35 +13,34 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.arialyy.aria.core.delegate; package com.arialyy.aria.core.common.ftp;
import android.text.TextUtils; import android.text.TextUtils;
import com.arialyy.aria.core.inf.AbsEntity; import com.arialyy.aria.core.FtpUrlEntity;
import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.inf.AbsTarget;
import com.arialyy.aria.core.inf.IFtpTarget; import com.arialyy.aria.core.inf.IFtpTarget;
import com.arialyy.aria.core.inf.ITarget;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import java.net.Proxy;
/** /**
* Created by laoyuyu on 2018/3/9. * Created by laoyuyu on 2018/3/9.
* ftp 委托 * ftp 委托
*/ */
public class FtpDelegate<TARGET extends ITarget, ENTITY extends AbsEntity, TASK_ENTITY extends AbsTaskEntity<ENTITY>> public class FtpDelegate<TARGET extends AbsTarget> implements IFtpTarget<TARGET> {
implements IFtpTarget<TARGET> {
private static final String TAG = "FtpDelegate"; private static final String TAG = "FtpDelegate";
private ENTITY mEntity; private FtpUrlEntity mUrlEntity;
private TASK_ENTITY mTaskEntity;
private TARGET mTarget; private TARGET mTarget;
public FtpDelegate(TARGET target, TASK_ENTITY taskEntity) { public FtpDelegate(TARGET target) {
mTarget = target; mTarget = target;
mTaskEntity = taskEntity; mUrlEntity = target.getTaskEntity().getUrlEntity();
mEntity = mTaskEntity.getEntity();
} }
@Override public TARGET charSet(String charSet) { @Override public TARGET charSet(String charSet) {
if (TextUtils.isEmpty(charSet)) return mTarget; if (TextUtils.isEmpty(charSet)) {
mTaskEntity.setCharSet(charSet); throw new NullPointerException("字符编码为空");
}
mTarget.getTaskEntity().setCharSet(charSet);
return mTarget; return mTarget;
} }
@ -57,10 +56,15 @@ public class FtpDelegate<TARGET extends ITarget, ENTITY extends AbsEntity, TASK_
ALog.e(TAG, "密码不能为null"); ALog.e(TAG, "密码不能为null");
return mTarget; return mTarget;
} }
mTaskEntity.getUrlEntity().needLogin = true; mUrlEntity.needLogin = true;
mTaskEntity.getUrlEntity().user = userName; mUrlEntity.user = userName;
mTaskEntity.getUrlEntity().password = password; mUrlEntity.password = password;
mTaskEntity.getUrlEntity().account = account; mUrlEntity.account = account;
return mTarget;
}
@Override public TARGET setProxy(Proxy proxy) {
mTarget.getTaskEntity().setProxy(proxy);
return mTarget; return mTarget;
} }
} }

@ -0,0 +1,52 @@
package com.arialyy.aria.core.common.ftp;
import aria.apache.commons.net.ftp.FTPSClient;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.Socket;
import java.util.Locale;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.SSLSocket;
public class SSLSessionReuseFTPSClient extends FTPSClient {
SSLSessionReuseFTPSClient(boolean b, SSLContext context) {
super(b, context);
}
// adapted from:
// https://trac.cyberduck.io/browser/trunk/ftp/src/main/java/ch/cyberduck/core/ftp/FTPClient.java
@Override
protected void _prepareDataSocket_(final Socket socket) throws IOException {
if (socket instanceof SSLSocket) {
// Control socket is SSL
final SSLSession session = ((SSLSocket) _socket_).getSession();
if (session.isValid()) {
final SSLSessionContext context = session.getSessionContext();
try {
//final Field sessionHostPortCache = context.getClass().getDeclaredField("sessionHostPortCache");
final Field sessionHostPortCache =
context.getClass().getDeclaredField("sessionsByHostAndPort");
sessionHostPortCache.setAccessible(true);
final Object cache = sessionHostPortCache.get(context);
final Method method =
cache.getClass().getDeclaredMethod("put", Object.class, Object.class);
method.setAccessible(true);
method.invoke(cache, String.format("%s:%s", socket.getInetAddress().getHostName(),
String.valueOf(socket.getPort())).toLowerCase(Locale.ROOT), session);
method.invoke(cache, String.format("%s:%s", socket.getInetAddress().getHostAddress(),
String.valueOf(socket.getPort())).toLowerCase(Locale.ROOT), session);
} catch (NoSuchFieldException e) {
throw new IOException(e);
} catch (Exception e) {
throw new IOException(e);
}
} else {
throw new IOException("Invalid SSL Session");
}
}
}
}

@ -13,16 +13,15 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.arialyy.aria.core.delegate; package com.arialyy.aria.core.common.http;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.text.TextUtils; import android.text.TextUtils;
import com.arialyy.aria.core.common.RequestEnum; import com.arialyy.aria.core.inf.AbsTarget;
import com.arialyy.aria.core.inf.AbsEntity;
import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.inf.AbsTaskEntity;
import com.arialyy.aria.core.inf.IHttpHeaderTarget; import com.arialyy.aria.core.inf.IHttpHeaderDelegate;
import com.arialyy.aria.core.inf.ITarget;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import java.net.Proxy;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -31,18 +30,13 @@ import java.util.Set;
* Created by laoyuyu on 2018/3/9. * Created by laoyuyu on 2018/3/9.
* HTTP header参数设置委托类 * HTTP header参数设置委托类
*/ */
public class HttpHeaderDelegate<TARGET extends ITarget, ENTITY extends AbsEntity, TASK_ENTITY extends AbsTaskEntity<ENTITY>> public class HttpHeaderDelegate<TARGET extends AbsTarget>
implements IHttpHeaderTarget<TARGET> { implements IHttpHeaderDelegate<TARGET> {
private static final String TAG = "HttpHeaderDelegate"; private static final String TAG = "HttpHeaderDelegate";
private ENTITY mEntity;
private TASK_ENTITY mTaskEntity;
private TARGET mTarget; private TARGET mTarget;
public HttpHeaderDelegate(TARGET target, TASK_ENTITY taskEntity) { public HttpHeaderDelegate(TARGET target) {
mTarget = target; mTarget = target;
mTaskEntity = taskEntity;
mEntity = mTaskEntity.getEntity();
} }
/** /**
@ -61,7 +55,7 @@ public class HttpHeaderDelegate<TARGET extends ITarget, ENTITY extends AbsEntity
ALog.w(TAG, "设置header失败,header对应的value不能为null"); ALog.w(TAG, "设置header失败,header对应的value不能为null");
return mTarget; return mTarget;
} }
addHeader(mTaskEntity, key, value); addHeader(mTarget.getTaskEntity(), key, value);
return mTarget; return mTarget;
} }
@ -77,19 +71,12 @@ public class HttpHeaderDelegate<TARGET extends ITarget, ENTITY extends AbsEntity
ALog.w(TAG, "设置header失败,map没有header数据"); ALog.w(TAG, "设置header失败,map没有header数据");
return mTarget; return mTarget;
} }
addHeaders(mTaskEntity, headers); addHeaders(mTarget.getTaskEntity(), headers);
return mTarget; return mTarget;
} }
/** @Override public TARGET setUrlProxy(Proxy proxy) {
* 设置请求类型POST或GET默认为在GET mTarget.getTaskEntity().setProxy(proxy);
* 只试用于HTTP请求
*
* @param requestEnum {@link RequestEnum}
*/
@Override
public TARGET setRequestMode(RequestEnum requestEnum) {
setRequestMode(mTaskEntity, requestEnum);
return mTarget; return mTarget;
} }
@ -101,10 +88,6 @@ public class HttpHeaderDelegate<TARGET extends ITarget, ENTITY extends AbsEntity
} }
} }
public void setRequestMode(AbsTaskEntity taskEntity, RequestEnum requestEnum) {
taskEntity.setRequestEnum(requestEnum);
}
public void addHeaders(AbsTaskEntity taskEntity, Map<String, String> headers) { public void addHeaders(AbsTaskEntity taskEntity, Map<String, String> headers) {
/* /*
两个map比较逻辑 两个map比较逻辑

@ -0,0 +1,86 @@
/*
* 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.common.http;
import android.text.TextUtils;
import com.arialyy.aria.core.common.RequestEnum;
import com.arialyy.aria.core.download.DownloadGroupTarget;
import com.arialyy.aria.core.download.DownloadGroupTaskEntity;
import com.arialyy.aria.core.download.DownloadTaskEntity;
import com.arialyy.aria.core.inf.AbsTarget;
import com.arialyy.aria.core.inf.IPostDelegate;
import com.arialyy.aria.core.inf.ITarget;
import com.arialyy.aria.util.ALog;
import java.util.HashMap;
import java.util.Map;
/**
* post处理委托类
*/
public class PostDelegate<TARGET extends AbsTarget> implements IPostDelegate<TARGET>, ITarget {
private static final String TAG = "PostDelegate";
private TARGET mTarget;
public PostDelegate(TARGET target) {
mTarget = target;
mTarget.getTaskEntity().setRequestEnum(RequestEnum.POST);
}
@Override public TARGET setParams(Map<String, String> params) {
mTarget.getTaskEntity().setParams(params);
if (mTarget instanceof DownloadGroupTarget) {
for (DownloadTaskEntity subTask : ((DownloadGroupTaskEntity) mTarget.getTaskEntity()).getSubTaskEntities()) {
subTask.setParams(params);
}
}
return mTarget;
}
@Override public TARGET setParam(String key, String value) {
if (TextUtils.isEmpty(key) || TextUtils.isEmpty(value)) {
ALog.d(TAG, "key 或value 为空");
return mTarget;
}
Map<String, String> params = mTarget.getTaskEntity().getParams();
if (params == null) {
params = new HashMap<>();
mTarget.getTaskEntity().setParams(params);
}
params.put(key, value);
if (mTarget instanceof DownloadGroupTarget) {
for (DownloadTaskEntity subTask : ((DownloadGroupTaskEntity) mTarget.getTaskEntity()).getSubTaskEntities()) {
subTask.setParams(params);
}
}
return mTarget;
}
@Override public void start() {
mTarget.start();
}
@Override public void stop() {
mTarget.stop();
}
@Override public void resume() {
mTarget.resume();
}
@Override public void cancel() {
mTarget.cancel();
}
}

@ -40,6 +40,11 @@ abstract class AbsDownloadTarget<TARGET extends AbsTarget, ENTITY extends AbsEnt
*/ */
String mTempFilePath; String mTempFilePath;
/**
* {@code true}强制下载不考虑文件路径是否被占用
*/
boolean forceDownload = false;
/** /**
* 将任务设置为最高优先级任务最高优先级任务有以下特点 * 将任务设置为最高优先级任务最高优先级任务有以下特点
* 1在下载队列中有且只有一个最高优先级任务 * 1在下载队列中有且只有一个最高优先级任务
@ -52,8 +57,8 @@ abstract class AbsDownloadTarget<TARGET extends AbsTarget, ENTITY extends AbsEnt
protected void setHighestPriority() { protected void setHighestPriority() {
if (checkEntity()) { if (checkEntity()) {
AriaManager.getInstance(AriaManager.APP) AriaManager.getInstance(AriaManager.APP)
.setCmd(CommonUtil.createNormalCmd(mTargetName, mTaskEntity, .setCmd(CommonUtil.createNormalCmd(mTaskEntity, NormalCmdFactory.TASK_HIGHEST_PRIORITY,
NormalCmdFactory.TASK_HIGHEST_PRIORITY, checkTaskType())) checkTaskType()))
.exe(); .exe();
} }
} }
@ -64,7 +69,7 @@ abstract class AbsDownloadTarget<TARGET extends AbsTarget, ENTITY extends AbsEnt
public void add() { public void add() {
if (checkEntity()) { if (checkEntity()) {
AriaManager.getInstance(AriaManager.APP) AriaManager.getInstance(AriaManager.APP)
.setCmd(CommonUtil.createNormalCmd(mTargetName, mTaskEntity, NormalCmdFactory.TASK_CREATE, .setCmd(CommonUtil.createNormalCmd(mTaskEntity, NormalCmdFactory.TASK_CREATE,
checkTaskType())) checkTaskType()))
.exe(); .exe();
} }

@ -16,53 +16,27 @@
package com.arialyy.aria.core.download; package com.arialyy.aria.core.download;
import android.os.Handler; import android.os.Handler;
import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.common.BaseListener;
import com.arialyy.aria.core.common.TaskRecord; import com.arialyy.aria.core.common.TaskRecord;
import com.arialyy.aria.core.inf.AbsEntity;
import com.arialyy.aria.core.inf.AbsTask;
import com.arialyy.aria.core.inf.AbsTaskEntity;
import com.arialyy.aria.core.inf.IDownloadListener; import com.arialyy.aria.core.inf.IDownloadListener;
import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.core.scheduler.ISchedulers; import com.arialyy.aria.core.scheduler.ISchedulers;
import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.orm.DbEntity;
import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.CommonUtil;
import java.lang.ref.WeakReference;
/** /**
* 下载监听类 * 下载监听类
*/ */
class BaseDListener<ENTITY extends AbsEntity, TASK_ENTITY extends AbsTaskEntity<ENTITY>, TASK extends AbsTask<TASK_ENTITY>> class BaseDListener extends BaseListener<DownloadEntity, DownloadTaskEntity, DownloadTask>
implements IDownloadListener { implements IDownloadListener {
private static final String TAG = "BaseDListener"; private static final String TAG = "BaseDListener";
WeakReference<Handler> outHandler;
private int RUN_SAVE_INTERVAL = 5 * 1000; //5s保存一次下载中的进度
private long mLastLen = 0; //上一次发送长度
private boolean isFirst = true;
protected ENTITY mEntity;
protected TASK_ENTITY mTaskEntity;
private TASK mTask;
private boolean isConvertSpeed = false;
boolean isWait = false;
private long mLastSaveTime;
private long mUpdateInterval;
BaseDListener(TASK task, Handler outHandler) { BaseDListener(DownloadTask task, Handler outHandler) {
this.outHandler = new WeakReference<>(outHandler); super(task, outHandler);
this.mTask = new WeakReference<>(task).get();
this.mEntity = mTask.getTaskEntity().getEntity();
this.mTaskEntity = mTask.getTaskEntity();
final AriaManager manager = AriaManager.getInstance(AriaManager.APP);
isConvertSpeed = manager.getDownloadConfig().isConvertSpeed(); isConvertSpeed = manager.getDownloadConfig().isConvertSpeed();
mLastLen = mEntity.getCurrentProgress();
mLastSaveTime = System.currentTimeMillis();
mUpdateInterval = manager.getDownloadConfig().getUpdateInterval(); mUpdateInterval = manager.getDownloadConfig().getUpdateInterval();
} }
@Override public void onPre() {
saveData(IEntity.STATE_PRE, -1);
sendInState2Target(ISchedulers.PRE);
}
@Override public void onPostPre(long fileSize) { @Override public void onPostPre(long fileSize) {
mEntity.setFileSize(fileSize); mEntity.setFileSize(fileSize);
mEntity.setConvertFileSize(CommonUtil.formatFileSize(fileSize)); mEntity.setConvertFileSize(CommonUtil.formatFileSize(fileSize));
@ -74,84 +48,8 @@ class BaseDListener<ENTITY extends AbsEntity, TASK_ENTITY extends AbsTaskEntity<
} }
@Override public void onStart(long startLocation) { @Override
saveData(IEntity.STATE_RUNNING, startLocation); protected void saveData(int state, long location) {
sendInState2Target(ISchedulers.START);
}
@Override public void onResume(long resumeLocation) {
saveData(IEntity.STATE_RUNNING, resumeLocation);
sendInState2Target(ISchedulers.RESUME);
}
@Override public void onProgress(long currentLocation) {
mEntity.setCurrentProgress(currentLocation);
long speed = currentLocation - mLastLen;
if (isFirst) {
speed = 0;
isFirst = false;
}
handleSpeed(speed);
sendInState2Target(ISchedulers.RUNNING);
if (System.currentTimeMillis() - mLastSaveTime >= RUN_SAVE_INTERVAL) {
saveData(IEntity.STATE_RUNNING, currentLocation);
mLastSaveTime = System.currentTimeMillis();
}
mLastLen = currentLocation;
}
@Override public void onStop(long stopLocation) {
saveData(isWait ? IEntity.STATE_WAIT : IEntity.STATE_STOP, stopLocation);
handleSpeed(0);
sendInState2Target(ISchedulers.STOP);
}
@Override public void onCancel() {
saveData(IEntity.STATE_CANCEL, -1);
handleSpeed(0);
sendInState2Target(ISchedulers.CANCEL);
}
@Override public void onComplete() {
saveData(IEntity.STATE_COMPLETE, mEntity.getFileSize());
handleSpeed(0);
sendInState2Target(ISchedulers.COMPLETE);
}
@Override public void onFail(boolean needRetry) {
mEntity.setFailNum(mEntity.getFailNum() + 1);
saveData(IEntity.STATE_FAIL, mEntity.getCurrentProgress());
handleSpeed(0);
mTask.needRetry = needRetry;
sendInState2Target(ISchedulers.FAIL);
}
private void handleSpeed(long speed) {
if (mUpdateInterval != 1000) {
speed = speed * 1000 / mUpdateInterval;
}
if (isConvertSpeed) {
mEntity.setConvertSpeed(CommonUtil.formatFileSize(speed < 0 ? 0 : speed) + "/s");
}
mEntity.setSpeed(speed < 0 ? 0 : speed);
mEntity.setPercent((int) (mEntity.getFileSize() <= 0 ? 0
: mEntity.getCurrentProgress() * 100 / mEntity.getFileSize()));
}
/**
* 将任务状态发送给下载器
*
* @param state {@link ISchedulers#START}
*/
private void sendInState2Target(int state) {
if (outHandler.get() != null) {
outHandler.get().obtainMessage(state, mTask).sendToTarget();
}
}
private void saveData(int state, long location) {
mTaskEntity.setState(state); mTaskEntity.setState(state);
mEntity.setState(state); mEntity.setState(state);
mEntity.setComplete(state == IEntity.STATE_COMPLETE); mEntity.setComplete(state == IEntity.STATE_COMPLETE);
@ -160,14 +58,11 @@ class BaseDListener<ENTITY extends AbsEntity, TASK_ENTITY extends AbsTaskEntity<
TaskRecord record = TaskRecord record =
DbEntity.findFirst(TaskRecord.class, "TaskRecord.filePath=?", mTaskEntity.getKey()); DbEntity.findFirst(TaskRecord.class, "TaskRecord.filePath=?", mTaskEntity.getKey());
if (record != null) { if (record != null) {
CommonUtil.delTaskRecord(record, mTaskEntity.isRemoveFile(), (DownloadEntity) mEntity); CommonUtil.delTaskRecord(record, mTaskEntity.isRemoveFile(), mEntity);
} else { } else {
mEntity.deleteData(); mEntity.deleteData();
} }
} else if (mEntity instanceof DownloadGroupEntity) {
CommonUtil.delGroupTaskRecord(mTaskEntity.isRemoveFile(), ((DownloadGroupEntity) mEntity));
} }
//mEntity.deleteData();
return; return;
} else if (state == IEntity.STATE_STOP) { } else if (state == IEntity.STATE_STOP) {
mEntity.setStopTime(System.currentTimeMillis()); mEntity.setStopTime(System.currentTimeMillis());

@ -25,7 +25,7 @@ import java.io.File;
import java.util.List; import java.util.List;
/** /**
* Created by Aria.Lao on 2017/7/26. * Created by lyy on 2017/7/26.
*/ */
abstract class BaseGroupTarget<TARGET extends BaseGroupTarget> abstract class BaseGroupTarget<TARGET extends BaseGroupTarget>
extends AbsDownloadTarget<TARGET, DownloadGroupEntity, DownloadGroupTaskEntity> { extends AbsDownloadTarget<TARGET, DownloadGroupEntity, DownloadGroupTaskEntity> {

@ -86,7 +86,7 @@ abstract class BaseNormalTarget<TARGET extends BaseNormalTarget>
* @return {@code true}任务存在 * @return {@code true}任务存在
*/ */
@Override public boolean taskExists() { @Override public boolean taskExists() {
return DownloadTaskQueue.getInstance().getTask(mEntity.getUrl()) != null; return DownloadTaskQueue.getInstance().getTask(mEntity.getKey()) != null;
} }
/** /**
@ -162,11 +162,17 @@ abstract class BaseNormalTarget<TARGET extends BaseNormalTarget>
} }
} }
//设置文件保存路径,如果新文件路径和文件路径不同,则修改路径 //设置文件保存路径,如果新文件路径和文件路径不同,则修改路径
if (!filePath.equals(mEntity.getDownloadPath())) { if (!filePath.equals(mEntity.getDownloadPath())) {
if (DbEntity.checkDataExist(DownloadEntity.class, "downloadPath=?", filePath)) { if (DbEntity.checkDataExist(DownloadEntity.class, "downloadPath=?", filePath)) {
if (!forceDownload) {
ALog.e(TAG, "下载失败,保存路径【" + filePath + "】已经被其它任务占用,请设置其它保存路径"); ALog.e(TAG, "下载失败,保存路径【" + filePath + "】已经被其它任务占用,请设置其它保存路径");
return false; return false;
}else {
ALog.d(TAG, "保存路径【" + filePath + "】已经被其它任务占用,当前任务将覆盖该路径的文件");
DbEntity.deleteData(DownloadEntity.class, "downloadPath=?", filePath);
mTaskEntity = TEManager.getInstance().getTEntity(DownloadTaskEntity.class, url);
}
} }
File oldFile = new File(mEntity.getDownloadPath()); File oldFile = new File(mEntity.getDownloadPath());
File newFile = new File(filePath); File newFile = new File(filePath);
@ -200,7 +206,7 @@ abstract class BaseNormalTarget<TARGET extends BaseNormalTarget>
ALog.e(TAG, "下载失败,url【" + url + "】不合法"); ALog.e(TAG, "下载失败,url【" + url + "】不合法");
return false; return false;
} }
if (!TextUtils.isEmpty(newUrl)){ if (!TextUtils.isEmpty(newUrl)) {
mEntity.setUrl(newUrl); mEntity.setUrl(newUrl);
mTaskEntity.setUrl(newUrl); mTaskEntity.setUrl(newUrl);
} }

@ -16,17 +16,22 @@
package com.arialyy.aria.core.download; package com.arialyy.aria.core.download;
import android.os.Handler; import android.os.Handler;
import com.arialyy.aria.core.common.BaseListener;
import com.arialyy.aria.core.download.downloader.IDownloadGroupListener; import com.arialyy.aria.core.download.downloader.IDownloadGroupListener;
import com.arialyy.aria.core.inf.GroupSendParams; import com.arialyy.aria.core.inf.GroupSendParams;
import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.core.scheduler.ISchedulers; import com.arialyy.aria.core.scheduler.ISchedulers;
import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.ErrorHelp;
/** /**
* Created by Aria.Lao on 2017/7/20. * Created by Aria.Lao on 2017/7/20.
* 任务组下载事件 * 任务组下载事件
*/ */
class DownloadGroupListener class DownloadGroupListener
extends BaseDListener<DownloadGroupEntity, DownloadGroupTaskEntity, DownloadGroupTask> extends BaseListener<DownloadGroupEntity, DownloadGroupTaskEntity, DownloadGroupTask>
implements IDownloadGroupListener { implements IDownloadGroupListener {
private final String TAG = "DownloadGroupListener"; private final String TAG = "DownloadGroupListener";
private GroupSendParams<DownloadGroupTask, DownloadEntity> mSeedEntity; private GroupSendParams<DownloadGroupTask, DownloadEntity> mSeedEntity;
@ -35,6 +40,8 @@ class DownloadGroupListener
super(task, outHandler); super(task, outHandler);
mSeedEntity = new GroupSendParams<>(); mSeedEntity = new GroupSendParams<>();
mSeedEntity.groupTask = task; mSeedEntity.groupTask = task;
isConvertSpeed = manager.getDownloadConfig().isConvertSpeed();
mUpdateInterval = manager.getDownloadConfig().getUpdateInterval();
} }
@Override public void onSubPre(DownloadEntity subEntity) { @Override public void onSubPre(DownloadEntity subEntity) {
@ -59,9 +66,11 @@ class DownloadGroupListener
sendInState2Target(ISchedulers.SUB_COMPLETE, subEntity); sendInState2Target(ISchedulers.SUB_COMPLETE, subEntity);
} }
@Override public void onSubFail(DownloadEntity subEntity) { @Override public void onSubFail(DownloadEntity subEntity, BaseException e) {
saveCurrentLocation(); saveCurrentLocation();
sendInState2Target(ISchedulers.SUB_FAIL, subEntity); sendInState2Target(ISchedulers.SUB_FAIL, subEntity);
e.printStackTrace();
ErrorHelp.saveError(e.getTag(), "", ALog.getExceptionString(e));
} }
@Override public void onSubCancel(DownloadEntity subEntity) { @Override public void onSubCancel(DownloadEntity subEntity) {
@ -100,4 +109,36 @@ class DownloadGroupListener
mEntity.setCurrentProgress(location); mEntity.setCurrentProgress(location);
mEntity.update(); mEntity.update();
} }
@Override public void onPostPre(long fileSize) {
mEntity.setFileSize(fileSize);
mEntity.setConvertFileSize(CommonUtil.formatFileSize(fileSize));
saveData(IEntity.STATE_POST_PRE, -1);
sendInState2Target(ISchedulers.POST_PRE);
}
@Override public void supportBreakpoint(boolean support) {
}
@Override protected void saveData(int state, long location) {
mTaskEntity.setState(state);
mEntity.setState(state);
mEntity.setComplete(state == IEntity.STATE_COMPLETE);
if (state == IEntity.STATE_CANCEL) {
if (mEntity instanceof DownloadGroupEntity) {
CommonUtil.delGroupTaskRecord(mTaskEntity.isRemoveFile(), mEntity);
}
return;
} else if (state == IEntity.STATE_STOP) {
mEntity.setStopTime(System.currentTimeMillis());
} else if (mEntity.isComplete()) {
mEntity.setCompleteTime(System.currentTimeMillis());
mEntity.setCurrentProgress(mEntity.getFileSize());
}
if (location > 0) {
mEntity.setCurrentProgress(location);
}
mTaskEntity.update();
}
} }

@ -19,12 +19,15 @@ import android.support.annotation.CheckResult;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.text.TextUtils; import android.text.TextUtils;
import com.arialyy.aria.core.common.RequestEnum; import com.arialyy.aria.core.common.RequestEnum;
import com.arialyy.aria.core.delegate.HttpHeaderDelegate; import com.arialyy.aria.core.common.http.HttpHeaderDelegate;
import com.arialyy.aria.core.inf.IHttpHeaderTarget; import com.arialyy.aria.core.common.http.PostDelegate;
import com.arialyy.aria.core.inf.IHttpHeaderDelegate;
import com.arialyy.aria.core.manager.TEManager; import com.arialyy.aria.core.manager.TEManager;
import com.arialyy.aria.orm.DbEntity;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.CommonUtil;
import java.io.File; import java.io.File;
import java.net.Proxy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -36,9 +39,8 @@ import java.util.Set;
* 下载任务组 * 下载任务组
*/ */
public class DownloadGroupTarget extends BaseGroupTarget<DownloadGroupTarget> implements public class DownloadGroupTarget extends BaseGroupTarget<DownloadGroupTarget> implements
IHttpHeaderTarget<DownloadGroupTarget> { IHttpHeaderDelegate<DownloadGroupTarget> {
private HttpHeaderDelegate<DownloadGroupTarget, DownloadGroupEntity, DownloadGroupTaskEntity> private HttpHeaderDelegate<DownloadGroupTarget> mDelegate;
mDelegate;
/** /**
* 子任务下载地址 * 子任务下载地址
*/ */
@ -67,11 +69,45 @@ public class DownloadGroupTarget extends BaseGroupTarget<DownloadGroupTarget> im
mGroupName = CommonUtil.getMd5Code(mUrls); mGroupName = CommonUtil.getMd5Code(mUrls);
mTaskEntity = TEManager.getInstance().getGTEntity(DownloadGroupTaskEntity.class, mUrls); mTaskEntity = TEManager.getInstance().getGTEntity(DownloadGroupTaskEntity.class, mUrls);
mEntity = mTaskEntity.getEntity(); mEntity = mTaskEntity.getEntity();
if (mEntity != null) { if (mEntity != null) {
mDirPathTemp = mEntity.getDirPath(); mDirPathTemp = mEntity.getDirPath();
} }
mDelegate = new HttpHeaderDelegate<>(this, mTaskEntity); mDelegate = new HttpHeaderDelegate<>(this);
}
/**
* Post处理
*/
public PostDelegate asPost() {
return new PostDelegate<>(this);
}
/**
* 更新组合任务下载地址
*
* @param urls 新的组合任务下载地址列表
*/
@CheckResult
public DownloadGroupTarget updateUrls(List<String> urls) {
if (urls == null || urls.isEmpty()) {
throw new NullPointerException("下载地址列表为空");
}
if (urls.size() != mUrls.size()) {
throw new IllegalArgumentException("新下载地址数量和旧下载地址数量不一致");
}
mUrls.clear();
mUrls.addAll(urls);
mGroupName = CommonUtil.getMd5Code(urls);
mEntity.setGroupName(mGroupName);
mTaskEntity.setKey(mGroupName);
mEntity.update();
if (mEntity.getSubEntities() != null && !mEntity.getSubEntities().isEmpty()) {
for (DownloadEntity de : mEntity.getSubEntities()) {
de.setGroupName(mGroupName);
de.update();
}
}
return this;
} }
/** /**
@ -150,6 +186,12 @@ public class DownloadGroupTarget extends BaseGroupTarget<DownloadGroupTarget> im
return false; return false;
} }
if (mTaskEntity.getRequestEnum() == RequestEnum.POST) {
for (DownloadTaskEntity subTask : mTaskEntity.getSubTaskEntities()) {
subTask.setRequestEnum(RequestEnum.POST);
}
}
mEntity.save(); mEntity.save();
mTaskEntity.save(); mTaskEntity.save();
@ -234,10 +276,17 @@ public class DownloadGroupTarget extends BaseGroupTarget<DownloadGroupTarget> im
if (!newName.equals(entity.getFileName())) { if (!newName.equals(entity.getFileName())) {
String oldPath = mEntity.getDirPath() + "/" + entity.getFileName(); String oldPath = mEntity.getDirPath() + "/" + entity.getFileName();
String newPath = mEntity.getDirPath() + "/" + newName; String newPath = mEntity.getDirPath() + "/" + newName;
if (DbEntity.checkDataExist(DownloadEntity.class, "downloadPath=? or isComplete='true'",
newPath)) {
ALog.w(TAG, String.format("更新文件名失败,路径【%s】已存在或文件已下载", newPath));
return;
}
File oldFile = new File(oldPath); File oldFile = new File(oldPath);
if (oldFile.exists()) { if (oldFile.exists()) {
oldFile.renameTo(new File(newPath)); oldFile.renameTo(new File(newPath));
} }
CommonUtil.modifyTaskRecord(oldFile.getPath(), newPath); CommonUtil.modifyTaskRecord(oldFile.getPath(), newPath);
entity.setDownloadPath(newPath); entity.setDownloadPath(newPath);
taskEntity.setKey(newPath); taskEntity.setKey(newPath);
@ -263,6 +312,7 @@ public class DownloadGroupTarget extends BaseGroupTarget<DownloadGroupTarget> im
return true; return true;
} }
@CheckResult
@Override public DownloadGroupTarget addHeader(@NonNull String key, @NonNull String value) { @Override public DownloadGroupTarget addHeader(@NonNull String key, @NonNull String value) {
for (DownloadTaskEntity subTask : mTaskEntity.getSubTaskEntities()) { for (DownloadTaskEntity subTask : mTaskEntity.getSubTaskEntities()) {
mDelegate.addHeader(subTask, key, value); mDelegate.addHeader(subTask, key, value);
@ -270,6 +320,7 @@ public class DownloadGroupTarget extends BaseGroupTarget<DownloadGroupTarget> im
return mDelegate.addHeader(key, value); return mDelegate.addHeader(key, value);
} }
@CheckResult
@Override public DownloadGroupTarget addHeaders(Map<String, String> headers) { @Override public DownloadGroupTarget addHeaders(Map<String, String> headers) {
for (DownloadTaskEntity subTask : mTaskEntity.getSubTaskEntities()) { for (DownloadTaskEntity subTask : mTaskEntity.getSubTaskEntities()) {
mDelegate.addHeaders(subTask, headers); mDelegate.addHeaders(subTask, headers);
@ -277,10 +328,8 @@ public class DownloadGroupTarget extends BaseGroupTarget<DownloadGroupTarget> im
return mDelegate.addHeaders(headers); return mDelegate.addHeaders(headers);
} }
@Override public DownloadGroupTarget setRequestMode(RequestEnum requestEnum) { @CheckResult
for (DownloadTaskEntity subTask : mTaskEntity.getSubTaskEntities()) { @Override public DownloadGroupTarget setUrlProxy(Proxy proxy) {
subTask.setRequestEnum(requestEnum); return mDelegate.setUrlProxy(proxy);
}
return mDelegate.setRequestMode(requestEnum);
} }
} }

@ -18,23 +18,20 @@ package com.arialyy.aria.core.download;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log;
import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.core.download.downloader.DownloadGroupUtil; import com.arialyy.aria.core.download.downloader.DownloadGroupUtil;
import com.arialyy.aria.core.download.downloader.FtpDirDownloadUtil; import com.arialyy.aria.core.download.downloader.FtpDirDownloadUtil;
import com.arialyy.aria.core.download.downloader.IDownloadGroupListener;
import com.arialyy.aria.core.inf.AbsGroupTask; import com.arialyy.aria.core.inf.AbsGroupTask;
import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.inf.AbsTaskEntity;
import com.arialyy.aria.core.scheduler.ISchedulers; import com.arialyy.aria.core.scheduler.ISchedulers;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CheckUtil; import com.arialyy.aria.util.CheckUtil;
/** /**
* Created by AriaL on 2017/6/27. * Created by AriaL on 2017/6/27.
* 任务组任务 * 任务组任务
*/ */
public class DownloadGroupTask extends AbsGroupTask<DownloadGroupTaskEntity> { public class DownloadGroupTask extends AbsGroupTask<DownloadGroupEntity, DownloadGroupTaskEntity> {
private final String TAG = "DownloadGroupTask";
private DownloadGroupListener mListener;
private DownloadGroupTask(DownloadGroupTaskEntity taskEntity, Handler outHandler) { private DownloadGroupTask(DownloadGroupTaskEntity taskEntity, Handler outHandler) {
mTaskEntity = taskEntity; mTaskEntity = taskEntity;
@ -43,49 +40,18 @@ public class DownloadGroupTask extends AbsGroupTask<DownloadGroupTaskEntity> {
mListener = new DownloadGroupListener(this, mOutHandler); mListener = new DownloadGroupListener(this, mOutHandler);
switch (taskEntity.getRequestType()) { switch (taskEntity.getRequestType()) {
case AbsTaskEntity.D_HTTP: case AbsTaskEntity.D_HTTP:
mUtil = new DownloadGroupUtil(mListener, mTaskEntity); mUtil = new DownloadGroupUtil((IDownloadGroupListener) mListener, mTaskEntity);
break; break;
case AbsTaskEntity.D_FTP_DIR: case AbsTaskEntity.D_FTP_DIR:
mUtil = new FtpDirDownloadUtil(mListener, mTaskEntity); mUtil = new FtpDirDownloadUtil((IDownloadGroupListener) mListener, mTaskEntity);
break; break;
} }
Log.d(TAG, "FTP_TASK_MD5:" + mTaskEntity.hashCode());
}
@Override public boolean isRunning() {
return mUtil.isRunning();
} }
public DownloadGroupEntity getEntity() { public DownloadGroupEntity getEntity() {
return mTaskEntity.getEntity(); return mTaskEntity.getEntity();
} }
@Override public void start() {
if (mUtil.isRunning()) {
ALog.d(TAG, "任务正在下载");
} else {
mUtil.start();
}
}
@Override public void stop() {
super.stop();
if (!mUtil.isRunning()) {
mListener.onStop(getCurrentProgress());
} else {
mUtil.stop();
}
}
@Override public void cancel() {
super.cancel();
if (!mUtil.isRunning()) {
mListener.onCancel();
} else {
mUtil.cancel();
}
}
@Override public String getTaskName() { @Override public String getTaskName() {
return "任务组->" + (TextUtils.isEmpty(mTaskEntity.getEntity().getAlias()) return "任务组->" + (TextUtils.isEmpty(mTaskEntity.getEntity().getAlias())
? mTaskEntity.getEntity().getGroupName() : mTaskEntity.getEntity().getAlias()); ? mTaskEntity.getEntity().getGroupName() : mTaskEntity.getEntity().getAlias());
@ -94,11 +60,9 @@ public class DownloadGroupTask extends AbsGroupTask<DownloadGroupTaskEntity> {
public static class Builder { public static class Builder {
DownloadGroupTaskEntity taskEntity; DownloadGroupTaskEntity taskEntity;
Handler outHandler; Handler outHandler;
String targetName;
public Builder(String targetName, DownloadGroupTaskEntity taskEntity) { public Builder(DownloadGroupTaskEntity taskEntity) {
CheckUtil.checkTaskEntity(taskEntity); CheckUtil.checkTaskEntity(taskEntity);
this.targetName = targetName;
this.taskEntity = taskEntity; this.taskEntity = taskEntity;
} }
@ -108,19 +72,12 @@ public class DownloadGroupTask extends AbsGroupTask<DownloadGroupTaskEntity> {
* @param schedulers {@link ISchedulers} * @param schedulers {@link ISchedulers}
*/ */
public DownloadGroupTask.Builder setOutHandler(ISchedulers schedulers) { public DownloadGroupTask.Builder setOutHandler(ISchedulers schedulers) {
try {
outHandler = new Handler(schedulers);
} catch (Exception e) {
e.printStackTrace();
outHandler = new Handler(Looper.getMainLooper(), schedulers); outHandler = new Handler(Looper.getMainLooper(), schedulers);
}
return this; return this;
} }
public DownloadGroupTask build() { public DownloadGroupTask build() {
DownloadGroupTask task = new DownloadGroupTask(taskEntity, outHandler); return new DownloadGroupTask(taskEntity, outHandler);
task.setTargetName(targetName);
return task;
} }
} }
} }

@ -47,12 +47,12 @@ public class DownloadReceiver extends AbsReceiver {
/** /**
* 设置最大下载速度单位kb * 设置最大下载速度单位kb
* 该方法为实验性功能清不要轻易在生产环境中使用
* *
* @param maxSpeed 为0表示不限速 * @param maxSpeed 为0表示不限速
*/ */
@Deprecated public void setMaxSpeed(int maxSpeed) { public DownloadReceiver setMaxSpeed(int maxSpeed) {
AriaManager.getInstance(AriaManager.APP).getDownloadConfig().setMaxSpeed(maxSpeed); AriaManager.getInstance(AriaManager.APP).getDownloadConfig().setMaxSpeed(maxSpeed);
return this;
} }
/** /**
@ -160,17 +160,19 @@ public class DownloadReceiver extends AbsReceiver {
ALog.e(TAG, String.format("【%s】观察者为空", targetName)); ALog.e(TAG, String.format("【%s】观察者为空", targetName));
return; return;
} }
Set<String> dCounter = ProxyHelper.getInstance().downloadCounter; Set<Integer> set = ProxyHelper.getInstance().checkProxyType(obj.getClass());
Set<String> dgCounter = ProxyHelper.getInstance().downloadGroupCounter; if (set != null && !set.isEmpty()) {
Set<String> dgsCounter = ProxyHelper.getInstance().downloadGroupSubCounter; for (Integer type : set) {
if (dCounter != null && dCounter.contains(targetName)) { if (type == ProxyHelper.PROXY_TYPE_DOWNLOAD) {
DownloadSchedulers.getInstance().register(obj); DownloadSchedulers.getInstance().register(obj);
} } else if (type == ProxyHelper.PROXY_TYPE_DOWNLOAD_GROUP) {
if ((dgCounter != null && dgCounter.contains(targetName)) || (dgsCounter != null
&& dgsCounter.contains(targetName))) {
DownloadGroupSchedulers.getInstance().register(obj); DownloadGroupSchedulers.getInstance().register(obj);
} }
} }
} else {
ALog.w(TAG, "没有Aria的注解方法");
}
}
/** /**
* 取消注册如果是Activity或fragmentAria会界面销毁时自动调用该方法不需要你手动调用 * 取消注册如果是Activity或fragmentAria会界面销毁时自动调用该方法不需要你手动调用
@ -204,17 +206,17 @@ public class DownloadReceiver extends AbsReceiver {
ALog.e(TAG, String.format("【%s】观察者为空", targetName)); ALog.e(TAG, String.format("【%s】观察者为空", targetName));
return; return;
} }
Set<String> dCounter = ProxyHelper.getInstance().downloadCounter; Set<Integer> set = ProxyHelper.getInstance().mProxyCache.get(obj.getClass().getName());
Set<String> dgCounter = ProxyHelper.getInstance().downloadGroupCounter; if (set != null) {
Set<String> dgsCounter = ProxyHelper.getInstance().downloadGroupSubCounter; for (Integer integer : set) {
if (dCounter != null && dCounter.contains(targetName)) { if (integer == ProxyHelper.PROXY_TYPE_DOWNLOAD) {
DownloadSchedulers.getInstance().unRegister(obj); DownloadSchedulers.getInstance().unRegister(obj);
} } else if (integer == ProxyHelper.PROXY_TYPE_DOWNLOAD_GROUP) {
if (dgCounter != null && dgCounter.contains(targetName) || (dgsCounter != null
&& dgsCounter.contains(targetName))) {
DownloadGroupSchedulers.getInstance().unRegister(obj); DownloadGroupSchedulers.getInstance().unRegister(obj);
} }
} }
}
}
/** /**
* 通过下载链接获取下载实体 * 通过下载链接获取下载实体
@ -286,7 +288,7 @@ public class DownloadReceiver extends AbsReceiver {
* @return {@code true}存在{@code false} 不存在 * @return {@code true}存在{@code false} 不存在
*/ */
public boolean taskExists(String downloadUrl) { public boolean taskExists(String downloadUrl) {
return DownloadEntity.findFirst(DownloadEntity.class, "url=?", downloadUrl) != null; return DbEntity.checkDataExist(DownloadTaskEntity.class, "url=?", downloadUrl);
} }
/** /**
@ -299,8 +301,7 @@ public class DownloadReceiver extends AbsReceiver {
return false; return false;
} }
String groupName = CommonUtil.getMd5Code(urls); String groupName = CommonUtil.getMd5Code(urls);
return DownloadGroupEntity.findFirst(DownloadGroupEntity.class, "groupName=?", groupName) return DbEntity.checkDataExist(DownloadGroupEntity.class, "groupName=?", groupName);
!= null;
} }
/** /**
@ -368,7 +369,7 @@ public class DownloadReceiver extends AbsReceiver {
public void stopAllTask() { public void stopAllTask() {
AriaManager.getInstance(AriaManager.APP) AriaManager.getInstance(AriaManager.APP)
.setCmd(NormalCmdFactory.getInstance() .setCmd(NormalCmdFactory.getInstance()
.createCmd(targetName, new DownloadTaskEntity(), NormalCmdFactory.TASK_STOP_ALL, .createCmd(new DownloadTaskEntity(), NormalCmdFactory.TASK_STOP_ALL,
ICmd.TASK_TYPE_DOWNLOAD)) ICmd.TASK_TYPE_DOWNLOAD))
.exe(); .exe();
} }
@ -381,7 +382,7 @@ public class DownloadReceiver extends AbsReceiver {
public void resumeAllTask() { public void resumeAllTask() {
AriaManager.getInstance(AriaManager.APP) AriaManager.getInstance(AriaManager.APP)
.setCmd(NormalCmdFactory.getInstance() .setCmd(NormalCmdFactory.getInstance()
.createCmd(targetName, new DownloadTaskEntity(), NormalCmdFactory.TASK_RESUME_ALL, .createCmd(new DownloadTaskEntity(), NormalCmdFactory.TASK_RESUME_ALL,
ICmd.TASK_TYPE_DOWNLOAD)) ICmd.TASK_TYPE_DOWNLOAD))
.exe(); .exe();
} }
@ -395,7 +396,7 @@ public class DownloadReceiver extends AbsReceiver {
public void removeAllTask(boolean removeFile) { public void removeAllTask(boolean removeFile) {
final AriaManager ariaManager = AriaManager.getInstance(AriaManager.APP); final AriaManager ariaManager = AriaManager.getInstance(AriaManager.APP);
CancelAllCmd cancelCmd = CancelAllCmd cancelCmd =
(CancelAllCmd) CommonUtil.createNormalCmd(targetName, new DownloadTaskEntity(), (CancelAllCmd) CommonUtil.createNormalCmd(new DownloadTaskEntity(),
NormalCmdFactory.TASK_CANCEL_ALL, ICmd.TASK_TYPE_DOWNLOAD); NormalCmdFactory.TASK_CANCEL_ALL, ICmd.TASK_TYPE_DOWNLOAD);
cancelCmd.removeFile = removeFile; cancelCmd.removeFile = removeFile;
ariaManager.setCmd(cancelCmd).exe(); ariaManager.setCmd(cancelCmd).exe();

@ -18,8 +18,9 @@ package com.arialyy.aria.core.download;
import android.support.annotation.CheckResult; import android.support.annotation.CheckResult;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import com.arialyy.aria.core.common.RequestEnum; import com.arialyy.aria.core.common.RequestEnum;
import com.arialyy.aria.core.delegate.HttpHeaderDelegate; import com.arialyy.aria.core.common.http.HttpHeaderDelegate;
import com.arialyy.aria.core.inf.IHttpHeaderTarget; import com.arialyy.aria.core.common.http.PostDelegate;
import com.arialyy.aria.core.inf.IHttpHeaderDelegate;
import java.net.Proxy; import java.net.Proxy;
import java.util.Map; import java.util.Map;
@ -28,9 +29,8 @@ import java.util.Map;
* https://github.com/AriaLyy/Aria * https://github.com/AriaLyy/Aria
*/ */
public class DownloadTarget extends BaseNormalTarget<DownloadTarget> public class DownloadTarget extends BaseNormalTarget<DownloadTarget>
implements IHttpHeaderTarget<DownloadTarget> { implements IHttpHeaderDelegate<DownloadTarget> {
private HttpHeaderDelegate<DownloadTarget, DownloadEntity, DownloadTaskEntity> mDelegate; private HttpHeaderDelegate<DownloadTarget> mHeaderDelegate;
DownloadTarget(DownloadEntity entity, String targetName) { DownloadTarget(DownloadEntity entity, String targetName) {
this(entity.getUrl(), targetName); this(entity.getUrl(), targetName);
@ -38,7 +38,14 @@ public class DownloadTarget extends BaseNormalTarget<DownloadTarget>
DownloadTarget(String url, String targetName) { DownloadTarget(String url, String targetName) {
initTarget(url, targetName); initTarget(url, targetName);
mDelegate = new HttpHeaderDelegate<>(this, mTaskEntity); mHeaderDelegate = new HttpHeaderDelegate<>(this);
}
/**
* Post处理
*/
public PostDelegate asPost() {
return new PostDelegate<>(this);
} }
/** /**
@ -53,17 +60,6 @@ public class DownloadTarget extends BaseNormalTarget<DownloadTarget>
return this; return this;
} }
/**
* 设置URL的代理
*
* @param proxy {@link Proxy}
*/
@CheckResult
public DownloadTarget setUrlProxy(Proxy proxy) {
mTaskEntity.setProxy(proxy);
return this;
}
/** /**
* 设置文件存储路径 * 设置文件存储路径
* 该api后续版本会删除 * 该api后续版本会删除
@ -89,6 +85,21 @@ public class DownloadTarget extends BaseNormalTarget<DownloadTarget>
return this; return this;
} }
/**
* 设置文件存储路径如果需要修改新的文件名修改路径便可
* 原文件路径 /mnt/sdcard/test.zip
* 如果需要将test.zip改为game.zip只需要重新设置文件路径为/mnt/sdcard/game.zip
*
* @param filePath 路径必须为文件路径不能为文件夹路径
* @param forceDownload {@code true}强制下载不考虑未见路径是否被占用
*/
@CheckResult
public DownloadTarget setFilePath(@NonNull String filePath, boolean forceDownload) {
mTempFilePath = filePath;
this.forceDownload = forceDownload;
return this;
}
/** /**
* 从header中获取文件描述信息 * 从header中获取文件描述信息
*/ */
@ -100,18 +111,24 @@ public class DownloadTarget extends BaseNormalTarget<DownloadTarget>
return HTTP; return HTTP;
} }
/**
* 设置URL的代理
*
* @param proxy {@link Proxy}
*/
@CheckResult
@Override public DownloadTarget setUrlProxy(Proxy proxy) {
return mHeaderDelegate.setUrlProxy(proxy);
}
@CheckResult @CheckResult
@Override public DownloadTarget addHeader(@NonNull String key, @NonNull String value) { @Override public DownloadTarget addHeader(@NonNull String key, @NonNull String value) {
return mDelegate.addHeader(key, value); return mHeaderDelegate.addHeader(key, value);
} }
@CheckResult @CheckResult
@Override public DownloadTarget addHeaders(Map<String, String> headers) { @Override public DownloadTarget addHeaders(Map<String, String> headers) {
return mDelegate.addHeaders(headers); return mHeaderDelegate.addHeaders(headers);
} }
@CheckResult
@Override public DownloadTarget setRequestMode(RequestEnum requestEnum) {
return mDelegate.setRequestMode(requestEnum);
}
} }

@ -19,9 +19,9 @@ package com.arialyy.aria.core.download;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.core.common.IUtil;
import com.arialyy.aria.core.download.downloader.SimpleDownloadUtil; import com.arialyy.aria.core.download.downloader.SimpleDownloadUtil;
import com.arialyy.aria.core.inf.AbsNormalTask; import com.arialyy.aria.core.inf.AbsNormalTask;
import com.arialyy.aria.core.inf.IDownloadListener;
import com.arialyy.aria.core.scheduler.ISchedulers; import com.arialyy.aria.core.scheduler.ISchedulers;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import java.io.File; import java.io.File;
@ -30,19 +30,15 @@ import java.io.File;
* Created by lyy on 2016/8/11. * Created by lyy on 2016/8/11.
* 下载任务类 * 下载任务类
*/ */
public class DownloadTask extends AbsNormalTask<DownloadTaskEntity> { public class DownloadTask extends AbsNormalTask<DownloadEntity, DownloadTaskEntity> {
public static final String TAG = "DownloadTask"; public static final String TAG = "DownloadTask";
private DownloadListener mListener;
private DownloadEntity mEntity;
private IUtil mUtil;
private DownloadTask(DownloadTaskEntity taskEntity, Handler outHandler) { private DownloadTask(DownloadTaskEntity taskEntity, Handler outHandler) {
mTaskEntity = taskEntity; mTaskEntity = taskEntity;
mOutHandler = outHandler; mOutHandler = outHandler;
mContext = AriaManager.APP; mContext = AriaManager.APP;
mListener = new DownloadListener(this, mOutHandler); mListener = new BaseDListener(this, mOutHandler);
mUtil = new SimpleDownloadUtil(taskEntity, mListener); mUtil = new SimpleDownloadUtil(taskEntity, (IDownloadListener) mListener);
mEntity = taskEntity.getEntity(); mEntity = taskEntity.getEntity();
} }
@ -76,76 +72,10 @@ public class DownloadTask extends AbsNormalTask<DownloadTaskEntity> {
return mEntity.getUrl(); return mEntity.getUrl();
} }
/**
* 是否真正下载
*
* @return {@code true} 真正下载
*/
@Override public boolean isRunning() {
return mUtil.isRunning();
}
public DownloadEntity getDownloadEntity() { public DownloadEntity getDownloadEntity() {
return mEntity; return mEntity;
} }
/**
* 暂停任务并让任务处于等待状态
*/
@Override public void stopAndWait() {
stop(true);
}
/**
* 设置最大下载速度单位kb
*
* @param maxSpeed 为0表示不限速
*/
public void setMaxSpeed(double maxSpeed) {
mUtil.setMaxSpeed(maxSpeed);
}
/**
* 开始下载
*/
@Override public void start() {
mListener.isWait = false;
if (mUtil.isRunning()) {
ALog.d(TAG, "任务正在下载");
} else {
mUtil.start();
}
}
/**
* 停止下载
*/
@Override public void stop() {
super.stop();
stop(false);
}
private void stop(boolean isWait) {
mListener.isWait = isWait;
if (mUtil.isRunning()) {
mUtil.stop();
} else {
mListener.onStop(mEntity.getCurrentProgress());
}
}
/**
* 取消下载
*/
@Override public void cancel() {
super.cancel();
if (mUtil.isRunning()) {
mUtil.cancel();
} else {
mListener.onCancel();
}
}
@Override public String getTaskName() { @Override public String getTaskName() {
return mEntity.getFileName(); return mEntity.getFileName();
} }
@ -153,10 +83,8 @@ public class DownloadTask extends AbsNormalTask<DownloadTaskEntity> {
public static class Builder { public static class Builder {
DownloadTaskEntity taskEntity; DownloadTaskEntity taskEntity;
Handler outHandler; Handler outHandler;
String targetName;
public Builder(String targetName, DownloadTaskEntity taskEntity) { public Builder(DownloadTaskEntity taskEntity) {
this.targetName = targetName;
this.taskEntity = taskEntity; this.taskEntity = taskEntity;
} }
@ -166,19 +94,12 @@ public class DownloadTask extends AbsNormalTask<DownloadTaskEntity> {
* @param schedulers {@link ISchedulers} * @param schedulers {@link ISchedulers}
*/ */
public Builder setOutHandler(ISchedulers schedulers) { public Builder setOutHandler(ISchedulers schedulers) {
try {
outHandler = new Handler(schedulers);
} catch (Exception e) {
ALog.w(TAG, ALog.getExceptionString(e));
outHandler = new Handler(Looper.getMainLooper(), schedulers); outHandler = new Handler(Looper.getMainLooper(), schedulers);
}
return this; return this;
} }
public DownloadTask build() { public DownloadTask build() {
DownloadTask task = new DownloadTask(taskEntity, outHandler); return new DownloadTask(taskEntity, outHandler);
task.setTargetName(targetName);
return task;
} }
} }
} }

@ -21,6 +21,7 @@ import com.arialyy.aria.orm.annotation.Foreign;
import com.arialyy.aria.orm.annotation.Ignore; import com.arialyy.aria.orm.annotation.Ignore;
import com.arialyy.aria.orm.annotation.NoNull; import com.arialyy.aria.orm.annotation.NoNull;
import com.arialyy.aria.orm.annotation.Primary; import com.arialyy.aria.orm.annotation.Primary;
import java.util.Map;
/** /**
* Created by lyy on 2017/1/23. * Created by lyy on 2017/1/23.
@ -60,8 +61,6 @@ public class DownloadTaskEntity extends AbsNormalTaskEntity<DownloadEntity> {
onUpdate = ActionPolicy.CASCADE, onDelete = ActionPolicy.CASCADE) onUpdate = ActionPolicy.CASCADE, onDelete = ActionPolicy.CASCADE)
private String key; private String key;
public DownloadTaskEntity() { public DownloadTaskEntity() {
} }
@ -112,6 +111,4 @@ public class DownloadTaskEntity extends AbsNormalTaskEntity<DownloadEntity> {
public void setGroupTask(boolean groupTask) { public void setGroupTask(boolean groupTask) {
isGroupTask = groupTask; isGroupTask = groupTask;
} }
} }

@ -17,11 +17,14 @@ package com.arialyy.aria.core.download;
import android.support.annotation.CheckResult; import android.support.annotation.CheckResult;
import android.text.TextUtils; import android.text.TextUtils;
import com.arialyy.aria.core.delegate.FtpDelegate; import com.arialyy.aria.core.FtpUrlEntity;
import com.arialyy.aria.core.common.ftp.FTPSConfig;
import com.arialyy.aria.core.common.ftp.FtpDelegate;
import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.inf.AbsTaskEntity;
import com.arialyy.aria.core.inf.IFtpTarget; import com.arialyy.aria.core.inf.IFtpTarget;
import com.arialyy.aria.core.manager.TEManager; import com.arialyy.aria.core.manager.TEManager;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import java.net.Proxy;
/** /**
* Created by Aria.Lao on 2017/7/26. * Created by Aria.Lao on 2017/7/26.
@ -29,7 +32,7 @@ import com.arialyy.aria.util.ALog;
*/ */
public class FtpDirDownloadTarget extends BaseGroupTarget<FtpDirDownloadTarget> public class FtpDirDownloadTarget extends BaseGroupTarget<FtpDirDownloadTarget>
implements IFtpTarget<FtpDirDownloadTarget> { implements IFtpTarget<FtpDirDownloadTarget> {
private FtpDelegate<FtpDirDownloadTarget, DownloadGroupEntity, DownloadGroupTaskEntity> mDelegate; private FtpDelegate<FtpDirDownloadTarget> mDelegate;
FtpDirDownloadTarget(String url, String targetName) { FtpDirDownloadTarget(String url, String targetName) {
mTargetName = targetName; mTargetName = targetName;
@ -44,7 +47,7 @@ public class FtpDirDownloadTarget extends BaseGroupTarget<FtpDirDownloadTarget>
if (mEntity != null) { if (mEntity != null) {
mDirPathTemp = mEntity.getDirPath(); mDirPathTemp = mEntity.getDirPath();
} }
mDelegate = new FtpDelegate<>(this, mTaskEntity); mDelegate = new FtpDelegate<>(this);
} }
@Override protected int getTargetType() { @Override protected int getTargetType() {
@ -58,14 +61,34 @@ public class FtpDirDownloadTarget extends BaseGroupTarget<FtpDirDownloadTarget>
mTaskEntity.save(); mTaskEntity.save();
if (mTaskEntity.getSubTaskEntities() != null) { if (mTaskEntity.getSubTaskEntities() != null) {
//初始化子项的登录信息 //初始化子项的登录信息
FtpUrlEntity tUrlEntity = mTaskEntity.getUrlEntity();
for (DownloadTaskEntity entity : mTaskEntity.getSubTaskEntities()) { for (DownloadTaskEntity entity : mTaskEntity.getSubTaskEntities()) {
entity.getUrlEntity().needLogin = mTaskEntity.getUrlEntity().needLogin; FtpUrlEntity urlEntity = entity.getUrlEntity();
entity.getUrlEntity().account = mTaskEntity.getUrlEntity().account; urlEntity.needLogin = tUrlEntity.needLogin;
entity.getUrlEntity().user = mTaskEntity.getUrlEntity().user; urlEntity.account = tUrlEntity.account;
entity.getUrlEntity().password = mTaskEntity.getUrlEntity().password; urlEntity.user = tUrlEntity.user;
urlEntity.password = tUrlEntity.password;
// 处理ftps详细
if (tUrlEntity.isFtps) {
urlEntity.isFtps = true;
urlEntity.protocol = tUrlEntity.protocol;
urlEntity.storePath = tUrlEntity.storePath;
urlEntity.storePass = tUrlEntity.storePass;
urlEntity.keyAlias = tUrlEntity.keyAlias;
} }
} }
} }
}
if (mTaskEntity.getUrlEntity().isFtps) {
if (TextUtils.isEmpty(mTaskEntity.getUrlEntity().storePath)) {
ALog.e(TAG, "证书路径为空");
return false;
}
if (TextUtils.isEmpty(mTaskEntity.getUrlEntity().keyAlias)) {
ALog.e(TAG, "证书别名为空");
return false;
}
}
return b; return b;
} }
@ -90,16 +113,34 @@ public class FtpDirDownloadTarget extends BaseGroupTarget<FtpDirDownloadTarget>
} }
return true; return true;
} }
/**
* 是否是FTPS协议
* 如果是FTPS协议需要使用{@link FTPSConfig#setStorePath(String)} {@link FTPSConfig#setAlias(String)}
* 设置证书信息
*/
@CheckResult
public FTPSConfig<FtpDirDownloadTarget> asFtps() {
mTaskEntity.getUrlEntity().isFtps = true;
return new FTPSConfig<>(this);
}
@CheckResult @CheckResult
@Override public FtpDirDownloadTarget charSet(String charSet) { @Override public FtpDirDownloadTarget charSet(String charSet) {
return mDelegate.charSet(charSet); return mDelegate.charSet(charSet);
} }
@CheckResult @CheckResult
@Override public FtpDirDownloadTarget login(String userName, String password) { @Override public FtpDirDownloadTarget login(String userName, String password) {
return mDelegate.login(userName, password); return mDelegate.login(userName, password);
} }
@CheckResult @CheckResult
@Override public FtpDirDownloadTarget login(String userName, String password, String account) { @Override public FtpDirDownloadTarget login(String userName, String password, String account) {
return mDelegate.login(userName, password, account); return mDelegate.login(userName, password, account);
} }
@Override public FtpDirDownloadTarget setProxy(Proxy proxy) {
return mDelegate.setProxy(proxy);
}
} }

@ -17,10 +17,14 @@ package com.arialyy.aria.core.download;
import android.support.annotation.CheckResult; import android.support.annotation.CheckResult;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import com.arialyy.aria.core.delegate.FtpDelegate; import android.text.TextUtils;
import com.arialyy.aria.core.common.ftp.FTPSConfig;
import com.arialyy.aria.core.common.ftp.FtpDelegate;
import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.inf.AbsTaskEntity;
import com.arialyy.aria.core.inf.IFtpTarget; import com.arialyy.aria.core.inf.IFtpTarget;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.CommonUtil;
import java.net.Proxy;
/** /**
* Created by lyy on 2016/12/5. * Created by lyy on 2016/12/5.
@ -28,7 +32,7 @@ import com.arialyy.aria.util.CommonUtil;
*/ */
public class FtpDownloadTarget extends BaseNormalTarget<FtpDownloadTarget> public class FtpDownloadTarget extends BaseNormalTarget<FtpDownloadTarget>
implements IFtpTarget<FtpDownloadTarget> { implements IFtpTarget<FtpDownloadTarget> {
private FtpDelegate<FtpDownloadTarget, DownloadEntity, DownloadTaskEntity> mDelegate; private FtpDelegate<FtpDownloadTarget> mDelegate;
public FtpDownloadTarget(DownloadEntity entity, String targetName) { public FtpDownloadTarget(DownloadEntity entity, String targetName) {
this(entity.getUrl(), targetName); this(entity.getUrl(), targetName);
@ -45,7 +49,32 @@ public class FtpDownloadTarget extends BaseNormalTarget<FtpDownloadTarget>
mTaskEntity.setUrlEntity(CommonUtil.getFtpUrlInfo(url)); mTaskEntity.setUrlEntity(CommonUtil.getFtpUrlInfo(url));
mTaskEntity.setRequestType(AbsTaskEntity.D_FTP); mTaskEntity.setRequestType(AbsTaskEntity.D_FTP);
mDelegate = new FtpDelegate<>(this, mTaskEntity); mDelegate = new FtpDelegate<>(this);
}
/**
* 是否是FTPS协议
* 如果是FTPS协议需要使用{@link FTPSConfig#setStorePath(String)} {@link FTPSConfig#setAlias(String)}
* 设置证书信息
*/
@CheckResult
public FTPSConfig<FtpDownloadTarget> asFtps() {
mTaskEntity.getUrlEntity().isFtps = true;
return new FTPSConfig<>(this);
}
@Override protected boolean checkEntity() {
if (mTaskEntity.getUrlEntity().isFtps){
if (TextUtils.isEmpty(mTaskEntity.getUrlEntity().storePath)){
ALog.e(TAG, "证书路径为空");
return false;
}
if (TextUtils.isEmpty(mTaskEntity.getUrlEntity().keyAlias)){
ALog.e(TAG, "证书别名为空");
return false;
}
}
return super.checkEntity();
} }
/** /**
@ -90,4 +119,9 @@ public class FtpDownloadTarget extends BaseNormalTarget<FtpDownloadTarget>
@Override public FtpDownloadTarget login(String userName, String password, String account) { @Override public FtpDownloadTarget login(String userName, String password, String account) {
return mDelegate.login(userName, password, account); return mDelegate.login(userName, password, account);
} }
@CheckResult
@Override public FtpDownloadTarget setProxy(Proxy proxy) {
return mDelegate.setProxy(proxy);
}
} }

@ -22,23 +22,23 @@ import com.arialyy.aria.core.download.DownloadGroupTaskEntity;
import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.download.DownloadTaskEntity;
import com.arialyy.aria.core.inf.IDownloadListener; import com.arialyy.aria.core.inf.IDownloadListener;
import com.arialyy.aria.core.inf.IEntity; 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.ALog;
import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.NetUtils; import com.arialyy.aria.util.NetUtils;
import java.io.File; import java.io.File;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.Timer; import java.util.concurrent.ConcurrentHashMap;
import java.util.TimerTask; import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit;
import java.util.concurrent.Executors;
/** /**
* Created by AriaL on 2017/6/30. * Created by AriaL on 2017/6/30.
* 任务组核心逻辑 * 任务组核心逻辑
*/ */
public abstract class AbsGroupUtil implements IUtil { public abstract class AbsGroupUtil implements IUtil, Runnable {
private static final Object LOCK = new Object(); private static final Object LOCK = new Object();
private final String TAG = "AbsGroupUtil"; private final String TAG = "AbsGroupUtil";
/** /**
@ -55,30 +55,29 @@ public abstract class AbsGroupUtil implements IUtil {
*/ */
long mTotalLen = 0; long mTotalLen = 0;
long mCurrentLocation = 0; long mCurrentLocation = 0;
private ExecutorService mExePool;
protected IDownloadGroupListener mListener; protected IDownloadGroupListener mListener;
DownloadGroupTaskEntity mGTEntity; DownloadGroupTaskEntity mGTEntity;
private boolean isRunning = false; private boolean isRunning = false;
private Timer mTimer; private ScheduledThreadPoolExecutor mTimer;
/** /**
* 保存所有没有下载完成的任务key为下载地址 * 保存所有没有下载完成的任务key为下载地址
*/ */
Map<String, DownloadTaskEntity> mExeMap = new HashMap<>(); Map<String, DownloadTaskEntity> mExeMap = new ConcurrentHashMap<>();
/** /**
* 下载失败的映射表key为下载地址 * 下载失败的映射表key为下载地址
*/ */
Map<String, DownloadTaskEntity> mFailMap = new HashMap<>(); Map<String, DownloadTaskEntity> mFailMap = new ConcurrentHashMap<>();
/** /**
* 该任务组对应的所有任务 * 该任务组对应的所有任务
*/ */
private Map<String, DownloadTaskEntity> mTasksMap = new HashMap<>(); private Map<String, DownloadTaskEntity> mTasksMap = new ConcurrentHashMap<>();
/** /**
* 下载器映射表key为下载地址 * 下载器映射表key为下载地址
*/ */
private Map<String, Downloader> mDownloaderMap = new HashMap<>(); private Map<String, Downloader> mDownloaderMap = new ConcurrentHashMap<>();
/** /**
* 是否需要读取文件长度{@code true}需要 * 是否需要读取文件长度{@code true}需要
@ -96,7 +95,6 @@ public abstract class AbsGroupUtil implements IUtil {
AbsGroupUtil(IDownloadGroupListener listener, DownloadGroupTaskEntity groupEntity) { AbsGroupUtil(IDownloadGroupListener listener, DownloadGroupTaskEntity groupEntity) {
mListener = listener; mListener = listener;
mGTEntity = groupEntity; mGTEntity = groupEntity;
mExePool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
mUpdateInterval = mUpdateInterval =
AriaManager.getInstance(AriaManager.APP).getDownloadConfig().getUpdateInterval(); AriaManager.getInstance(AriaManager.APP).getDownloadConfig().getUpdateInterval();
} }
@ -227,10 +225,6 @@ public abstract class AbsGroupUtil implements IUtil {
isCancel = true; isCancel = true;
closeTimer(); closeTimer();
onCancel(); onCancel();
if (!mExePool.isShutdown()) {
mExePool.shutdown();
}
Set<String> keys = mDownloaderMap.keySet(); Set<String> keys = mDownloaderMap.keySet();
for (String key : keys) { for (String key : keys) {
Downloader dt = mDownloaderMap.get(key); Downloader dt = mDownloaderMap.get(key);
@ -250,9 +244,6 @@ public abstract class AbsGroupUtil implements IUtil {
isStop = true; isStop = true;
closeTimer(); closeTimer();
onStop(); onStop();
if (!mExePool.isShutdown()) {
mExePool.shutdown();
}
Set<String> keys = mDownloaderMap.keySet(); Set<String> keys = mDownloaderMap.keySet();
for (String key : keys) { for (String key : keys) {
@ -275,6 +266,7 @@ public abstract class AbsGroupUtil implements IUtil {
*/ */
protected void onPre() { protected void onPre() {
mListener.onPre(); mListener.onPre();
isRunning = true;
mGroupSize = mGTEntity.getSubTaskEntities().size(); mGroupSize = mGTEntity.getSubTaskEntities().size();
mTotalLen = mGTEntity.getEntity().getFileSize(); mTotalLen = mGTEntity.getEntity().getFileSize();
isNeedLoadFileSize = mTotalLen <= 10; isNeedLoadFileSize = mTotalLen <= 10;
@ -295,6 +287,10 @@ public abstract class AbsGroupUtil implements IUtil {
} }
@Override public void start() { @Override public void start() {
new Thread(this).start();
}
@Override public void run() {
if (isStop || isCancel) { if (isStop || isCancel) {
closeTimer(); closeTimer();
return; return;
@ -311,8 +307,14 @@ public abstract class AbsGroupUtil implements IUtil {
start(); start();
} }
@Override public void setMaxSpeed(double maxSpeed) { @Override public void setMaxSpeed(int speed) {
Set<String> keys = mDownloaderMap.keySet();
for (String key : keys) {
Downloader dt = mDownloaderMap.get(key);
if (dt != null) {
dt.setMaxSpeed(speed);
}
}
} }
private void clearState() { private void clearState() {
@ -320,12 +322,9 @@ public abstract class AbsGroupUtil implements IUtil {
mFailMap.clear(); mFailMap.clear();
} }
void closeTimer() { synchronized void closeTimer() {
isRunning = false; if (mTimer != null && !mTimer.isShutdown()) {
if (mTimer != null) { mTimer.shutdown();
mTimer.purge();
mTimer.cancel();
mTimer = null;
} }
} }
@ -343,10 +342,10 @@ public abstract class AbsGroupUtil implements IUtil {
startTimer(); startTimer();
} }
private void startTimer() { private synchronized void startTimer() {
isRunning = true; isRunning = true;
mTimer = new Timer(true); mTimer = new ScheduledThreadPoolExecutor(1);
mTimer.schedule(new TimerTask() { mTimer.scheduleWithFixedDelay(new Runnable() {
@Override public void run() { @Override public void run() {
if (!isRunning) { if (!isRunning) {
closeTimer(); closeTimer();
@ -363,7 +362,7 @@ public abstract class AbsGroupUtil implements IUtil {
mListener.onProgress(t); mListener.onProgress(t);
} }
} }
}, 0, mUpdateInterval); }, 0, mUpdateInterval, TimeUnit.MILLISECONDS);
} }
/** /**
@ -382,9 +381,8 @@ public abstract class AbsGroupUtil implements IUtil {
ChildDownloadListener listener = new ChildDownloadListener(taskEntity); ChildDownloadListener listener = new ChildDownloadListener(taskEntity);
Downloader dt = new Downloader(listener, taskEntity); Downloader dt = new Downloader(listener, taskEntity);
mDownloaderMap.put(taskEntity.getEntity().getUrl(), dt); mDownloaderMap.put(taskEntity.getEntity().getUrl(), dt);
if (mExePool.isShutdown()) return dt;
if (start) { if (start) {
mExePool.execute(dt); dt.start();
} }
return dt; return dt;
} }
@ -397,9 +395,9 @@ public abstract class AbsGroupUtil implements IUtil {
private DownloadEntity subEntity; private DownloadEntity subEntity;
private int RUN_SAVE_INTERVAL = 5 * 1000; //5s保存一次下载中的进度 private int RUN_SAVE_INTERVAL = 5 * 1000; //5s保存一次下载中的进度
private long lastSaveTime; private long lastSaveTime;
private long lastLen = 0; private long lastLen;
private Timer timer; private ScheduledThreadPoolExecutor timer;
private boolean isNotNetRetry = false; private boolean isNotNetRetry;
ChildDownloadListener(DownloadTaskEntity entity) { ChildDownloadListener(DownloadTaskEntity entity) {
subTaskEntity = entity; subTaskEntity = entity;
@ -486,7 +484,7 @@ public abstract class AbsGroupUtil implements IUtil {
} }
} }
@Override public void onFail(boolean needRetry) { @Override public void onFail(boolean needRetry, BaseException e) {
subEntity.setFailNum(subEntity.getFailNum() + 1); subEntity.setFailNum(subEntity.getFailNum() + 1);
saveData(IEntity.STATE_FAIL, lastLen); saveData(IEntity.STATE_FAIL, lastLen);
handleSpeed(0); handleSpeed(0);
@ -497,22 +495,26 @@ public abstract class AbsGroupUtil implements IUtil {
* 重试下载只有全部都下载失败才会执行任务组的整体重试否则只会执行单个子任务的重试 * 重试下载只有全部都下载失败才会执行任务组的整体重试否则只会执行单个子任务的重试
*/ */
private void reTry(boolean needRetry) { private void reTry(boolean needRetry) {
Downloader dt = mDownloaderMap.get(subEntity.getUrl());
synchronized (AbsGroupUtil.LOCK) { synchronized (AbsGroupUtil.LOCK) {
Downloader dt = mDownloaderMap.get(subEntity.getUrl());
if (!isCancel && !isStop && dt != null if (!isCancel && !isStop && dt != null
&& !dt.isBreak() && !dt.isBreak()
&& needRetry && needRetry
&& subEntity.getFailNum() < 3 && subEntity.getFailNum() < 3
&& (NetUtils.isConnected(AriaManager.APP) || isNotNetRetry)) { && (NetUtils.isConnected(AriaManager.APP) || isNotNetRetry)) {
ALog.d(TAG, "downloader retry");
reStartTask(dt); reStartTask(dt);
} else { } else {
mFailMap.put(subTaskEntity.getUrl(), subTaskEntity); mFailMap.put(subTaskEntity.getUrl(), subTaskEntity);
mListener.onSubFail(subEntity); mListener.onSubFail(subEntity, new TaskException(TAG,
String.format("任务组子任务【%s】下载失败,下载地址【%s】", subEntity.getFileName(),
subEntity.getUrl())));
if (mFailMap.size() == mExeMap.size() || mFailMap.size() + mCompleteNum == mGroupSize) { if (mFailMap.size() == mExeMap.size() || mFailMap.size() + mCompleteNum == mGroupSize) {
closeTimer(); closeTimer();
} }
if (mFailMap.size() == mGroupSize) { if (mFailMap.size() == mGroupSize) {
mListener.onFail(true); mListener.onFail(true, new TaskException(TAG,
String.format("任务组【%s】下载失败", mGTEntity.getEntity().getGroupName())));
} else if (mFailMap.size() + mCompleteNum >= mExeMap.size()) { } else if (mFailMap.size() + mCompleteNum >= mExeMap.size()) {
mListener.onStop(mCurrentLocation); mListener.onStop(mCurrentLocation);
} }
@ -521,18 +523,16 @@ public abstract class AbsGroupUtil implements IUtil {
} }
private void reStartTask(final Downloader dt) { private void reStartTask(final Downloader dt) {
if (timer != null) { if (timer == null || timer.isShutdown()) {
timer.purge(); timer = new ScheduledThreadPoolExecutor(1);
timer.cancel();
} }
timer = new Timer(); timer.schedule(new Runnable() {
timer.schedule(new TimerTask() {
@Override public void run() { @Override public void run() {
if (dt != null) { if (dt != null) {
dt.retryThreadTask(); dt.retryTask();
} }
} }
}, 5000); }, 5, TimeUnit.SECONDS);
} }
private void handleSpeed(long speed) { private void handleSpeed(long speed) {

@ -16,18 +16,21 @@
package com.arialyy.aria.core.download.downloader; package com.arialyy.aria.core.download.downloader;
import android.text.TextUtils; import android.text.TextUtils;
import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.core.common.ProtocolType;
import com.arialyy.aria.core.common.RequestEnum; import com.arialyy.aria.core.common.RequestEnum;
import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.download.DownloadTaskEntity;
import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.inf.AbsTaskEntity;
import com.arialyy.aria.util.SSLContextUtil; import com.arialyy.aria.util.SSLContextUtil;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.CookieManager;
import java.net.CookieStore;
import java.net.HttpCookie;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.ProtocolException; import java.net.ProtocolException;
import java.net.URL; import java.net.URL;
import java.net.URLConnection; import java.net.URLConnection;
import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
import java.util.zip.InflaterInputStream; import java.util.zip.InflaterInputStream;
@ -75,11 +78,13 @@ class ConnectionHelp {
urlConn = url.openConnection(); urlConn = url.openConnection();
} }
if (urlConn instanceof HttpsURLConnection) { if (urlConn instanceof HttpsURLConnection) {
AriaManager manager = AriaManager.getInstance(AriaManager.APP);
conn = (HttpsURLConnection) urlConn; conn = (HttpsURLConnection) urlConn;
SSLContext sslContext = SSLContext sslContext =
SSLContextUtil.getSSLContext(SSLContextUtil.CA_ALIAS, SSLContextUtil.CA_PATH); SSLContextUtil.getSSLContextFromAssets(manager.getDownloadConfig().getCaName(),
manager.getDownloadConfig().getCaPath(), ProtocolType.Default);
if (sslContext == null) { if (sslContext == null) {
sslContext = SSLContextUtil.getDefaultSLLContext(); sslContext = SSLContextUtil.getDefaultSLLContext(ProtocolType.Default);
} }
SSLSocketFactory ssf = sslContext.getSocketFactory(); SSLSocketFactory ssf = sslContext.getSocketFactory();
((HttpsURLConnection) conn).setSSLSocketFactory(ssf); ((HttpsURLConnection) conn).setSSLSocketFactory(ssf);
@ -98,6 +103,8 @@ class ConnectionHelp {
static HttpURLConnection setConnectParam(DownloadTaskEntity entity, HttpURLConnection conn) { static HttpURLConnection setConnectParam(DownloadTaskEntity entity, HttpURLConnection conn) {
if (entity.getRequestEnum() == RequestEnum.POST) { if (entity.getRequestEnum() == RequestEnum.POST) {
conn.setDoInput(true); conn.setDoInput(true);
conn.setDoOutput(true);
conn.setUseCaches(false);
} }
Set<String> keys = null; Set<String> keys = null;
if (entity.getHeaders() != null && entity.getHeaders().size() > 0) { if (entity.getHeaders() != null && entity.getHeaders().size() > 0) {
@ -106,7 +113,9 @@ class ConnectionHelp {
conn.setRequestProperty(key, entity.getHeaders().get(key)); conn.setRequestProperty(key, entity.getHeaders().get(key));
} }
} }
if (conn.getRequestProperty("Accept-Language") == null) {
conn.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7");
}
if (conn.getRequestProperty("Accept-Encoding") == null) { if (conn.getRequestProperty("Accept-Encoding") == null) {
conn.setRequestProperty("Accept-Encoding", "identity"); conn.setRequestProperty("Accept-Encoding", "identity");
} }
@ -124,7 +133,7 @@ class ConnectionHelp {
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"); "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36");
} }
if (conn.getRequestProperty("Accept") == null) { if (conn.getRequestProperty("Accept") == null) {
StringBuilder accept = new StringBuilder(); //StringBuilder accept = new StringBuilder();
//accept //accept
//.append("image/gif, ") //.append("image/gif, ")
//.append("image/jpeg, ") //.append("image/jpeg, ")
@ -148,6 +157,16 @@ class ConnectionHelp {
} }
//302获取重定向地址 //302获取重定向地址
conn.setInstanceFollowRedirects(false); conn.setInstanceFollowRedirects(false);
CookieManager manager = entity.getCookieManager();
if (manager != null) {
CookieStore store = manager.getCookieStore();
if (store != null && store.getCookies().size() > 0) {
conn.setRequestProperty("Cookie",
TextUtils.join(";", store.getCookies()));
}
}
return conn; return conn;
} }
} }

@ -22,6 +22,8 @@ import com.arialyy.aria.core.common.OnFileInfoCallback;
import com.arialyy.aria.core.download.DownloadGroupTaskEntity; import com.arialyy.aria.core.download.DownloadGroupTaskEntity;
import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.download.DownloadTaskEntity;
import com.arialyy.aria.core.inf.IEntity; 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.ALog;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@ -85,8 +87,8 @@ public class DownloadGroupUtil extends AbsGroupUtil implements IUtil {
} }
if (mExeMap.size() == 0) { if (mExeMap.size() == 0) {
ALog.e(TAG, "任务组无可执行任务"); mListener.onFail(false, new TaskException(TAG,
mListener.onFail(false); String.format("任务组【%s】无可执行任务", mGTEntity.getEntity().getGroupName())));
return; return;
} }
Set<String> keys = mExeMap.keySet(); Set<String> keys = mExeMap.keySet();
@ -133,7 +135,7 @@ public class DownloadGroupUtil extends AbsGroupUtil implements IUtil {
checkStartFlow(); checkStartFlow();
} }
@Override public void onFail(String url, String errorMsg, boolean needRetry) { @Override public void onFail(String url, BaseException e, boolean needRetry) {
if (isStop) return; if (isStop) return;
ALog.e(TAG, String.format("任务【%s】初始化失败", url)); ALog.e(TAG, String.format("任务【%s】初始化失败", url));
DownloadTaskEntity te = mExeMap.get(url); DownloadTaskEntity te = mExeMap.get(url);
@ -163,9 +165,14 @@ public class DownloadGroupUtil extends AbsGroupUtil implements IUtil {
*/ */
private void checkStartFlow() { private void checkStartFlow() {
synchronized (DownloadGroupUtil.class) { synchronized (DownloadGroupUtil.class) {
if (isStop) {
closeTimer();
return;
}
if (mInitFailNum == mExeNum) { if (mInitFailNum == mExeNum) {
closeTimer(); closeTimer();
mListener.onFail(true); mListener.onFail(true, new TaskException(TAG,
String.format("任务组【%s】初始化失败", mGTEntity.getEntity().getGroupName())));
} }
if (!isStart && mInitCompleteNum + mInitFailNum == mExeNum || !isNeedLoadFileSize) { if (!isStart && mInitCompleteNum + mInitFailNum == mExeNum || !isNeedLoadFileSize) {
startRunningFlow(); startRunningFlow();

@ -23,10 +23,9 @@ import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.download.DownloadTaskEntity;
import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.inf.AbsTaskEntity;
import com.arialyy.aria.core.inf.IDownloadListener; import com.arialyy.aria.core.inf.IDownloadListener;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.util.BufferedRandomAccessFile; import com.arialyy.aria.util.BufferedRandomAccessFile;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.ErrorHelp;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -58,7 +57,10 @@ class Downloader extends AbsFileer<DownloadEntity, DownloadTaskEntity> {
@Override protected boolean handleNewTask() { @Override protected boolean handleNewTask() {
if (!mRecord.isBlock) { if (!mRecord.isBlock) {
CommonUtil.createFile(mTempFile.getPath()); if (mTempFile.exists()) {
mTempFile.delete();
}
//CommonUtil.createFile(mTempFile.getPath());
} }
BufferedRandomAccessFile file = null; BufferedRandomAccessFile file = null;
try { try {
@ -69,8 +71,9 @@ class Downloader extends AbsFileer<DownloadEntity, DownloadTaskEntity> {
} }
return true; return true;
} catch (IOException e) { } catch (IOException e) {
failDownload(String.format("下载失败【downloadUrl:%s】;【filePath:%s】\n %S", mEntity.getUrl(), failDownload(new TaskException(TAG,
mEntity.getDownloadPath(), ALog.getExceptionString(e))); String.format("下载失败,filePath: %s, url: %s", mEntity.getDownloadPath(),
mEntity.getUrl()), e));
} finally { } finally {
if (file != null) { if (file != null) {
try { try {
@ -83,6 +86,15 @@ class Downloader extends AbsFileer<DownloadEntity, DownloadTaskEntity> {
return false; return false;
} }
@Override protected void onPostPre() {
super.onPostPre();
((IDownloadListener) mListener).onPostPre(mEntity.getFileSize());
File file = new File(mEntity.getDownloadPath());
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
}
@Override protected AbsThreadTask selectThreadTask(SubThreadConfig<DownloadTaskEntity> config) { @Override protected AbsThreadTask selectThreadTask(SubThreadConfig<DownloadTaskEntity> config) {
switch (mTaskEntity.getRequestType()) { switch (mTaskEntity.getRequestType()) {
case AbsTaskEntity.D_FTP: case AbsTaskEntity.D_FTP:
@ -94,11 +106,9 @@ class Downloader extends AbsFileer<DownloadEntity, DownloadTaskEntity> {
return null; return null;
} }
private void failDownload(String errorMsg) { private void failDownload(BaseException e) {
closeTimer(); closeTimer();
ALog.e(TAG, errorMsg);
mConstance.isRunning = false; mConstance.isRunning = false;
mListener.onFail(false); mListener.onFail(false, e);
ErrorHelp.saveError(TAG, "", errorMsg);
} }
} }

@ -19,6 +19,8 @@ import com.arialyy.aria.core.common.CompleteInfo;
import com.arialyy.aria.core.common.OnFileInfoCallback; import com.arialyy.aria.core.common.OnFileInfoCallback;
import com.arialyy.aria.core.download.DownloadGroupTaskEntity; import com.arialyy.aria.core.download.DownloadGroupTaskEntity;
import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.download.DownloadTaskEntity;
import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.util.ErrorHelp; import com.arialyy.aria.util.ErrorHelp;
import java.util.Set; import java.util.Set;
@ -51,14 +53,13 @@ public class FtpDirDownloadUtil extends AbsGroupUtil {
} }
} }
@Override public void onFail(String url, String errorMsg, boolean needRetry) { @Override public void onFail(String url, BaseException e, boolean needRetry) {
DownloadTaskEntity te = mExeMap.get(url); DownloadTaskEntity te = mExeMap.get(url);
if (te != null) { if (te != null) {
mFailMap.put(url, te); mFailMap.put(url, te);
mExeMap.remove(url); mExeMap.remove(url);
} }
mListener.onFail(needRetry); mListener.onFail(needRetry, e);
ErrorHelp.saveError(TAG, "", errorMsg);
} }
}).start(); }).start();
} }

@ -15,10 +15,11 @@
*/ */
package com.arialyy.aria.core.download.downloader; package com.arialyy.aria.core.download.downloader;
import aria.apache.commons.net.ftp.FTPFile;
import com.arialyy.aria.core.FtpUrlEntity; import com.arialyy.aria.core.FtpUrlEntity;
import com.arialyy.aria.core.common.AbsFtpInfoThread;
import com.arialyy.aria.core.common.CompleteInfo; import com.arialyy.aria.core.common.CompleteInfo;
import com.arialyy.aria.core.common.OnFileInfoCallback; import com.arialyy.aria.core.common.OnFileInfoCallback;
import com.arialyy.aria.core.common.ftp.AbsFtpInfoThread;
import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.download.DownloadGroupEntity;
import com.arialyy.aria.core.download.DownloadGroupTaskEntity; import com.arialyy.aria.core.download.DownloadGroupTaskEntity;
@ -27,7 +28,6 @@ import com.arialyy.aria.core.inf.AbsTaskEntity;
import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.CommonUtil;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import org.apache.commons.net.ftp.FTPFile;
/** /**
* Created by Aria.Lao on 2017/7/25. * Created by Aria.Lao on 2017/7/25.
@ -61,7 +61,7 @@ class FtpDirInfoThread extends AbsFtpInfoThread<DownloadGroupEntity, DownloadGro
final FtpUrlEntity urlEntity = mTaskEntity.getUrlEntity().clone(); final FtpUrlEntity urlEntity = mTaskEntity.getUrlEntity().clone();
DownloadEntity entity = new DownloadEntity(); DownloadEntity entity = new DownloadEntity();
entity.setUrl( entity.setUrl(
urlEntity.protocol + "://" + urlEntity.hostName + ":" + urlEntity.port + "/" + remotePath); urlEntity.scheme + "://" + urlEntity.hostName + ":" + urlEntity.port + "/" + remotePath);
entity.setDownloadPath(mEntity.getDirPath() + "/" + remotePath); entity.setDownloadPath(mEntity.getDirPath() + "/" + remotePath);
int lastIndex = remotePath.lastIndexOf("/"); int lastIndex = remotePath.lastIndexOf("/");
String fileName = lastIndex < 0 ? CommonUtil.keyToHashKey(remotePath) String fileName = lastIndex < 0 ? CommonUtil.keyToHashKey(remotePath)

@ -15,22 +15,36 @@
*/ */
package com.arialyy.aria.core.download.downloader; package com.arialyy.aria.core.download.downloader;
import com.arialyy.aria.core.common.AbsFtpInfoThread; import aria.apache.commons.net.ftp.FTPFile;
import com.arialyy.aria.core.common.CompleteInfo; import com.arialyy.aria.core.common.CompleteInfo;
import com.arialyy.aria.core.common.OnFileInfoCallback; import com.arialyy.aria.core.common.OnFileInfoCallback;
import com.arialyy.aria.core.common.ftp.AbsFtpInfoThread;
import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.download.DownloadTaskEntity;
import com.arialyy.aria.exception.AriaIOException;
import com.arialyy.aria.util.CommonUtil;
/** /**
* Created by Aria.Lao on 2017/7/25. * Created by Aria.Lao on 2017/7/25.
* 获取ftp文件信息 * 获取ftp文件信息
*/ */
class FtpFileInfoThread extends AbsFtpInfoThread<DownloadEntity, DownloadTaskEntity> { class FtpFileInfoThread extends AbsFtpInfoThread<DownloadEntity, DownloadTaskEntity> {
private final String TAG = "FtpFileInfoThread";
FtpFileInfoThread(DownloadTaskEntity taskEntity, OnFileInfoCallback callback) { FtpFileInfoThread(DownloadTaskEntity taskEntity, OnFileInfoCallback callback) {
super(taskEntity, callback); super(taskEntity, callback);
} }
@Override protected void handleFile(String remotePath, FTPFile ftpFile) {
super.handleFile(remotePath, ftpFile);
if (!CommonUtil.checkSDMemorySpace(mEntity.getDownloadPath(), ftpFile.getSize())) {
mCallback.onFail(mEntity.getUrl(),
new AriaIOException(TAG,
String.format("获取ftp文件信息失败,内存空间不足, filePath: %s", mEntity.getDownloadPath())),
false);
}
}
@Override protected String setRemotePath() { @Override protected String setRemotePath() {
return mTaskEntity.getUrlEntity().remotePath; return mTaskEntity.getUrlEntity().remotePath;
} }

@ -15,13 +15,16 @@
*/ */
package com.arialyy.aria.core.download.downloader; package com.arialyy.aria.core.download.downloader;
import com.arialyy.aria.core.AriaManager; import aria.apache.commons.net.ftp.FTPClient;
import com.arialyy.aria.core.common.AbsFtpThreadTask; import aria.apache.commons.net.ftp.FTPReply;
import com.arialyy.aria.core.common.StateConstance; import com.arialyy.aria.core.common.StateConstance;
import com.arialyy.aria.core.common.SubThreadConfig; import com.arialyy.aria.core.common.SubThreadConfig;
import com.arialyy.aria.core.common.ftp.AbsFtpThreadTask;
import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.download.DownloadTaskEntity;
import com.arialyy.aria.core.inf.IDownloadListener; import com.arialyy.aria.core.inf.IDownloadListener;
import com.arialyy.aria.exception.AriaIOException;
import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.BufferedRandomAccessFile; import com.arialyy.aria.util.BufferedRandomAccessFile;
import java.io.FileOutputStream; import java.io.FileOutputStream;
@ -31,8 +34,6 @@ import java.nio.ByteBuffer;
import java.nio.channels.Channels; import java.nio.channels.Channels;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel; import java.nio.channels.ReadableByteChannel;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
/** /**
* Created by Aria.Lao on 2017/7/24. * Created by Aria.Lao on 2017/7/24.
@ -46,20 +47,19 @@ class FtpThreadTask extends AbsFtpThreadTask<DownloadEntity, DownloadTaskEntity>
FtpThreadTask(StateConstance constance, IDownloadListener listener, FtpThreadTask(StateConstance constance, IDownloadListener listener,
SubThreadConfig<DownloadTaskEntity> downloadInfo) { SubThreadConfig<DownloadTaskEntity> downloadInfo) {
super(constance, listener, downloadInfo); super(constance, listener, downloadInfo);
AriaManager manager = AriaManager.getInstance(AriaManager.APP); mConnectTimeOut = mAridManager.getDownloadConfig().getConnectTimeOut();
mConnectTimeOut = manager.getDownloadConfig().getConnectTimeOut(); mReadTimeOut = mAridManager.getDownloadConfig().getIOTimeOut();
mReadTimeOut = manager.getDownloadConfig().getIOTimeOut(); mBufSize = mAridManager.getDownloadConfig().getBuffSize();
mBufSize = manager.getDownloadConfig().getBuffSize(); isNotNetRetry = mAridManager.getDownloadConfig().isNotNetRetry();
isNotNetRetry = manager.getDownloadConfig().isNotNetRetry();
isOpenDynamicFile = STATE.TASK_RECORD.isOpenDynamicFile; isOpenDynamicFile = STATE.TASK_RECORD.isOpenDynamicFile;
isBlock = STATE.TASK_RECORD.isBlock; isBlock = STATE.TASK_RECORD.isBlock;
setMaxSpeed(manager.getDownloadConfig().getMaxSpeed());
} }
@Override public void run() { @Override public FtpThreadTask call() throws Exception {
super.call();
if (mConfig.THREAD_RECORD.isComplete) { if (mConfig.THREAD_RECORD.isComplete) {
handleComplete(); handleComplete();
return; return this;
} }
mChildCurrentLocation = mConfig.START_LOCATION; mChildCurrentLocation = mConfig.START_LOCATION;
FTPClient client = null; FTPClient client = null;
@ -70,7 +70,10 @@ class FtpThreadTask extends AbsFtpThreadTask<DownloadEntity, DownloadTaskEntity>
String.format("任务【%s】线程__%s__开始下载【开始位置 : %s,结束位置:%s】", mConfig.TEMP_FILE.getName(), String.format("任务【%s】线程__%s__开始下载【开始位置 : %s,结束位置:%s】", mConfig.TEMP_FILE.getName(),
mConfig.THREAD_ID, mConfig.START_LOCATION, mConfig.END_LOCATION)); mConfig.THREAD_ID, mConfig.START_LOCATION, mConfig.END_LOCATION));
client = createClient(); client = createClient();
if (client == null) return; if (client == null) {
fail(mChildCurrentLocation, new TaskException(TAG, "ftp client 创建失败"));
return this;
}
if (mConfig.START_LOCATION > 0) { if (mConfig.START_LOCATION > 0) {
client.setRestartOffset(mConfig.START_LOCATION); client.setRestartOffset(mConfig.START_LOCATION);
} }
@ -78,10 +81,10 @@ class FtpThreadTask extends AbsFtpThreadTask<DownloadEntity, DownloadTaskEntity>
int reply = client.getReplyCode(); int reply = client.getReplyCode();
if (!FTPReply.isPositivePreliminary(reply) && reply != FTPReply.COMMAND_OK) { if (!FTPReply.isPositivePreliminary(reply) && reply != FTPReply.COMMAND_OK) {
fail(mChildCurrentLocation, fail(mChildCurrentLocation,
String.format("获取文件信息错误,错误码为:%s,msg:%s", reply, client.getReplyString()), new AriaIOException(TAG,
null); String.format("获取文件信息错误,错误码为:%s,msg:%s", reply, client.getReplyString())));
client.disconnect(); client.disconnect();
return; return this;
} }
String remotePath = String remotePath =
new String(mTaskEntity.getUrlEntity().remotePath.getBytes(charSet), SERVER_CHARSET); new String(mTaskEntity.getUrlEntity().remotePath.getBytes(charSet), SERVER_CHARSET);
@ -90,10 +93,10 @@ class FtpThreadTask extends AbsFtpThreadTask<DownloadEntity, DownloadTaskEntity>
reply = client.getReplyCode(); reply = client.getReplyCode();
if (!FTPReply.isPositivePreliminary(reply)) { if (!FTPReply.isPositivePreliminary(reply)) {
fail(mChildCurrentLocation, fail(mChildCurrentLocation,
String.format("获取流失败,错误码为:%s,msg:%s", reply, client.getReplyString()), new AriaIOException(TAG,
null); String.format("获取流失败,错误码为:%s,msg:%s", reply, client.getReplyString())));
client.disconnect(); client.disconnect();
return; return this;
} }
if (isOpenDynamicFile) { if (isOpenDynamicFile) {
@ -103,9 +106,11 @@ class FtpThreadTask extends AbsFtpThreadTask<DownloadEntity, DownloadTaskEntity>
handleComplete(); handleComplete();
} }
} catch (IOException e) { } catch (IOException e) {
fail(mChildCurrentLocation, String.format("下载失败【%s】", mConfig.URL), e); fail(mChildCurrentLocation,
new AriaIOException(TAG, String.format("下载失败【%s】", mConfig.URL), e));
} catch (Exception e) { } catch (Exception e) {
fail(mChildCurrentLocation, "获取流失败", e); fail(mChildCurrentLocation,
new AriaIOException(TAG, String.format("下载失败【%s】", mConfig.URL), e));
} finally { } finally {
try { try {
if (is != null) { if (is != null) {
@ -118,6 +123,7 @@ class FtpThreadTask extends AbsFtpThreadTask<DownloadEntity, DownloadTaskEntity>
e.printStackTrace(); e.printStackTrace();
} }
} }
return this;
} }
/** /**
@ -135,9 +141,10 @@ class FtpThreadTask extends AbsFtpThreadTask<DownloadEntity, DownloadTaskEntity>
if (isBlock) { if (isBlock) {
boolean success = mergeFile(); boolean success = mergeFile();
if (!success) { if (!success) {
ALog.e(TAG, String.format("任务【%s】分块文件合并失败", mConfig.TEMP_FILE.getName())); //ALog.e(TAG, String.format("任务【%s】分块文件合并失败", mConfig.TEMP_FILE.getName()));
STATE.isRunning = false; STATE.isRunning = false;
mListener.onFail(false); mListener.onFail(false,
new TaskException(TAG, String.format("任务【%s】分块文件合并失败", mConfig.TEMP_FILE.getName())));
return; return;
} }
} }
@ -147,7 +154,8 @@ class FtpThreadTask extends AbsFtpThreadTask<DownloadEntity, DownloadTaskEntity>
} }
if (STATE.isFail()) { if (STATE.isFail()) {
STATE.isRunning = false; STATE.isRunning = false;
mListener.onFail(false); mListener.onFail(false,
new TaskException(TAG, String.format("任务【%s】下载失败", mConfig.TEMP_FILE.getName())));
} }
} }
@ -164,12 +172,12 @@ class FtpThreadTask extends AbsFtpThreadTask<DownloadEntity, DownloadTaskEntity>
foc = fos.getChannel(); foc = fos.getChannel();
fic = Channels.newChannel(is); fic = Channels.newChannel(is);
ByteBuffer bf = ByteBuffer.allocate(mBufSize); ByteBuffer bf = ByteBuffer.allocate(mBufSize);
while ((len = fic.read(bf)) != -1) { while (isLive() && (len = fic.read(bf)) != -1) {
if (isBreak()) { if (isBreak()) {
break; break;
} }
if (mSleepTime > 0) { if (mSpeedBandUtil != null) {
Thread.sleep(mSleepTime); mSpeedBandUtil.limitNextBytes(len);
} }
if (mChildCurrentLocation + len >= mConfig.END_LOCATION) { if (mChildCurrentLocation + len >= mConfig.END_LOCATION) {
len = (int) (mConfig.END_LOCATION - mChildCurrentLocation); len = (int) (mConfig.END_LOCATION - mChildCurrentLocation);
@ -186,10 +194,9 @@ class FtpThreadTask extends AbsFtpThreadTask<DownloadEntity, DownloadTaskEntity>
} }
} }
handleComplete(); handleComplete();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) { } catch (IOException e) {
fail(mChildCurrentLocation, String.format("下载失败【%s】", mConfig.URL), e); fail(mChildCurrentLocation,
new AriaIOException(TAG, String.format("下载失败【%s】", mConfig.URL), e));
} finally { } finally {
try { try {
if (fos != null) { if (fos != null) {
@ -217,11 +224,13 @@ class FtpThreadTask extends AbsFtpThreadTask<DownloadEntity, DownloadTaskEntity>
file.seek(mConfig.START_LOCATION); file.seek(mConfig.START_LOCATION);
byte[] buffer = new byte[mBufSize]; byte[] buffer = new byte[mBufSize];
int len; int len;
while ((len = is.read(buffer)) != -1) { while (isLive() && (len = is.read(buffer)) != -1) {
if (isBreak()) { if (isBreak()) {
break; break;
} }
if (mSleepTime > 0) Thread.sleep(mSleepTime); if (mSpeedBandUtil != null) {
mSpeedBandUtil.limitNextBytes(len);
}
if (mChildCurrentLocation + len >= mConfig.END_LOCATION) { if (mChildCurrentLocation + len >= mConfig.END_LOCATION) {
len = (int) (mConfig.END_LOCATION - mChildCurrentLocation); len = (int) (mConfig.END_LOCATION - mChildCurrentLocation);
file.write(buffer, 0, len); file.write(buffer, 0, len);
@ -233,9 +242,8 @@ class FtpThreadTask extends AbsFtpThreadTask<DownloadEntity, DownloadTaskEntity>
} }
} }
} catch (IOException e) { } catch (IOException e) {
fail(mChildCurrentLocation, String.format("下载失败【%s】", mConfig.URL), e); fail(mChildCurrentLocation,
} catch (InterruptedException e) { new AriaIOException(TAG, String.format("下载失败【%s】", mConfig.URL), e));
e.printStackTrace();
} finally { } finally {
try { try {
if (file != null) { if (file != null) {
@ -246,4 +254,8 @@ class FtpThreadTask extends AbsFtpThreadTask<DownloadEntity, DownloadTaskEntity>
} }
} }
} }
@Override public int getMaxSpeed() {
return mAridManager.getDownloadConfig().getMaxSpeed();
}
} }

@ -15,12 +15,18 @@
*/ */
package com.arialyy.aria.core.download.downloader; package com.arialyy.aria.core.download.downloader;
import android.os.Process;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log;
import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.core.common.CompleteInfo; import com.arialyy.aria.core.common.CompleteInfo;
import com.arialyy.aria.core.common.OnFileInfoCallback; import com.arialyy.aria.core.common.OnFileInfoCallback;
import com.arialyy.aria.core.common.RequestEnum;
import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.download.DownloadTaskEntity;
import com.arialyy.aria.exception.AriaIOException;
import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CheckUtil; import com.arialyy.aria.util.CheckUtil;
import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.CommonUtil;
@ -28,9 +34,18 @@ import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.HttpCookie;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/** /**
* 下载文件信息获取 * 下载文件信息获取
@ -51,6 +66,7 @@ class HttpFileInfoThread implements Runnable {
} }
@Override public void run() { @Override public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
HttpURLConnection conn = null; HttpURLConnection conn = null;
try { try {
URL url = new URL(CommonUtil.convertUrl(mEntity.getUrl())); URL url = new URL(CommonUtil.convertUrl(mEntity.getUrl()));
@ -62,12 +78,9 @@ class HttpFileInfoThread implements Runnable {
conn.connect(); conn.connect();
handleConnect(conn); handleConnect(conn);
} catch (IOException e) { } catch (IOException e) {
failDownload("下载失败【downloadUrl:" failDownload(new AriaIOException(TAG,
+ mEntity.getUrl() String.format("下载失败,filePath: %s, url: %s", mEntity.getDownloadPath(), mEntity.getUrl())),
+ "】\n【filePath:" true);
+ mEntity.getDownloadPath()
+ "】\n"
+ ALog.getExceptionString(e), true);
} finally { } finally {
if (conn != null) { if (conn != null) {
conn.disconnect(); conn.disconnect();
@ -76,6 +89,23 @@ class HttpFileInfoThread implements Runnable {
} }
private void handleConnect(HttpURLConnection conn) throws IOException { private void handleConnect(HttpURLConnection conn) throws IOException {
if (mTaskEntity.getRequestEnum() == RequestEnum.POST) {
Map<String, String> params = mTaskEntity.getParams();
if (params != null) {
OutputStreamWriter dos = new OutputStreamWriter(conn.getOutputStream());
Set<String> keys = params.keySet();
StringBuilder sb = new StringBuilder();
for (String key : keys) {
sb.append(key).append("=").append(URLEncoder.encode(params.get(key))).append("&");
}
String url = sb.toString();
url = url.substring(0, url.length() - 1);
dos.write(url);
dos.flush();
dos.close();
}
}
long len = conn.getContentLength(); long len = conn.getContentLength();
if (len < 0) { if (len < 0) {
String temp = conn.getHeaderField("Content-Length"); String temp = conn.getHeaderField("Content-Length");
@ -92,6 +122,14 @@ class HttpFileInfoThread implements Runnable {
} }
} }
} }
if (!CommonUtil.checkSDMemorySpace(mEntity.getDownloadPath(), len)) {
failDownload(new TaskException(TAG,
String.format("下载失败,内存空间不足;filePath: %s, url: %s", mEntity.getDownloadPath(),
mEntity.getUrl())), false);
return;
}
int code = conn.getResponseCode(); int code = conn.getResponseCode();
boolean end = false; boolean end = false;
if (TextUtils.isEmpty(mEntity.getMd5Code())) { if (TextUtils.isEmpty(mEntity.getMd5Code())) {
@ -104,7 +142,7 @@ class HttpFileInfoThread implements Runnable {
if (!TextUtils.isEmpty(str) && str.equals("chunked")) { if (!TextUtils.isEmpty(str) && str.equals("chunked")) {
isChunked = true; isChunked = true;
} }
//Map<String, List<String>> headers = conn.getHeaderFields(); Map<String, List<String>> headers = conn.getHeaderFields();
String disposition = conn.getHeaderField("Content-Disposition"); String disposition = conn.getHeaderField("Content-Disposition");
if (mTaskEntity.isUseServerFileName() && !TextUtils.isEmpty(disposition)) { if (mTaskEntity.isUseServerFileName() && !TextUtils.isEmpty(disposition)) {
mEntity.setDisposition(CommonUtil.encryptBASE64(disposition)); mEntity.setDisposition(CommonUtil.encryptBASE64(disposition));
@ -114,19 +152,33 @@ class HttpFileInfoThread implements Runnable {
if (info.startsWith("filename") && info.contains("=")) { if (info.startsWith("filename") && info.contains("=")) {
String[] temp = info.split("="); String[] temp = info.split("=");
if (temp.length > 1) { if (temp.length > 1) {
String newName = URLDecoder.decode(temp[1], "utf-8"); String newName = URLDecoder.decode(temp[1], "utf-8").replaceAll("\"", "");
mEntity.setServerFileName(newName); mEntity.setServerFileName(newName);
fileRename(newName); renameFile(newName);
break; break;
} }
} }
} }
} }
} }
CookieManager msCookieManager = new CookieManager();
List<String> cookiesHeader = headers.get("Set-Cookie");
if (cookiesHeader != null) {
for (String cookie : cookiesHeader) {
msCookieManager.getCookieStore().add(null, HttpCookie.parse(cookie).get(0));
}
mTaskEntity.setCookieManager(msCookieManager);
}
mTaskEntity.setCode(code); mTaskEntity.setCode(code);
if (code == HttpURLConnection.HTTP_PARTIAL) { if (code == HttpURLConnection.HTTP_PARTIAL) {
if (!checkLen(len) && !isChunked) { if (!checkLen(len) && !isChunked) {
if (len < 0) {
failDownload(
new AriaIOException(TAG, String.format("任务下载失败,文件长度小于0, url: %s", mEntity.getUrl())),
false);
}
return; return;
} }
mEntity.setFileSize(len); mEntity.setFileSize(len);
@ -149,6 +201,12 @@ class HttpFileInfoThread implements Runnable {
handleUrlReTurn(conn, CommonUtil.getWindowReplaceUrl(sb.toString())); handleUrlReTurn(conn, CommonUtil.getWindowReplaceUrl(sb.toString()));
return; return;
} else if (!checkLen(len) && !isChunked) { } else if (!checkLen(len) && !isChunked) {
if (len < 0) {
failDownload(
new AriaIOException(TAG, String.format("任务下载失败,文件长度小于0, url: %s", mEntity.getUrl())),
false);
}
ALog.d(TAG, "len < 0");
return; return;
} }
mEntity.setFileSize(len); mEntity.setFileSize(len);
@ -156,14 +214,18 @@ class HttpFileInfoThread implements Runnable {
mTaskEntity.setSupportBP(false); mTaskEntity.setSupportBP(false);
end = true; end = true;
} else if (code == HttpURLConnection.HTTP_NOT_FOUND) { } else if (code == HttpURLConnection.HTTP_NOT_FOUND) {
failDownload("任务【" + mEntity.getUrl() + "】下载失败,错误码:404", true); failDownload(new AriaIOException(TAG,
String.format("任务下载失败,errorCode:404, url: %s", mEntity.getUrl())), true);
} else if (code == HttpURLConnection.HTTP_MOVED_TEMP } else if (code == HttpURLConnection.HTTP_MOVED_TEMP
|| code == HttpURLConnection.HTTP_MOVED_PERM || code == HttpURLConnection.HTTP_MOVED_PERM
|| code == HttpURLConnection.HTTP_SEE_OTHER || code == HttpURLConnection.HTTP_SEE_OTHER
|| code == HttpURLConnection.HTTP_CREATED // 201 跳转
|| code == 307) { || code == 307) {
handleUrlReTurn(conn, conn.getHeaderField("Location")); handleUrlReTurn(conn, conn.getHeaderField("Location"));
} else { } else {
failDownload("任务【" + mEntity.getUrl() + "】下载失败,错误码:" + code, true); failDownload(new AriaIOException(TAG,
String.format("任务下载失败,errorCode:%s, errorMsg: %s, url: %s", code,
conn.getResponseMessage(), mEntity.getUrl())), true);
} }
if (end) { if (end) {
mTaskEntity.setChunked(isChunked); mTaskEntity.setChunked(isChunked);
@ -178,7 +240,7 @@ class HttpFileInfoThread implements Runnable {
/** /**
* 重命名文件 * 重命名文件
*/ */
private void fileRename(String newName) { private void renameFile(String newName) {
if (TextUtils.isEmpty(newName)) { if (TextUtils.isEmpty(newName)) {
ALog.w(TAG, "重命名失败【服务器返回的文件名为空】"); ALog.w(TAG, "重命名失败【服务器返回的文件名为空】");
return; return;
@ -201,19 +263,20 @@ class HttpFileInfoThread implements Runnable {
if (TextUtils.isEmpty(newUrl) || newUrl.equalsIgnoreCase("null") || !newUrl.startsWith( if (TextUtils.isEmpty(newUrl) || newUrl.equalsIgnoreCase("null") || !newUrl.startsWith(
"http")) { "http")) {
if (onFileInfoCallback != null) { if (onFileInfoCallback != null) {
onFileInfoCallback.onFail(mEntity.getUrl(), "获取重定向链接失败", false); onFileInfoCallback.onFail(mEntity.getUrl(), new TaskException(TAG, "获取重定向链接失败"), false);
} }
return; return;
} }
if (!CheckUtil.checkUrl(newUrl)) { if (!CheckUtil.checkUrl(newUrl)) {
failDownload("下载失败,重定向url错误", false); failDownload(new TaskException(TAG, "下载失败,重定向url错误"), false);
return; return;
} }
mTaskEntity.setRedirectUrl(newUrl); mTaskEntity.setRedirectUrl(newUrl);
mEntity.setRedirect(true); mEntity.setRedirect(true);
mEntity.setRedirectUrl(newUrl); mEntity.setRedirectUrl(newUrl);
String cookies = conn.getHeaderField("Set-Cookie"); String cookies = conn.getHeaderField("Set-Cookie");
conn = (HttpURLConnection) new URL(newUrl).openConnection(); URL url = new URL(CommonUtil.convertUrl(newUrl));
conn = ConnectionHelp.handleConnection(url, mTaskEntity);
conn = ConnectionHelp.setConnectParam(mTaskEntity, conn); conn = ConnectionHelp.setConnectParam(mTaskEntity, conn);
conn.setRequestProperty("Cookie", cookies); conn.setRequestProperty("Cookie", cookies);
conn.setRequestProperty("Range", "bytes=" + 0 + "-"); conn.setRequestProperty("Range", "bytes=" + 0 + "-");
@ -233,17 +296,12 @@ class HttpFileInfoThread implements Runnable {
if (len != mEntity.getFileSize()) { if (len != mEntity.getFileSize()) {
mTaskEntity.setNewTask(true); mTaskEntity.setNewTask(true);
} }
if (len < 0) {
failDownload("任务【" + mEntity.getUrl() + "】下载失败,文件长度小于0", true);
return false;
}
return true; return true;
} }
private void failDownload(String errorMsg, boolean needRetry) { private void failDownload(BaseException e, boolean needRetry) {
ALog.e(TAG, errorMsg);
if (onFileInfoCallback != null) { if (onFileInfoCallback != null) {
onFileInfoCallback.onFail(mEntity.getUrl(), errorMsg, needRetry); onFileInfoCallback.onFail(mEntity.getUrl(), e, needRetry);
} }
} }
} }

@ -15,13 +15,15 @@
*/ */
package com.arialyy.aria.core.download.downloader; package com.arialyy.aria.core.download.downloader;
import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.core.common.AbsThreadTask; 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.StateConstance;
import com.arialyy.aria.core.common.SubThreadConfig; import com.arialyy.aria.core.common.SubThreadConfig;
import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.download.DownloadTaskEntity;
import com.arialyy.aria.core.inf.IDownloadListener; import com.arialyy.aria.core.inf.IDownloadListener;
import com.arialyy.aria.exception.AriaIOException;
import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.BufferedRandomAccessFile; import com.arialyy.aria.util.BufferedRandomAccessFile;
import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.CommonUtil;
@ -29,13 +31,17 @@ import java.io.BufferedInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.net.URLEncoder;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.Channels; import java.nio.channels.Channels;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel; import java.nio.channels.ReadableByteChannel;
import java.util.Map;
import java.util.Set;
/** /**
* Created by lyy on 2017/1/18. * Created by lyy on 2017/1/18.
@ -49,20 +55,19 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DownloadTaskEnt
HttpThreadTask(StateConstance constance, IDownloadListener listener, HttpThreadTask(StateConstance constance, IDownloadListener listener,
SubThreadConfig<DownloadTaskEntity> downloadInfo) { SubThreadConfig<DownloadTaskEntity> downloadInfo) {
super(constance, listener, downloadInfo); super(constance, listener, downloadInfo);
AriaManager manager = AriaManager.getInstance(AriaManager.APP); mConnectTimeOut = mAridManager.getDownloadConfig().getConnectTimeOut();
mConnectTimeOut = manager.getDownloadConfig().getConnectTimeOut(); mReadTimeOut = mAridManager.getDownloadConfig().getIOTimeOut();
mReadTimeOut = manager.getDownloadConfig().getIOTimeOut(); mBufSize = mAridManager.getDownloadConfig().getBuffSize();
mBufSize = manager.getDownloadConfig().getBuffSize(); isNotNetRetry = mAridManager.getDownloadConfig().isNotNetRetry();
isNotNetRetry = manager.getDownloadConfig().isNotNetRetry();
isOpenDynamicFile = STATE.TASK_RECORD.isOpenDynamicFile; isOpenDynamicFile = STATE.TASK_RECORD.isOpenDynamicFile;
isBlock = STATE.TASK_RECORD.isBlock; isBlock = STATE.TASK_RECORD.isBlock;
setMaxSpeed(manager.getDownloadConfig().getMaxSpeed());
} }
@Override public void run() { @Override public HttpThreadTask call() throws Exception {
if (mConfig.THREAD_RECORD.isComplete) { super.call();
if (getThreadRecord().isComplete) {
handleComplete(); handleComplete();
return; return this;
} }
HttpURLConnection conn = null; HttpURLConnection conn = null;
BufferedInputStream is = null; BufferedInputStream is = null;
@ -76,7 +81,6 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DownloadTaskEnt
ALog.d(TAG, ALog.d(TAG,
String.format("任务【%s】线程__%s__开始下载【开始位置 : %s,结束位置:%s】", mConfig.TEMP_FILE.getName(), String.format("任务【%s】线程__%s__开始下载【开始位置 : %s,结束位置:%s】", mConfig.TEMP_FILE.getName(),
mConfig.THREAD_ID, mConfig.START_LOCATION, mConfig.END_LOCATION)); mConfig.THREAD_ID, mConfig.START_LOCATION, mConfig.END_LOCATION));
//在头里面请求下载开始位置和结束位置
conn.setRequestProperty("Range", conn.setRequestProperty("Range",
String.format("bytes=%s-%s", mConfig.START_LOCATION, (mConfig.END_LOCATION - 1))); String.format("bytes=%s-%s", mConfig.START_LOCATION, (mConfig.END_LOCATION - 1)));
} else { } else {
@ -86,6 +90,23 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DownloadTaskEnt
conn.setConnectTimeout(mConnectTimeOut); conn.setConnectTimeout(mConnectTimeOut);
conn.setReadTimeout(mReadTimeOut); //设置读取流的等待时间,必须设置该参数 conn.setReadTimeout(mReadTimeOut); //设置读取流的等待时间,必须设置该参数
conn.connect(); conn.connect();
if (mTaskEntity.getRequestEnum() == RequestEnum.POST) {
Map<String, String> params = mTaskEntity.getParams();
if (params != null) {
OutputStreamWriter dos = new OutputStreamWriter(conn.getOutputStream());
Set<String> keys = params.keySet();
StringBuilder sb = new StringBuilder();
for (String key : keys) {
sb.append(key).append("=").append(URLEncoder.encode(params.get(key))).append("&");
}
String paramStr = sb.toString();
paramStr = paramStr.substring(0, paramStr.length() - 1);
dos.write(paramStr);
dos.flush();
dos.close();
}
}
is = new BufferedInputStream(ConnectionHelp.convertInputStream(conn)); is = new BufferedInputStream(ConnectionHelp.convertInputStream(conn));
if (isOpenDynamicFile) { if (isOpenDynamicFile) {
readDynamicFile(is); readDynamicFile(is);
@ -102,11 +123,17 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DownloadTaskEnt
handleComplete(); handleComplete();
} }
} catch (MalformedURLException e) { } catch (MalformedURLException e) {
fail(mChildCurrentLocation, "下载链接异常", e); fail(mChildCurrentLocation, new TaskException(TAG,
String.format("任务【%s】下载失败,filePath: %s, url: %s", mConfig.TEMP_FILE.getName(),
mEntity.getDownloadPath(), mEntity.getUrl()), e));
} catch (IOException e) { } catch (IOException e) {
fail(mChildCurrentLocation, String.format("下载失败【%s】", mConfig.URL), e); fail(mChildCurrentLocation, new TaskException(TAG,
String.format("任务【%s】下载失败,filePath: %s, url: %s", mConfig.TEMP_FILE.getName(),
mEntity.getDownloadPath(), mEntity.getUrl()), e));
} catch (Exception e) { } catch (Exception e) {
fail(mChildCurrentLocation, "获取流失败", e); fail(mChildCurrentLocation, new TaskException(TAG,
String.format("任务【%s】下载失败,filePath: %s, url: %s", mConfig.TEMP_FILE.getName(),
mEntity.getDownloadPath(), mEntity.getUrl()), e));
} finally { } finally {
try { try {
if (file != null) { if (file != null) {
@ -122,6 +149,7 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DownloadTaskEnt
e.printStackTrace(); e.printStackTrace();
} }
} }
return this;
} }
/** /**
@ -137,26 +165,38 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DownloadTaskEnt
foc = fos.getChannel(); foc = fos.getChannel();
fic = Channels.newChannel(is); fic = Channels.newChannel(is);
ByteBuffer bf = ByteBuffer.allocate(mBufSize); ByteBuffer bf = ByteBuffer.allocate(mBufSize);
while ((len = fic.read(bf)) != -1) { //如果要通过 Future 的 cancel 方法取消正在运行的任务,那么该任务必定是可以 对线程中断做出响应 的任务。
while (isLive() && (len = fic.read(bf)) != -1) {
if (isBreak()) { if (isBreak()) {
break; break;
} }
if (mSleepTime > 0) { if (mSpeedBandUtil != null) {
Thread.sleep(mSleepTime); mSpeedBandUtil.limitNextBytes(len);
} }
if (mChildCurrentLocation + len >= mConfig.END_LOCATION) {
len = (int) (mConfig.END_LOCATION - mChildCurrentLocation);
bf.flip();
fos.write(bf.array(), 0, len);
bf.compact();
progress(len);
break;
} else {
bf.flip(); bf.flip();
foc.write(bf); foc.write(bf);
bf.compact(); bf.compact();
progress(len); progress(len);
} }
}
handleComplete(); handleComplete();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) { } catch (IOException e) {
fail(mChildCurrentLocation, String.format("下载失败【%s】", mConfig.URL), e); fail(mChildCurrentLocation, new AriaIOException(TAG,
String.format("文件下载失败,savePath: %s, url: %s", mEntity.getDownloadPath(), mConfig.URL),
e));
} finally { } finally {
try { try {
if (fos != null) { if (fos != null) {
fos.flush();
fos.close(); fos.close();
} }
if (foc != null) { if (foc != null) {
@ -177,7 +217,7 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DownloadTaskEnt
* @deprecated 暂时先这样处理无chun * @deprecated 暂时先这样处理无chun
*/ */
private void readChunk(InputStream is, BufferedRandomAccessFile file) private void readChunk(InputStream is, BufferedRandomAccessFile file)
throws IOException, InterruptedException { throws IOException {
readNormal(is, file); readNormal(is, file);
} }
@ -185,15 +225,15 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DownloadTaskEnt
* 读取普通的文件流 * 读取普通的文件流
*/ */
private void readNormal(InputStream is, BufferedRandomAccessFile file) private void readNormal(InputStream is, BufferedRandomAccessFile file)
throws IOException, InterruptedException { throws IOException {
byte[] buffer = new byte[mBufSize]; byte[] buffer = new byte[mBufSize];
int len; int len;
while ((len = is.read(buffer)) != -1) { while (isLive() && (len = is.read(buffer)) != -1) {
if (isBreak()) { if (isBreak()) {
break; break;
} }
if (mSleepTime > 0) { if (mSpeedBandUtil != null) {
Thread.sleep(mSleepTime); mSpeedBandUtil.limitNextBytes(len);
} }
file.write(buffer, 0, len); file.write(buffer, 0, len);
progress(len); progress(len);
@ -219,9 +259,9 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DownloadTaskEnt
if (isBlock) { if (isBlock) {
boolean success = mergeFile(); boolean success = mergeFile();
if (!success) { if (!success) {
ALog.e(TAG, String.format("任务【%s】分块文件合并失败", mConfig.TEMP_FILE.getName()));
STATE.isRunning = false; STATE.isRunning = false;
mListener.onFail(false); mListener.onFail(false, new TaskException(TAG,
String.format("任务【%s】分块文件合并失败", mConfig.TEMP_FILE.getName())));
return; return;
} }
} }
@ -231,7 +271,10 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DownloadTaskEnt
} }
if (STATE.isFail()) { if (STATE.isFail()) {
STATE.isRunning = false; STATE.isRunning = false;
mListener.onFail(false); mListener.onFail(false,
new TaskException(TAG,
String.format("任务【%s】下载失败,filePath: %s, url: %s", mConfig.TEMP_FILE.getName(),
mEntity.getDownloadPath(), mEntity.getUrl())));
} }
} else { } else {
ALog.i(TAG, "任务下载完成"); ALog.i(TAG, "任务下载完成");
@ -242,4 +285,8 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DownloadTaskEnt
STATE.FAIL_NUM++; STATE.FAIL_NUM++;
} }
} }
@Override public int getMaxSpeed() {
return mAridManager.getDownloadConfig().getMaxSpeed();
}
} }

@ -17,6 +17,7 @@ package com.arialyy.aria.core.download.downloader;
import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.inf.IDownloadListener; import com.arialyy.aria.core.inf.IDownloadListener;
import com.arialyy.aria.exception.BaseException;
/** /**
* Created by Aria.Lao on 2017/7/20. * Created by Aria.Lao on 2017/7/20.
@ -54,7 +55,7 @@ public interface IDownloadGroupListener extends IDownloadListener {
/** /**
* 子任务下载失败 * 子任务下载失败
*/ */
void onSubFail(DownloadEntity subEntity); void onSubFail(DownloadEntity subEntity, BaseException e);
/** /**
* 子任务取消下载 * 子任务取消下载

@ -23,7 +23,8 @@ import com.arialyy.aria.core.download.DownloadTaskEntity;
import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.inf.AbsTaskEntity;
import com.arialyy.aria.core.inf.IDownloadListener; import com.arialyy.aria.core.inf.IDownloadListener;
import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.util.ErrorHelp; import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.exception.TaskException;
/** /**
* Created by lyy on 2015/8/25. * Created by lyy on 2015/8/25.
@ -77,7 +78,7 @@ public class SimpleDownloadUtil implements IUtil, Runnable {
* 多线程断点续传下载文件开始下载 * 多线程断点续传下载文件开始下载
*/ */
@Override public void start() { @Override public void start() {
if (isStop || isCancel){ if (isStop || isCancel) {
return; return;
} }
new Thread(this).start(); new Thread(this).start();
@ -87,16 +88,15 @@ public class SimpleDownloadUtil implements IUtil, Runnable {
start(); start();
} }
public void setMaxSpeed(double maxSpeed) { @Override public void setMaxSpeed(int speed) {
mDownloader.setMaxSpeed(maxSpeed); mDownloader.setMaxSpeed(speed);
} }
private void failDownload(String msg, boolean needRetry) { private void failDownload(BaseException e, boolean needRetry) {
if (isStop || isCancel) { if (isStop || isCancel) {
return; return;
} }
mListener.onFail(needRetry); mListener.onFail(needRetry, e);
ErrorHelp.saveError(TAG, msg, "");
} }
@Override public void run() { @Override public void run() {
@ -125,8 +125,9 @@ public class SimpleDownloadUtil implements IUtil, Runnable {
mDownloader.start(); mDownloader.start();
} }
@Override public void onFail(String url, String errorMsg, boolean needRetry) { @Override public void onFail(String url, BaseException e, boolean needRetry) {
failDownload(errorMsg, needRetry); failDownload(e, needRetry);
mDownloader.closeTimer();
} }
}); });
case AbsTaskEntity.D_HTTP: case AbsTaskEntity.D_HTTP:
@ -135,8 +136,9 @@ public class SimpleDownloadUtil implements IUtil, Runnable {
mDownloader.start(); mDownloader.start();
} }
@Override public void onFail(String url, String errorMsg, boolean needRetry) { @Override public void onFail(String url, BaseException e, boolean needRetry) {
failDownload(errorMsg, needRetry); failDownload(e, needRetry);
mDownloader.closeTimer();
} }
}); });
} }

@ -19,11 +19,12 @@ import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.orm.DbEntity;
import com.arialyy.aria.orm.annotation.Ignore; import com.arialyy.aria.orm.annotation.Ignore;
import java.io.Serializable;
/** /**
* Created by AriaL on 2017/6/29. * Created by AriaL on 2017/6/29.
*/ */
public abstract class AbsEntity extends DbEntity implements IEntity, Parcelable { public abstract class AbsEntity extends DbEntity implements IEntity, Parcelable, Serializable {
/** /**
* 速度 * 速度
*/ */

@ -21,10 +21,8 @@ import com.arialyy.aria.core.download.downloader.AbsGroupUtil;
* Created by AriaL on 2017/6/29. * Created by AriaL on 2017/6/29.
* 任务组任务抽象类 * 任务组任务抽象类
*/ */
public abstract class AbsGroupTask<TASK_ENTITY extends AbsGroupTaskEntity> public abstract class AbsGroupTask<ENTITY extends AbsEntity, TASK_ENTITY extends AbsGroupTaskEntity>
extends AbsTask<TASK_ENTITY> { extends AbsTask<ENTITY, TASK_ENTITY> {
protected AbsGroupUtil mUtil;
@Override public String getKey() { @Override public String getKey() {
return mTaskEntity.getEntity().getKey(); return mTaskEntity.getEntity().getKey();
@ -37,7 +35,7 @@ public abstract class AbsGroupTask<TASK_ENTITY extends AbsGroupTaskEntity>
*/ */
public void startSubTask(String url) { public void startSubTask(String url) {
if (mUtil != null) { if (mUtil != null) {
mUtil.startSubTask(url); ((AbsGroupUtil) mUtil).startSubTask(url);
} }
} }
@ -48,7 +46,7 @@ public abstract class AbsGroupTask<TASK_ENTITY extends AbsGroupTaskEntity>
*/ */
public void stopSubTask(String url) { public void stopSubTask(String url) {
if (mUtil != null) { if (mUtil != null) {
mUtil.stopSubTask(url); ((AbsGroupUtil) mUtil).stopSubTask(url);
} }
} }
@ -59,7 +57,7 @@ public abstract class AbsGroupTask<TASK_ENTITY extends AbsGroupTaskEntity>
*/ */
public void cancelSubTask(String url) { public void cancelSubTask(String url) {
if (mUtil != null) { if (mUtil != null) {
mUtil.cancelSubTask(url); ((AbsGroupUtil) mUtil).cancelSubTask(url);
} }
} }
} }

@ -18,15 +18,8 @@ package com.arialyy.aria.core.inf;
/** /**
* Created by lyy on 2017/6/3. * Created by lyy on 2017/6/3.
*/ */
public abstract class AbsNormalTask<TASK_ENTITY extends AbsNormalTaskEntity> public abstract class AbsNormalTask<ENTITY extends AbsEntity, TASK_ENTITY extends AbsNormalTaskEntity>
extends AbsTask<TASK_ENTITY> { extends AbsTask<ENTITY, TASK_ENTITY> {
/**
* 暂停任务并让任务处于等待状态
*/
public void stopAndWait() {
}
/** /**
* 最高优先级命令最高优先级命令有以下属性 * 最高优先级命令最高优先级命令有以下属性

@ -21,10 +21,14 @@ import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.core.command.ICmd; import com.arialyy.aria.core.command.ICmd;
import com.arialyy.aria.core.command.normal.CancelCmd; import com.arialyy.aria.core.command.normal.CancelCmd;
import com.arialyy.aria.core.command.normal.NormalCmdFactory; import com.arialyy.aria.core.command.normal.NormalCmdFactory;
import com.arialyy.aria.core.common.TaskRecord;
import com.arialyy.aria.core.common.http.PostDelegate;
import com.arialyy.aria.core.download.DownloadGroupEntity;
import com.arialyy.aria.core.download.DownloadGroupTaskEntity; import com.arialyy.aria.core.download.DownloadGroupTaskEntity;
import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.download.DownloadTaskEntity;
import com.arialyy.aria.core.manager.TEManager; import com.arialyy.aria.core.manager.TEManager;
import com.arialyy.aria.core.upload.UploadTaskEntity; import com.arialyy.aria.core.upload.UploadTaskEntity;
import com.arialyy.aria.orm.DbEntity;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.CommonUtil;
import java.util.ArrayList; import java.util.ArrayList;
@ -34,8 +38,8 @@ import java.util.List;
* Created by AriaL on 2017/7/3. * Created by AriaL on 2017/7/3.
*/ */
public abstract class AbsTarget<TARGET extends AbsTarget, ENTITY extends AbsEntity, TASK_ENTITY extends AbsTaskEntity> public abstract class AbsTarget<TARGET extends AbsTarget, ENTITY extends AbsEntity, TASK_ENTITY extends AbsTaskEntity>
implements ITarget<TARGET> { implements ITarget {
protected String TAG ; protected String TAG;
protected ENTITY mEntity; protected ENTITY mEntity;
protected TASK_ENTITY mTaskEntity; protected TASK_ENTITY mTaskEntity;
protected String mTargetName; protected String mTargetName;
@ -63,18 +67,34 @@ public abstract class AbsTarget<TARGET extends AbsTarget, ENTITY extends AbsEnti
if (isRunning()) { if (isRunning()) {
ALog.d("AbsTarget", "任务正在下载,即将删除任务"); ALog.d("AbsTarget", "任务正在下载,即将删除任务");
cancel(); cancel();
} else {
if (mEntity instanceof AbsNormalEntity) {
TaskRecord record =
DbEntity.findFirst(TaskRecord.class, "TaskRecord.filePath=?", mTaskEntity.getKey());
if (record != null) {
CommonUtil.delTaskRecord(record, mTaskEntity.isRemoveFile(), (AbsNormalEntity) mEntity);
} else { } else {
mEntity.deleteData(); mEntity.deleteData();
}
} else if (mEntity instanceof DownloadGroupEntity) {
CommonUtil.delGroupTaskRecord(mTaskEntity.isRemoveFile(), ((DownloadGroupEntity) mEntity));
}
TEManager.getInstance().removeTEntity(mEntity.getKey()); TEManager.getInstance().removeTEntity(mEntity.getKey());
} }
} }
/**
* 获取任务实体
*/
public TASK_ENTITY getTaskEntity() {
return mTaskEntity;
}
/** /**
* 获取任务进度如果任务存在则返回当前进度 * 获取任务进度如果任务存在则返回当前进度
* *
* @return 该任务进度 * @return 该任务进度
*/ */
@Override
public long getCurrentProgress() { public long getCurrentProgress() {
return mEntity == null ? -1 : mEntity.getCurrentProgress(); return mEntity == null ? -1 : mEntity.getCurrentProgress();
} }
@ -84,7 +104,7 @@ public abstract class AbsTarget<TARGET extends AbsTarget, ENTITY extends AbsEnti
* *
* @return 文件大小 * @return 文件大小
*/ */
@Override public long getSize() { public long getSize() {
return mEntity == null ? 0 : mEntity.getFileSize(); return mEntity == null ? 0 : mEntity.getFileSize();
} }
@ -93,7 +113,7 @@ public abstract class AbsTarget<TARGET extends AbsTarget, ENTITY extends AbsEnti
* *
* @return 文件大小{@code xxx mb} * @return 文件大小{@code xxx mb}
*/ */
@Override public String getConvertSize() { public String getConvertSize() {
return mEntity == null ? "0b" : CommonUtil.formatFileSize(mEntity.getFileSize()); return mEntity == null ? "0b" : CommonUtil.formatFileSize(mEntity.getFileSize());
} }
@ -129,7 +149,6 @@ public abstract class AbsTarget<TARGET extends AbsTarget, ENTITY extends AbsEnti
* *
* @return {@link IEntity} * @return {@link IEntity}
*/ */
@Override
public int getTaskState() { public int getTaskState() {
return mEntity.getState(); return mEntity.getState();
} }
@ -139,7 +158,7 @@ public abstract class AbsTarget<TARGET extends AbsTarget, ENTITY extends AbsEnti
* *
* @return 返回任务进度 * @return 返回任务进度
*/ */
@Override public int getPercent() { public int getPercent() {
if (mEntity == null) { if (mEntity == null) {
ALog.e("AbsTarget", "下载管理器中没有该任务"); ALog.e("AbsTarget", "下载管理器中没有该任务");
return 0; return 0;
@ -178,14 +197,28 @@ public abstract class AbsTarget<TARGET extends AbsTarget, ENTITY extends AbsEnti
} }
} }
/**
* 任务是否在执行
*
* @return {@code true} 任务正在执行
*/
public abstract boolean isRunning();
/**
* 任务是否存在
*
* @return {@code true} 任务存在
*/
public abstract boolean taskExists();
/** /**
* 开始任务 * 开始任务
*/ */
@Override public void start() { @Override public void start() {
if (checkEntity()) { if (checkEntity()) {
AriaManager.getInstance(AriaManager.APP) AriaManager.getInstance(AriaManager.APP)
.setCmd(CommonUtil.createNormalCmd(mTargetName, mTaskEntity, NormalCmdFactory.TASK_START, .setCmd(
checkTaskType())) CommonUtil.createNormalCmd(mTaskEntity, NormalCmdFactory.TASK_START, checkTaskType()))
.exe(); .exe();
} }
} }
@ -204,8 +237,8 @@ public abstract class AbsTarget<TARGET extends AbsTarget, ENTITY extends AbsEnti
@Override public void stop() { @Override public void stop() {
if (checkEntity()) { if (checkEntity()) {
AriaManager.getInstance(AriaManager.APP) AriaManager.getInstance(AriaManager.APP)
.setCmd(CommonUtil.createNormalCmd(mTargetName, mTaskEntity, NormalCmdFactory.TASK_STOP, .setCmd(
checkTaskType())) CommonUtil.createNormalCmd(mTaskEntity, NormalCmdFactory.TASK_STOP, checkTaskType()))
.exe(); .exe();
} }
} }
@ -216,8 +249,8 @@ public abstract class AbsTarget<TARGET extends AbsTarget, ENTITY extends AbsEnti
@Override public void resume() { @Override public void resume() {
if (checkEntity()) { if (checkEntity()) {
AriaManager.getInstance(AriaManager.APP) AriaManager.getInstance(AriaManager.APP)
.setCmd(CommonUtil.createNormalCmd(mTargetName, mTaskEntity, NormalCmdFactory.TASK_START, .setCmd(
checkTaskType())) CommonUtil.createNormalCmd(mTaskEntity, NormalCmdFactory.TASK_START, checkTaskType()))
.exe(); .exe();
} }
} }
@ -228,7 +261,7 @@ public abstract class AbsTarget<TARGET extends AbsTarget, ENTITY extends AbsEnti
@Override public void cancel() { @Override public void cancel() {
if (checkEntity()) { if (checkEntity()) {
AriaManager.getInstance(AriaManager.APP) AriaManager.getInstance(AriaManager.APP)
.setCmd(CommonUtil.createNormalCmd(mTargetName, mTaskEntity, NormalCmdFactory.TASK_CANCEL, .setCmd(CommonUtil.createNormalCmd(mTaskEntity, NormalCmdFactory.TASK_CANCEL,
checkTaskType())) checkTaskType()))
.exe(); .exe();
} }
@ -242,10 +275,8 @@ public abstract class AbsTarget<TARGET extends AbsTarget, ENTITY extends AbsEnti
List<ICmd> cmds = new ArrayList<>(); List<ICmd> cmds = new ArrayList<>();
int taskType = checkTaskType(); int taskType = checkTaskType();
cmds.add( cmds.add(
CommonUtil.createNormalCmd(mTargetName, mTaskEntity, NormalCmdFactory.TASK_STOP, CommonUtil.createNormalCmd(mTaskEntity, NormalCmdFactory.TASK_STOP, taskType));
taskType)); cmds.add(CommonUtil.createNormalCmd(mTaskEntity, NormalCmdFactory.TASK_START, taskType));
cmds.add(CommonUtil.createNormalCmd(mTargetName, mTaskEntity, NormalCmdFactory.TASK_START,
taskType));
AriaManager.getInstance(AriaManager.APP).setCmds(cmds).exe(); AriaManager.getInstance(AriaManager.APP).setCmds(cmds).exe();
} }
} }
@ -258,8 +289,9 @@ public abstract class AbsTarget<TARGET extends AbsTarget, ENTITY extends AbsEnti
*/ */
public void cancel(boolean removeFile) { public void cancel(boolean removeFile) {
if (checkEntity()) { if (checkEntity()) {
CancelCmd cancelCmd = (CancelCmd) CommonUtil.createNormalCmd(mTargetName, mTaskEntity, CancelCmd cancelCmd =
NormalCmdFactory.TASK_CANCEL, checkTaskType()); (CancelCmd) CommonUtil.createNormalCmd(mTaskEntity, NormalCmdFactory.TASK_CANCEL,
checkTaskType());
cancelCmd.removeFile = removeFile; cancelCmd.removeFile = removeFile;
AriaManager.getInstance(AriaManager.APP).setCmd(cancelCmd).exe(); AriaManager.getInstance(AriaManager.APP).setCmd(cancelCmd).exe();
} }

@ -17,12 +17,19 @@ package com.arialyy.aria.core.inf;
import android.content.Context; import android.content.Context;
import android.os.Handler; import android.os.Handler;
import android.text.TextUtils;
import com.arialyy.aria.core.common.IUtil;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.CommonUtil;
import java.util.HashMap;
import java.util.Map;
/** /**
* Created by AriaL on 2017/6/29. * Created by AriaL on 2017/6/29.
*/ */
public abstract class AbsTask<TASK_ENTITY extends AbsTaskEntity> implements ITask<TASK_ENTITY> { public abstract class AbsTask<ENTITY extends AbsEntity, TASK_ENTITY extends AbsTaskEntity>
implements ITask<TASK_ENTITY> {
public static final String ERROR_INFO_KEY = "ERROR_INFO_KEY";
/** /**
* 是否需要重试默认为true * 是否需要重试默认为true
@ -30,19 +37,64 @@ public abstract class AbsTask<TASK_ENTITY extends AbsTaskEntity> implements ITas
public boolean needRetry = true; public boolean needRetry = true;
protected TASK_ENTITY mTaskEntity; protected TASK_ENTITY mTaskEntity;
protected Handler mOutHandler; protected Handler mOutHandler;
/**
* 用于生成该任务对象的hash码
*/
private String mTargetName;
protected Context mContext; protected Context mContext;
protected boolean isHeighestTask = false; boolean isHeighestTask = false;
private boolean isCancel = false, isStop = false; private boolean isCancel = false, isStop = false;
protected IUtil mUtil;
/**
* 扩展信息
*/
private Map<String, Object> mExpand = new HashMap<>();
/**
* 该任务的调度类型
*/
@TaskSchedulerType
private int mSchedulerType = TaskSchedulerType.TYPE_DEFAULT;
protected IEventListener mListener;
protected ENTITY mEntity;
protected String TAG;
protected AbsTask() {
TAG = CommonUtil.getClassName(this);
}
public Handler getOutHandler() { public Handler getOutHandler() {
return mOutHandler; return mOutHandler;
} }
/**
* 添加扩展数据
* 读取扩展数据{@link #getExpand(String)}
*/
public void putExpand(String key, Object obj) {
if (TextUtils.isEmpty(key)) {
ALog.e(TAG, "key 为空");
return;
} else if (obj == null) {
ALog.e(TAG, "扩展数据为空");
return;
}
mExpand.put(key, obj);
}
/**
* 读取扩展数据
*/
public Object getExpand(String key) {
return mExpand.get(key);
}
/**
* 设置最大下载/上传速度
*
* @param speed 单位为kb
*/
public void setMaxSpeed(int speed) {
if (mUtil != null) {
mUtil.setMaxSpeed(speed);
}
}
/** /**
* 任务是否完成 * 任务是否完成
* *
@ -118,16 +170,55 @@ public abstract class AbsTask<TASK_ENTITY extends AbsTaskEntity> implements ITas
* *
* @return 如果实体不存在则返回null否则返回扩展字段 * @return 如果实体不存在则返回null否则返回扩展字段
*/ */
@Override public String getExtendField() { public String getExtendField() {
return mTaskEntity.getEntity() == null ? null : mTaskEntity.getEntity().getStr(); return mTaskEntity.getEntity() == null ? null : mTaskEntity.getEntity().getStr();
} }
@Override public void start() {
if (mUtil.isRunning()) {
ALog.d(TAG, "任务正在下载");
} else {
mUtil.start();
}
}
@Override public void stop() { @Override public void stop() {
stop(TaskSchedulerType.TYPE_DEFAULT);
}
@Override public void stop(@TaskSchedulerType int type) {
isStop = true; isStop = true;
mSchedulerType = type;
if (mUtil.isRunning()) {
mUtil.stop();
} else {
mListener.onStop(mEntity.getCurrentProgress());
}
} }
@Override public void cancel() { @Override public void cancel() {
isCancel = true; isCancel = true;
if (!mUtil.isRunning()) {
mListener.onCancel();
} else {
mUtil.cancel();
}
}
/**
* 是否真正下载
*
* @return {@code true} 真正下载
*/
@Override public boolean isRunning() {
return mUtil.isRunning();
}
/**
* 任务的调度类型
*/
public int getSchedulerType() {
return mSchedulerType;
} }
/** /**
@ -199,14 +290,6 @@ public abstract class AbsTask<TASK_ENTITY extends AbsTaskEntity> implements ITas
*/ */
public abstract String getTaskName(); public abstract String getTaskName();
public String getTargetName() {
return mTargetName;
}
@Override public void setTargetName(String targetName) {
this.mTargetName = targetName;
}
public boolean isHighestPriorityTask() { public boolean isHighestPriorityTask() {
return isHeighestTask; return isHeighestTask;
} }

@ -19,8 +19,12 @@ import com.arialyy.aria.core.FtpUrlEntity;
import com.arialyy.aria.core.common.RequestEnum; import com.arialyy.aria.core.common.RequestEnum;
import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.orm.DbEntity;
import com.arialyy.aria.orm.annotation.Ignore; import com.arialyy.aria.orm.annotation.Ignore;
import java.net.CookieManager;
import java.net.HttpCookie;
import java.net.Proxy; import java.net.Proxy;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
@ -55,6 +59,8 @@ public abstract class AbsTaskEntity<ENTITY extends AbsEntity> extends DbEntity {
*/ */
public static final int U_FTP = 0xA2; public static final int U_FTP = 0xA2;
@Ignore private CookieManager cookieManager;
/** /**
* 账号和密码 * 账号和密码
*/ */
@ -70,6 +76,11 @@ public abstract class AbsTaskEntity<ENTITY extends AbsEntity> extends DbEntity {
*/ */
@Ignore private boolean isNewTask = false; @Ignore private boolean isNewTask = false;
/**
* 请求参数
*/
@Ignore private Map<String, String> params;
/** /**
* 任务状态和Entity的state同步 * 任务状态和Entity的state同步
*/ */
@ -129,6 +140,14 @@ public abstract class AbsTaskEntity<ENTITY extends AbsEntity> extends DbEntity {
public abstract ENTITY getEntity(); public abstract ENTITY getEntity();
public CookieManager getCookieManager() {
return cookieManager;
}
public void setCookieManager(CookieManager cookieManager) {
this.cookieManager = cookieManager;
}
/** /**
* 获取任务下载状态 * 获取任务下载状态
* *
@ -256,4 +275,12 @@ public abstract class AbsTaskEntity<ENTITY extends AbsEntity> extends DbEntity {
public void setCode(int code) { public void setCode(int code) {
this.code = code; this.code = code;
} }
public Map<String, String> getParams() {
return params;
}
public void setParams(Map<String, String> params) {
this.params = params;
}
} }

@ -15,6 +15,8 @@
*/ */
package com.arialyy.aria.core.inf; package com.arialyy.aria.core.inf;
import com.arialyy.aria.exception.BaseException;
/** /**
* Created by Aria.Lao on 2017/7/18. * Created by Aria.Lao on 2017/7/18.
* 基础事件 * 基础事件
@ -58,8 +60,9 @@ public interface IEventListener {
/** /**
* 下载失败 * 下载失败
*
* @param needRetry 是否需要重试{@code true} 需要 * @param needRetry 是否需要重试{@code true} 需要
* @param e 失败信息
*/ */
void onFail(boolean needRetry); void onFail(boolean needRetry, BaseException e);
} }

@ -16,6 +16,7 @@
package com.arialyy.aria.core.inf; package com.arialyy.aria.core.inf;
import android.support.annotation.CheckResult; import android.support.annotation.CheckResult;
import java.net.Proxy;
/** /**
* Created by laoyuyu on 2018/3/9. * Created by laoyuyu on 2018/3/9.
@ -45,4 +46,12 @@ public interface IFtpTarget<TARGET extends ITarget> {
*/ */
@CheckResult @CheckResult
TARGET login(String userName, String password, String account); TARGET login(String userName, String password, String account);
/**
* 设置代理
*
* @param proxy {@link Proxy}
*/
@CheckResult
TARGET setProxy(Proxy proxy);
} }

@ -19,13 +19,14 @@ import android.support.annotation.CheckResult;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import com.arialyy.aria.core.common.RequestEnum; import com.arialyy.aria.core.common.RequestEnum;
import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadEntity;
import java.net.Proxy;
import java.util.Map; import java.util.Map;
/** /**
* Created by laoyuyu on 2018/3/9. * Created by laoyuyu on 2018/3/9.
* HTTP Header功能接口 * HTTP Header功能接口
*/ */
public interface IHttpHeaderTarget<TARGET extends ITarget> { public interface IHttpHeaderDelegate<TARGET extends ITarget> {
/** /**
* 给url请求添加Header数据 * 给url请求添加Header数据
@ -46,11 +47,6 @@ public interface IHttpHeaderTarget<TARGET extends ITarget> {
@CheckResult @CheckResult
TARGET addHeaders(Map<String, String> headers); TARGET addHeaders(Map<String, String> headers);
/**
* 设置HTTP请求类型
*
* @param requestEnum {@link RequestEnum}
*/
@CheckResult @CheckResult
TARGET setRequestMode(RequestEnum requestEnum); TARGET setUrlProxy(Proxy proxy);
} }

@ -0,0 +1,31 @@
/*
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.arialyy.aria.core.inf;
import java.util.Map;
/**
* post 通用处理接口
*/
public interface IPostDelegate<TARGET extends ITarget> {
/**
* 设置Post请求参数
*/
TARGET setParams(Map<String, String> params);
TARGET setParam(String key, String value);
}

@ -18,48 +18,7 @@ package com.arialyy.aria.core.inf;
/** /**
* Created by AriaL on 2017/6/29. * Created by AriaL on 2017/6/29.
*/ */
public interface ITarget<TARGET extends ITarget> { public interface ITarget {
/**
* 获取任务状态
*
* @return {@link IEntity}
*/
int getTaskState();
/**
* 任务是否在执行
*
* @return {@code true} 任务正在执行
*/
boolean isRunning();
/**
* 任务是否存在
*
* @return {@code true} 任务存在
*/
boolean taskExists();
/**
* 任务文件大小
*/
long getSize();
/**
* 转换后的大小
*/
String getConvertSize();
/**
* 获取任务进度百分比
*/
int getPercent();
/**
* 获取任务进度如果任务存在则返回当前进度
*/
long getCurrentProgress();
/** /**
* 开始下载 * 开始下载
*/ */

@ -25,11 +25,6 @@ public interface ITask<TASK_ENTITY extends AbsTaskEntity> {
*/ */
int getState(); int getState();
/**
* 获取扩展字段
*/
String getExtendField();
/** /**
* 唯一标识符DownloadTask 为下载地址UploadTask 为文件路径 * 唯一标识符DownloadTask 为下载地址UploadTask 为文件路径
*/ */
@ -49,8 +44,17 @@ public interface ITask<TASK_ENTITY extends AbsTaskEntity> {
void start(); void start();
/**
* 停止任务
*/
void stop(); void stop();
/**
* 停止任务
* @param type {@code 0}默认操作{@code 1}停止任务不自动执行下一任务
*/
void stop(int type);
void cancel(); void cancel();
/** /**
@ -89,6 +93,4 @@ public interface ITask<TASK_ENTITY extends AbsTaskEntity> {
* @return 返回 3mb * @return 返回 3mb
*/ */
String getConvertCurrentProgress(); String getConvertCurrentProgress();
void setTargetName(String targetName);
} }

@ -0,0 +1,22 @@
package com.arialyy.aria.core.inf;
import android.support.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@IntDef({
TaskSchedulerType.TYPE_DEFAULT,
TaskSchedulerType.TYPE_STOP_NOT_NEXT,
TaskSchedulerType.TYPE_STOP_AND_WAIT
})
@Retention(RetentionPolicy.SOURCE) public @interface TaskSchedulerType {
int TYPE_DEFAULT = 1;
/**
* 停止当前任务并且不自动启动下一任务
*/
int TYPE_STOP_NOT_NEXT = 2;
/**
* 停止任务并让当前任务处于等待状态
*/
int TYPE_STOP_AND_WAIT = 3;
}

@ -59,15 +59,11 @@ class DGTEFactory implements IGTEFactory<DownloadGroupEntity, DownloadGroupTaskE
if (gte == null) { if (gte == null) {
// 创建新的任务组任务实体 // 创建新的任务组任务实体
gte = new DownloadGroupTaskEntity(); gte = new DownloadGroupTaskEntity();
//创建子任务的任务实体
gte.setSubTaskEntities(createDGSubTaskEntity(entity));
} else if (gte.getSubTaskEntities() == null || gte.getSubTaskEntities().isEmpty()) {
gte.setSubTaskEntities(createDGSubTaskEntity(entity));
} }
} else { } else {
gte = new DownloadGroupTaskEntity(); gte = new DownloadGroupTaskEntity();
gte.setSubTaskEntities(createDGSubTaskEntity(entity));
} }
gte.setSubTaskEntities(createDGSubTaskEntity(entity));
gte.setKey(entity.getGroupName()); gte.setKey(entity.getGroupName());
gte.setEntity(entity); gte.setEntity(entity);

@ -0,0 +1,135 @@
/*
* 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.manager;
import com.arialyy.aria.core.common.AbsThreadTask;
import com.arialyy.aria.core.inf.AbsTaskEntity;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* 线程管理器
*/
public class ThreadTaskManager {
private static volatile ThreadTaskManager INSTANCE = null;
private static final Object LOCK = new Object();
private final String TAG = "ThreadTaskManager";
private ExecutorService mExePool;
private Map<String, Set<Future>> mThreadTasks = new HashMap<>();
public static ThreadTaskManager getInstance() {
if (INSTANCE == null) {
synchronized (LOCK) {
INSTANCE = new ThreadTaskManager();
}
}
return INSTANCE;
}
private ThreadTaskManager() {
mExePool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
}
/**
* 启动线程任务
*
* @param key 任务对应的key{@link AbsTaskEntity#getKey()}
* @param threadTask 线程任务{@link AbsThreadTask}
*/
public synchronized void startThread(String key, AbsThreadTask threadTask) {
if (mExePool.isShutdown()) {
ALog.e(TAG, "线程池已经关闭");
return;
}
key = getKey(key);
Set<Future> temp = mThreadTasks.get(key);
if (temp == null) {
temp = new HashSet<>();
mThreadTasks.put(key, temp);
}
temp.add(mExePool.submit(threadTask));
}
/**
* 停止任务的所有线程
*
* @param key 任务对应的key{@link AbsTaskEntity#getKey()}
*/
public synchronized void stopTaskThread(String key) {
if (mExePool.isShutdown()) {
ALog.e(TAG, "线程池已经关闭");
return;
}
key = getKey(key);
Set<Future> temp = mThreadTasks.get(key);
if (temp != null && temp.size() > 0) {
try {
for (Future future : temp) {
if (future.isDone() || future.isCancelled()) {
continue;
}
AbsThreadTask task = (AbsThreadTask) future.get();
task.setInterrupted(true);
future.cancel(true);
}
} catch (Exception e) {
ALog.e(TAG, e);
}
temp.clear();
}
mThreadTasks.remove(key);
}
/**
* 重试线程任务
*
* @param task 线程任务
*/
public synchronized void retryThread(AbsThreadTask task) {
if (mExePool.isShutdown()) {
ALog.e(TAG, "线程池已经关闭");
return;
}
try {
if (task == null || task.isInterrupted()) {
ALog.e(TAG, "线程为空或线程已经中断");
return;
}
} catch (Exception e) {
ALog.e(TAG, e);
return;
}
mExePool.submit(task);
}
/**
* map中的key
*
* @param key 任务的key{@link AbsTaskEntity#getKey()}
* @return 转换后的map中的key
*/
private String getKey(String key) {
return CommonUtil.getStrMd5(key);
}
}

@ -19,9 +19,12 @@ package com.arialyy.aria.core.queue;
import com.arialyy.aria.core.inf.AbsTask; import com.arialyy.aria.core.inf.AbsTask;
import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.inf.AbsTaskEntity;
import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.core.inf.TaskSchedulerType;
import com.arialyy.aria.core.queue.pool.BaseCachePool; import com.arialyy.aria.core.queue.pool.BaseCachePool;
import com.arialyy.aria.core.queue.pool.BaseExecutePool; import com.arialyy.aria.core.queue.pool.BaseExecutePool;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import java.util.Map;
import java.util.Set;
/** /**
* Created by lyy on 2017/2/23. * Created by lyy on 2017/2/23.
@ -67,16 +70,36 @@ abstract class AbsTaskQueue<TASK extends AbsTask, TASK_ENTITY extends AbsTaskEnt
* 停止所有任务 * 停止所有任务
*/ */
@Override public void stopAllTask() { @Override public void stopAllTask() {
mCachePool.clear();
for (String key : mExecutePool.getAllTask().keySet()) { for (String key : mExecutePool.getAllTask().keySet()) {
TASK task = mExecutePool.getAllTask().get(key); TASK task = mExecutePool.getAllTask().get(key);
if (task != null) { if (task != null) {
int state = task.getState(); int state = task.getState();
if (task.isRunning() || (state != IEntity.STATE_COMPLETE if (task.isRunning() || (state != IEntity.STATE_COMPLETE
&& state != IEntity.STATE_CANCEL)) { && state != IEntity.STATE_CANCEL)) {
task.stop(); task.stop(TaskSchedulerType.TYPE_STOP_NOT_NEXT);
}
}
}
for (String key : mCachePool.getAllTask().keySet()) {
TASK task = mCachePool.getAllTask().get(key);
if (task != null) {
task.stop(TaskSchedulerType.TYPE_STOP_NOT_NEXT);
} }
} }
mCachePool.clear();
}
/**
* 最大下载速度
*/
public void setMaxSpeed(int maxSpeed) {
Map<String, TASK> tasks = mExecutePool.getAllTask();
Set<String> keys = tasks.keySet();
for (String key : keys) {
TASK task = tasks.get(key);
task.setMaxSpeed(maxSpeed);
} }
} }
@ -173,7 +196,6 @@ abstract class AbsTaskQueue<TASK extends AbsTask, TASK_ENTITY extends AbsTaskEnt
} }
@Override public void removeTaskFormQueue(String key) { @Override public void removeTaskFormQueue(String key) {
//TEManager.getInstance().removeTEntity(key);
TASK task = mExecutePool.getTask(key); TASK task = mExecutePool.getTask(key);
if (task != null) { if (task != null) {
ALog.d(TAG, String.format("从执行池删除任务【%s】%s", task.getTaskName(), ALog.d(TAG, String.format("从执行池删除任务【%s】%s", task.getTaskName(),
@ -191,10 +213,6 @@ abstract class AbsTaskQueue<TASK extends AbsTask, TASK_ENTITY extends AbsTaskEnt
ALog.e(TAG, "任务重试失败,原因:task 为null"); ALog.e(TAG, "任务重试失败,原因:task 为null");
return; return;
} }
//if (!NetUtils.isConnected(AriaManager.APP)) {
// ALog.e(TAG, "任务【" + task.getTaskName() + "】重试失败,原因:网络未连接");
// return;
//}
if (!task.isRunning()) { if (!task.isRunning()) {
task.start(); task.start();
} else { } else {

@ -16,7 +16,6 @@
package com.arialyy.aria.core.queue; package com.arialyy.aria.core.queue;
import android.text.TextUtils;
import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.core.download.DownloadGroupTask; import com.arialyy.aria.core.download.DownloadGroupTask;
import com.arialyy.aria.core.download.DownloadGroupTaskEntity; import com.arialyy.aria.core.download.DownloadGroupTaskEntity;
@ -60,20 +59,16 @@ public class DownloadGroupTaskQueue
return AriaManager.getInstance(AriaManager.APP).getDownloadConfig().getMaxTaskNum(); return AriaManager.getInstance(AriaManager.APP).getDownloadConfig().getMaxTaskNum();
} }
@Override public DownloadGroupTask createTask(String targetName, DownloadGroupTaskEntity entity) { @Override public DownloadGroupTask createTask(DownloadGroupTaskEntity entity) {
DownloadGroupTask task = null; DownloadGroupTask task = null;
if (!TextUtils.isEmpty(targetName)) {
if (mCachePool.getTask(entity.getEntity().getKey()) == null if (mCachePool.getTask(entity.getEntity().getKey()) == null
&& mExecutePool.getTask(entity.getEntity().getKey()) == null) { && mExecutePool.getTask(entity.getEntity().getKey()) == null) {
task = (DownloadGroupTask) TaskFactory.getInstance() task = (DownloadGroupTask) TaskFactory.getInstance()
.createTask(targetName, entity, DownloadGroupSchedulers.getInstance()); .createTask(entity, DownloadGroupSchedulers.getInstance());
mCachePool.putTask(task); mCachePool.putTask(task);
} else { } else {
ALog.w(TAG, "任务已存在"); ALog.w(TAG, "任务已存在");
} }
} else {
ALog.e(TAG, "target name 为 null!!");
}
return task; return task;
} }

@ -16,10 +16,11 @@
package com.arialyy.aria.core.queue; package com.arialyy.aria.core.queue;
import android.text.TextUtils;
import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.core.download.DownloadTask; import com.arialyy.aria.core.download.DownloadTask;
import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.download.DownloadTaskEntity;
import com.arialyy.aria.core.inf.AbsTask;
import com.arialyy.aria.core.inf.TaskSchedulerType;
import com.arialyy.aria.core.queue.pool.BaseCachePool; import com.arialyy.aria.core.queue.pool.BaseCachePool;
import com.arialyy.aria.core.queue.pool.BaseExecutePool; import com.arialyy.aria.core.queue.pool.BaseExecutePool;
import com.arialyy.aria.core.queue.pool.DownloadSharePool; import com.arialyy.aria.core.queue.pool.DownloadSharePool;
@ -89,7 +90,7 @@ public class DownloadTaskQueue extends AbsTaskQueue<DownloadTask, DownloadTaskEn
DownloadTask oldTsk = mExecutePool.pollTask(); DownloadTask oldTsk = mExecutePool.pollTask();
if (oldTsk != null && oldTsk.isRunning()) { if (oldTsk != null && oldTsk.isRunning()) {
if (i == maxSize - 1) { if (i == maxSize - 1) {
oldTsk.stopAndWait(); oldTsk.stop(TaskSchedulerType.TYPE_STOP_AND_WAIT);
mCachePool.putTaskToFirst(oldTsk); mCachePool.putTaskToFirst(oldTsk);
break; break;
} }
@ -104,32 +105,16 @@ public class DownloadTaskQueue extends AbsTaskQueue<DownloadTask, DownloadTaskEn
} }
} }
/** @Override public DownloadTask createTask(DownloadTaskEntity entity) {
* 最大下载速度
*/
public void setMaxSpeed(double maxSpeed) {
Map<String, DownloadTask> tasks = mExecutePool.getAllTask();
Set<String> keys = tasks.keySet();
for (String key : keys) {
DownloadTask task = tasks.get(key);
task.setMaxSpeed(maxSpeed);
}
}
@Override public DownloadTask createTask(String target, DownloadTaskEntity entity) {
DownloadTask task = null; DownloadTask task = null;
if (!TextUtils.isEmpty(target)) {
if (mCachePool.getTask(entity.getEntity().getKey()) == null if (mCachePool.getTask(entity.getEntity().getKey()) == null
&& mExecutePool.getTask(entity.getEntity().getKey()) == null) { && mExecutePool.getTask(entity.getEntity().getKey()) == null) {
task = (DownloadTask) TaskFactory.getInstance() task = (DownloadTask) TaskFactory.getInstance()
.createTask(target, entity, DownloadSchedulers.getInstance()); .createTask(entity, DownloadSchedulers.getInstance());
mCachePool.putTask(task); mCachePool.putTask(task);
} else { } else {
ALog.w(TAG, "任务已存在"); ALog.w(TAG, "任务已存在");
} }
} else {
ALog.e(TAG, "target name 为 null!!");
}
return task; return task;
} }

@ -113,10 +113,9 @@ public interface ITaskQueue<TASK extends AbsTask, TASK_ENTITY extends AbsTaskEnt
* 创建一个新的任务创建时只是将新任务存储到缓存池 * 创建一个新的任务创建时只是将新任务存储到缓存池
* *
* @param entity 任务实体{@link DownloadTaskEntity}{@link UploadTaskEntity} * @param entity 任务实体{@link DownloadTaskEntity}{@link UploadTaskEntity}
* @param targetName 生成该任务的对象
* @return {@link DownloadTask}{@link UploadTask} * @return {@link DownloadTask}{@link UploadTask}
*/ */
TASK createTask(String targetName, TASK_ENTITY entity); TASK createTask(TASK_ENTITY entity);
/** /**
* 通过工作实体缓存池或任务池搜索下载任务如果缓存池或任务池都没有任务则创建新任务 * 通过工作实体缓存池或任务池搜索下载任务如果缓存池或任务池都没有任务则创建新任务

@ -21,8 +21,8 @@ import com.arialyy.aria.core.download.DownloadGroupTask;
import com.arialyy.aria.core.download.DownloadGroupTaskEntity; import com.arialyy.aria.core.download.DownloadGroupTaskEntity;
import com.arialyy.aria.core.download.DownloadTask; import com.arialyy.aria.core.download.DownloadTask;
import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.download.DownloadTaskEntity;
import com.arialyy.aria.core.inf.ITask;
import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.inf.AbsTaskEntity;
import com.arialyy.aria.core.inf.ITask;
import com.arialyy.aria.core.scheduler.DownloadSchedulers; import com.arialyy.aria.core.scheduler.DownloadSchedulers;
import com.arialyy.aria.core.scheduler.ISchedulers; import com.arialyy.aria.core.scheduler.ISchedulers;
import com.arialyy.aria.core.scheduler.UploadSchedulers; import com.arialyy.aria.core.scheduler.UploadSchedulers;
@ -61,13 +61,13 @@ class TaskFactory {
* @return {@link DownloadTask}{@link UploadTask}{@link DownloadGroupTask} * @return {@link DownloadTask}{@link UploadTask}{@link DownloadGroupTask}
*/ */
<TASK_ENTITY extends AbsTaskEntity, SCHEDULER extends ISchedulers> ITask createTask( <TASK_ENTITY extends AbsTaskEntity, SCHEDULER extends ISchedulers> ITask createTask(
String targetName, TASK_ENTITY entity, SCHEDULER schedulers) { TASK_ENTITY entity, SCHEDULER schedulers) {
if (entity instanceof DownloadTaskEntity) { if (entity instanceof DownloadTaskEntity) {
return createDownloadTask(targetName, (DownloadTaskEntity) entity, schedulers); return createDownloadTask((DownloadTaskEntity) entity, schedulers);
} else if (entity instanceof UploadTaskEntity) { } else if (entity instanceof UploadTaskEntity) {
return createUploadTask(targetName, (UploadTaskEntity) entity, schedulers); return createUploadTask((UploadTaskEntity) entity, schedulers);
} else if (entity instanceof DownloadGroupTaskEntity) { } else if (entity instanceof DownloadGroupTaskEntity) {
return createDownloadGroupTask(targetName, (DownloadGroupTaskEntity) entity, schedulers); return createDownloadGroupTask((DownloadGroupTaskEntity) entity, schedulers);
} }
return null; return null;
} }
@ -78,9 +78,9 @@ class TaskFactory {
* @param entity 下载任务实体{@link DownloadGroupTask} * @param entity 下载任务实体{@link DownloadGroupTask}
* @param schedulers {@link ISchedulers} * @param schedulers {@link ISchedulers}
*/ */
private DownloadGroupTask createDownloadGroupTask(String targetName, private DownloadGroupTask createDownloadGroupTask(DownloadGroupTaskEntity entity,
DownloadGroupTaskEntity entity, ISchedulers schedulers) { ISchedulers schedulers) {
DownloadGroupTask.Builder builder = new DownloadGroupTask.Builder(targetName, entity); DownloadGroupTask.Builder builder = new DownloadGroupTask.Builder(entity);
builder.setOutHandler(schedulers); builder.setOutHandler(schedulers);
return builder.build(); return builder.build();
} }
@ -89,10 +89,8 @@ class TaskFactory {
* @param entity 上传任务实体{@link UploadTaskEntity} * @param entity 上传任务实体{@link UploadTaskEntity}
* @param schedulers {@link ISchedulers} * @param schedulers {@link ISchedulers}
*/ */
private UploadTask createUploadTask(String targetName, UploadTaskEntity entity, private UploadTask createUploadTask(UploadTaskEntity entity, ISchedulers schedulers) {
ISchedulers schedulers) {
UploadTask.Builder builder = new UploadTask.Builder(); UploadTask.Builder builder = new UploadTask.Builder();
builder.setTargetName(targetName);
builder.setUploadTaskEntity(entity); builder.setUploadTaskEntity(entity);
builder.setOutHandler(schedulers); builder.setOutHandler(schedulers);
return builder.build(); return builder.build();
@ -102,9 +100,8 @@ class TaskFactory {
* @param entity 下载任务实体{@link DownloadTaskEntity} * @param entity 下载任务实体{@link DownloadTaskEntity}
* @param schedulers {@link ISchedulers} * @param schedulers {@link ISchedulers}
*/ */
private DownloadTask createDownloadTask(String targetName, DownloadTaskEntity entity, private DownloadTask createDownloadTask(DownloadTaskEntity entity, ISchedulers schedulers) {
ISchedulers schedulers) { DownloadTask.Builder builder = new DownloadTask.Builder(entity);
DownloadTask.Builder builder = new DownloadTask.Builder(targetName, entity);
builder.setOutHandler(schedulers); builder.setOutHandler(schedulers);
return builder.build(); return builder.build();
} }

@ -16,7 +16,6 @@
package com.arialyy.aria.core.queue; package com.arialyy.aria.core.queue;
import android.text.TextUtils;
import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.core.queue.pool.BaseCachePool; import com.arialyy.aria.core.queue.pool.BaseCachePool;
import com.arialyy.aria.core.queue.pool.BaseExecutePool; import com.arialyy.aria.core.queue.pool.BaseExecutePool;
@ -62,20 +61,16 @@ public class UploadTaskQueue extends AbsTaskQueue<UploadTask, UploadTaskEntity>
return AriaManager.getInstance(AriaManager.APP).getUploadConfig().getMaxTaskNum(); return AriaManager.getInstance(AriaManager.APP).getUploadConfig().getMaxTaskNum();
} }
@Override public UploadTask createTask(String targetName, UploadTaskEntity entity) { @Override public UploadTask createTask(UploadTaskEntity entity) {
UploadTask task = null; UploadTask task = null;
if (!TextUtils.isEmpty(targetName)) {
if (mCachePool.getTask(entity.getEntity().getKey()) == null if (mCachePool.getTask(entity.getEntity().getKey()) == null
&& mExecutePool.getTask(entity.getEntity().getKey()) == null) { && mExecutePool.getTask(entity.getEntity().getKey()) == null) {
task = (UploadTask) TaskFactory.getInstance() task = (UploadTask) TaskFactory.getInstance()
.createTask(targetName, entity, UploadSchedulers.getInstance()); .createTask(entity, UploadSchedulers.getInstance());
mCachePool.putTask(task); mCachePool.putTask(task);
} else { } else {
ALog.w(TAG, "任务已存在"); ALog.w(TAG, "任务已存在");
} }
} else {
ALog.e(TAG, "target name 为 null是!!");
}
return task; return task;
} }
} }

@ -56,6 +56,10 @@ public class AbsSchedulerListener<TASK extends ITask, SUB_ENTITY extends AbsNorm
} }
@Override public void onTaskFail(TASK task, Exception e) {
}
@Override public void onTaskComplete(TASK task) { @Override public void onTaskComplete(TASK task) {
} }
@ -92,10 +96,15 @@ public class AbsSchedulerListener<TASK extends ITask, SUB_ENTITY extends AbsNorm
} }
@Deprecated
public void onSubTaskFail(TASK task, SUB_ENTITY subTask) { public void onSubTaskFail(TASK task, SUB_ENTITY subTask) {
} }
public void onSubTaskFail(TASK task, SUB_ENTITY subTask, Exception e) {
}
public void onSubTaskRunning(TASK task, SUB_ENTITY subTask) { public void onSubTaskRunning(TASK task, SUB_ENTITY subTask) {
} }

@ -26,6 +26,7 @@ import com.arialyy.aria.core.inf.AbsTask;
import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.inf.AbsTaskEntity;
import com.arialyy.aria.core.inf.GroupSendParams; import com.arialyy.aria.core.inf.GroupSendParams;
import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.core.inf.TaskSchedulerType;
import com.arialyy.aria.core.manager.TEManager; import com.arialyy.aria.core.manager.TEManager;
import com.arialyy.aria.core.queue.ITaskQueue; import com.arialyy.aria.core.queue.ITaskQueue;
import com.arialyy.aria.core.upload.UploadTask; import com.arialyy.aria.core.upload.UploadTask;
@ -40,7 +41,7 @@ import java.util.concurrent.ConcurrentHashMap;
* Created by lyy on 2017/6/4. * Created by lyy on 2017/6/4.
* 事件调度器用于处理任务状态的调度 * 事件调度器用于处理任务状态的调度
*/ */
abstract class AbsSchedulers<TASK_ENTITY extends AbsTaskEntity, TASK extends AbsTask<TASK_ENTITY>, abstract class AbsSchedulers<TASK_ENTITY extends AbsTaskEntity, TASK extends AbsTask,
QUEUE extends ITaskQueue<TASK, TASK_ENTITY>> implements ISchedulers<TASK> { QUEUE extends ITaskQueue<TASK, TASK_ENTITY>> implements ISchedulers<TASK> {
private final String TAG = "AbsSchedulers"; private final String TAG = "AbsSchedulers";
@ -139,7 +140,8 @@ abstract class AbsSchedulers<TASK_ENTITY extends AbsTaskEntity, TASK extends Abs
listener.onSubTaskStop((TASK) params.groupTask, params.entity); listener.onSubTaskStop((TASK) params.groupTask, params.entity);
break; break;
case SUB_FAIL: case SUB_FAIL:
listener.onSubTaskFail((TASK) params.groupTask, params.entity); listener.onSubTaskFail((TASK) params.groupTask, params.entity,
(Exception) (params.groupTask).getExpand(AbsTask.ERROR_INFO_KEY));
break; break;
case SUB_RUNNING: case SUB_RUNNING:
listener.onSubTaskRunning((TASK) params.groupTask, params.entity); listener.onSubTaskRunning((TASK) params.groupTask, params.entity);
@ -169,13 +171,13 @@ abstract class AbsSchedulers<TASK_ENTITY extends AbsTaskEntity, TASK extends Abs
mQueue.removeTaskFormQueue(task.getKey()); mQueue.removeTaskFormQueue(task.getKey());
if (mQueue.getCurrentExePoolNum() < mQueue.getMaxTaskNum()) { if (mQueue.getCurrentExePoolNum() < mQueue.getMaxTaskNum()) {
ALog.d(TAG, "stop_next"); ALog.d(TAG, "stop_next");
startNextTask(); startNextTask(task);
} }
break; break;
case COMPLETE: case COMPLETE:
mQueue.removeTaskFormQueue(task.getKey()); mQueue.removeTaskFormQueue(task.getKey());
ALog.d(TAG, "complete_next"); ALog.d(TAG, "complete_next");
startNextTask(); startNextTask(task);
break; break;
case FAIL: case FAIL:
handleFailTask(task); handleFailTask(task);
@ -243,7 +245,7 @@ abstract class AbsSchedulers<TASK_ENTITY extends AbsTaskEntity, TASK extends Abs
listener.onTaskComplete(task); listener.onTaskComplete(task);
break; break;
case FAIL: case FAIL:
listener.onTaskFail(task); listener.onTaskFail(task, (Exception) task.getExpand(AbsTask.ERROR_INFO_KEY));
break; break;
case SUPPORT_BREAK_POINT: case SUPPORT_BREAK_POINT:
listener.onNoSupportBreakPoint(task); listener.onNoSupportBreakPoint(task);
@ -259,9 +261,9 @@ abstract class AbsSchedulers<TASK_ENTITY extends AbsTaskEntity, TASK extends Abs
*/ */
private void handleFailTask(final TASK task) { private void handleFailTask(final TASK task) {
if (!task.needRetry || task.isStop() || task.isCancel()) { if (!task.needRetry || task.isStop() || task.isCancel()) {
callback(FAIL, task);
mQueue.removeTaskFormQueue(task.getKey()); mQueue.removeTaskFormQueue(task.getKey());
ALog.d(TAG, "fail_next"); startNextTask(task);
startNextTask();
return; return;
} }
long interval = 2000; long interval = 2000;
@ -283,8 +285,7 @@ abstract class AbsSchedulers<TASK_ENTITY extends AbsTaskEntity, TASK extends Abs
|| task.getTaskEntity().getEntity().getFailNum() > reTryNum) { || task.getTaskEntity().getEntity().getFailNum() > reTryNum) {
callback(FAIL, task); callback(FAIL, task);
mQueue.removeTaskFormQueue(task.getKey()); mQueue.removeTaskFormQueue(task.getKey());
startNextTask(); startNextTask(task);
ALog.d(TAG, "retry_next");
TEManager.getInstance().removeTEntity(task.getKey()); TEManager.getInstance().removeTEntity(task.getKey());
return; return;
} }
@ -302,8 +303,7 @@ abstract class AbsSchedulers<TASK_ENTITY extends AbsTaskEntity, TASK extends Abs
mQueue.reTryStart(task); mQueue.reTryStart(task);
} else { } else {
mQueue.removeTaskFormQueue(task.getKey()); mQueue.removeTaskFormQueue(task.getKey());
startNextTask(); startNextTask(task);
ALog.d(TAG, "retry_next_1");
TEManager.getInstance().removeTEntity(task.getKey()); TEManager.getInstance().removeTEntity(task.getKey());
} }
} }
@ -314,11 +314,14 @@ abstract class AbsSchedulers<TASK_ENTITY extends AbsTaskEntity, TASK extends Abs
/** /**
* 启动下一个任务条件任务停止取消下载任务完成 * 启动下一个任务条件任务停止取消下载任务完成
*/ */
private void startNextTask() { private void startNextTask(TASK oldTask) {
if (oldTask.getSchedulerType() == TaskSchedulerType.TYPE_STOP_NOT_NEXT) {
return;
}
TASK newTask = mQueue.getNextTask(); TASK newTask = mQueue.getNextTask();
if (newTask == null) { if (newTask == null) {
if (mQueue.getCurrentExePoolNum() == 0) { if (mQueue.getCurrentExePoolNum() == 0) {
ALog.i(TAG, "没有下一任务"); ALog.i(TAG, "没有等待中的任务");
} }
return; return;
} }

@ -59,10 +59,18 @@ public interface ISchedulerListener<TASK extends ITask> {
void onTaskCancel(TASK task); void onTaskCancel(TASK task);
/** /**
* 任务下载失败 * 任务失败
*
* @deprecated {@link #onTaskFail(ITask, Exception)}
*/ */
@Deprecated
void onTaskFail(TASK task); void onTaskFail(TASK task);
/**
* 任务失败
*/
void onTaskFail(TASK task, Exception e);
/** /**
* 任务完成 * 任务完成
*/ */

@ -80,6 +80,16 @@ abstract class BaseNormalTarget<TARGET extends AbsUploadTarget>
mEntity.save(); mEntity.save();
mTaskEntity.save(); mTaskEntity.save();
} }
if (mTaskEntity.getUrlEntity() != null && mTaskEntity.getUrlEntity().isFtps) {
if (TextUtils.isEmpty(mTaskEntity.getUrlEntity().storePath)) {
ALog.e(TAG, "证书路径为空");
return false;
}
if (TextUtils.isEmpty(mTaskEntity.getUrlEntity().keyAlias)) {
ALog.e(TAG, "证书别名为空");
return false;
}
}
return b; return b;
} }

@ -16,128 +16,27 @@
package com.arialyy.aria.core.upload; package com.arialyy.aria.core.upload;
import android.os.Handler; import android.os.Handler;
import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.common.BaseListener;
import com.arialyy.aria.core.common.TaskRecord; import com.arialyy.aria.core.common.TaskRecord;
import com.arialyy.aria.core.inf.AbsEntity;
import com.arialyy.aria.core.inf.AbsTask;
import com.arialyy.aria.core.inf.AbsTaskEntity;
import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.core.inf.IUploadListener; import com.arialyy.aria.core.inf.IUploadListener;
import com.arialyy.aria.core.scheduler.ISchedulers;
import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.orm.DbEntity;
import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.CommonUtil;
import java.lang.ref.WeakReference;
/** /**
* 下载监听类 * 下载监听类
*/ */
class BaseUListener<ENTITY extends AbsEntity, TASK_ENTITY extends AbsTaskEntity<ENTITY>, TASK extends AbsTask<TASK_ENTITY>> class BaseUListener extends BaseListener<UploadEntity, UploadTaskEntity, UploadTask>
implements IUploadListener { implements IUploadListener {
private WeakReference<Handler> outHandler;
private int RUN_SAVE_INTERVAL = 5 * 1000; //5s保存一次下载中的进度
private long mLastLen = 0; //上一次发送长度
private boolean isFirst = true;
protected ENTITY mEntity;
protected TASK_ENTITY mTaskEntity;
protected TASK mTask;
private boolean isConvertSpeed = false;
boolean isWait = false;
private long mLastSaveTime;
private long mUpdateInterval;
BaseUListener(TASK task, Handler outHandler) { BaseUListener(UploadTask task, Handler outHandler) {
this.outHandler = new WeakReference<>(outHandler); super(task, outHandler);
this.mTask = new WeakReference<>(task).get(); isConvertSpeed = manager.getUploadConfig().isConvertSpeed();
this.mEntity = this.mTask.getTaskEntity().getEntity();
this.mTaskEntity = this.mTask.getTaskEntity();
final AriaManager manager = AriaManager.getInstance(AriaManager.APP);
isConvertSpeed = manager.getDownloadConfig().isConvertSpeed();
mLastLen = mEntity.getCurrentProgress();
mLastSaveTime = System.currentTimeMillis();
mUpdateInterval = manager.getUploadConfig().getUpdateInterval(); mUpdateInterval = manager.getUploadConfig().getUpdateInterval();
} }
@Override public void onPre() { @Override
saveData(IEntity.STATE_PRE, -1); protected void saveData(int state, long location) {
sendInState2Target(ISchedulers.PRE);
}
@Override public void onStart(long startLocation) {
saveData(IEntity.STATE_RUNNING, startLocation);
sendInState2Target(ISchedulers.START);
}
@Override public void onResume(long resumeLocation) {
saveData(IEntity.STATE_RUNNING, resumeLocation);
sendInState2Target(ISchedulers.RESUME);
}
@Override public void onProgress(long currentLocation) {
mEntity.setCurrentProgress(currentLocation);
long speed = currentLocation - mLastLen;
if (isFirst) {
speed = 0;
isFirst = false;
}
handleSpeed(speed);
sendInState2Target(ISchedulers.RUNNING);
if (System.currentTimeMillis() - mLastSaveTime >= RUN_SAVE_INTERVAL) {
saveData(IEntity.STATE_RUNNING, currentLocation);
mLastSaveTime = System.currentTimeMillis();
}
mLastLen = currentLocation;
}
@Override public void onStop(long stopLocation) {
saveData(isWait ? IEntity.STATE_WAIT : IEntity.STATE_STOP, stopLocation);
handleSpeed(0);
sendInState2Target(ISchedulers.STOP);
}
@Override public void onCancel() {
saveData(IEntity.STATE_CANCEL, -1);
handleSpeed(0);
sendInState2Target(ISchedulers.CANCEL);
}
@Override public void onComplete() {
saveData(IEntity.STATE_COMPLETE, mEntity.getFileSize());
handleSpeed(0);
sendInState2Target(ISchedulers.COMPLETE);
}
@Override public void onFail(boolean needRetry) {
mEntity.setFailNum(mEntity.getFailNum() + 1);
saveData(IEntity.STATE_FAIL, mEntity.getCurrentProgress());
handleSpeed(0);
mTask.needRetry = needRetry;
sendInState2Target(ISchedulers.FAIL);
}
private void handleSpeed(long speed) {
if (mUpdateInterval != 1000) {
speed = speed * 1000 / mUpdateInterval;
}
if (isConvertSpeed) {
mEntity.setConvertSpeed(CommonUtil.formatFileSize(speed < 0 ? 0 : speed) + "/s");
}
mEntity.setSpeed(speed < 0 ? 0 : speed);
mEntity.setPercent((int) (mEntity.getFileSize() <= 0 ? 0
: mEntity.getCurrentProgress() * 100 / mEntity.getFileSize()));
}
/**
* 将任务状态发送给下载器
*
* @param state {@link ISchedulers#START}
*/
private void sendInState2Target(int state) {
if (outHandler.get() != null) {
outHandler.get().obtainMessage(state, mTask).sendToTarget();
}
}
private void saveData(int state, long location) {
mTaskEntity.setState(state); mTaskEntity.setState(state);
mEntity.setState(state); mEntity.setState(state);
mEntity.setComplete(state == IEntity.STATE_COMPLETE); mEntity.setComplete(state == IEntity.STATE_COMPLETE);
@ -146,7 +45,7 @@ class BaseUListener<ENTITY extends AbsEntity, TASK_ENTITY extends AbsTaskEntity<
TaskRecord record = TaskRecord record =
DbEntity.findFirst(TaskRecord.class, "TaskRecord.filePath=?", mTaskEntity.getKey()); DbEntity.findFirst(TaskRecord.class, "TaskRecord.filePath=?", mTaskEntity.getKey());
if (record != null) { if (record != null) {
CommonUtil.delTaskRecord(record, mTaskEntity.isRemoveFile(), (UploadEntity) mEntity); CommonUtil.delTaskRecord(record, mTaskEntity.isRemoveFile(), mEntity);
} }
} }
//else if (mEntity instanceof AbsGroupEntity) { //else if (mEntity instanceof AbsGroupEntity) {

@ -17,11 +17,14 @@ package com.arialyy.aria.core.upload;
import android.support.annotation.CheckResult; import android.support.annotation.CheckResult;
import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.core.FtpUrlEntity;
import com.arialyy.aria.core.command.normal.NormalCmdFactory; import com.arialyy.aria.core.command.normal.NormalCmdFactory;
import com.arialyy.aria.core.delegate.FtpDelegate; import com.arialyy.aria.core.common.ftp.FTPSConfig;
import com.arialyy.aria.core.common.ftp.FtpDelegate;
import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.inf.AbsTaskEntity;
import com.arialyy.aria.core.inf.IFtpTarget; import com.arialyy.aria.core.inf.IFtpTarget;
import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.CommonUtil;
import java.net.Proxy;
/** /**
* Created by Aria.Lao on 2017/7/27. * Created by Aria.Lao on 2017/7/27.
@ -29,7 +32,7 @@ import com.arialyy.aria.util.CommonUtil;
*/ */
public class FtpUploadTarget extends BaseNormalTarget<FtpUploadTarget> public class FtpUploadTarget extends BaseNormalTarget<FtpUploadTarget>
implements IFtpTarget<FtpUploadTarget> { implements IFtpTarget<FtpUploadTarget> {
private FtpDelegate<FtpUploadTarget, UploadEntity, UploadTaskEntity> mDelegate; private FtpDelegate<FtpUploadTarget> mDelegate;
private String mAccount, mUser, mPw; private String mAccount, mUser, mPw;
private boolean needLogin = false; private boolean needLogin = false;
@ -42,7 +45,7 @@ public class FtpUploadTarget extends BaseNormalTarget<FtpUploadTarget>
private void initTask(String filePath) { private void initTask(String filePath) {
initTarget(filePath); initTarget(filePath);
mTaskEntity.setRequestType(AbsTaskEntity.U_FTP); mTaskEntity.setRequestType(AbsTaskEntity.U_FTP);
mDelegate = new FtpDelegate<>(this, mTaskEntity); mDelegate = new FtpDelegate<>(this);
} }
/** /**
@ -51,7 +54,7 @@ public class FtpUploadTarget extends BaseNormalTarget<FtpUploadTarget>
public void add() { public void add() {
if (checkEntity()) { if (checkEntity()) {
AriaManager.getInstance(AriaManager.APP) AriaManager.getInstance(AriaManager.APP)
.setCmd(CommonUtil.createNormalCmd(mTargetName, mTaskEntity, NormalCmdFactory.TASK_CREATE, .setCmd(CommonUtil.createNormalCmd(mTaskEntity, NormalCmdFactory.TASK_CREATE,
checkTaskType())) checkTaskType()))
.exe(); .exe();
} }
@ -62,7 +65,16 @@ public class FtpUploadTarget extends BaseNormalTarget<FtpUploadTarget>
if (!b) { if (!b) {
return false; return false;
} }
mTaskEntity.setUrlEntity(CommonUtil.getFtpUrlInfo(mTempUrl)); FtpUrlEntity temp = mTaskEntity.getUrlEntity();
FtpUrlEntity newEntity = CommonUtil.getFtpUrlInfo(mTempUrl);
if (temp != null) { //处理FTPS的信息
newEntity.isFtps = temp.isFtps;
newEntity.storePass = temp.storePass;
newEntity.keyAlias = temp.keyAlias;
newEntity.protocol = temp.protocol;
newEntity.storePath = temp.storePath;
}
mTaskEntity.setUrlEntity(newEntity);
mTaskEntity.getUrlEntity().account = mAccount; mTaskEntity.getUrlEntity().account = mAccount;
mTaskEntity.getUrlEntity().user = mUser; mTaskEntity.getUrlEntity().user = mUser;
mTaskEntity.getUrlEntity().password = mPw; mTaskEntity.getUrlEntity().password = mPw;
@ -70,6 +82,21 @@ public class FtpUploadTarget extends BaseNormalTarget<FtpUploadTarget>
return true; return true;
} }
/**
* 是否是FTPS协议
* 如果是FTPS协议需要使用{@link FTPSConfig#setStorePath(String)} {@link FTPSConfig#setAlias(String)}
* 设置证书信息
*/
@CheckResult
public FTPSConfig<FtpUploadTarget> asFtps() {
if (mTaskEntity.getUrlEntity() == null) {
FtpUrlEntity urlEntity = new FtpUrlEntity();
urlEntity.isFtps = true;
mTaskEntity.setUrlEntity(urlEntity);
}
return new FTPSConfig<>(this);
}
@CheckResult @CheckResult
@Override public FtpUploadTarget charSet(String charSet) { @Override public FtpUploadTarget charSet(String charSet) {
return mDelegate.charSet(charSet); return mDelegate.charSet(charSet);
@ -89,4 +116,8 @@ public class FtpUploadTarget extends BaseNormalTarget<FtpUploadTarget>
mAccount = account; mAccount = account;
return this; return this;
} }
@Override public FtpUploadTarget setProxy(Proxy proxy) {
return mDelegate.setProxy(proxy);
}
} }

@ -16,6 +16,7 @@
package com.arialyy.aria.core.upload; package com.arialyy.aria.core.upload;
import com.arialyy.aria.core.inf.IUploadListener; import com.arialyy.aria.core.inf.IUploadListener;
import com.arialyy.aria.exception.BaseException;
/** /**
* Created by lyy on 2017/2/23. * Created by lyy on 2017/2/23.
@ -49,7 +50,7 @@ class UploadListener implements IUploadListener {
} }
@Override public void onFail(boolean needRetry) { @Override public void onFail(boolean needRetry, BaseException e) {
} }
} }

@ -20,6 +20,7 @@ import android.support.annotation.NonNull;
import android.text.TextUtils; import android.text.TextUtils;
import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.core.command.ICmd; import com.arialyy.aria.core.command.ICmd;
import com.arialyy.aria.core.command.normal.CancelAllCmd;
import com.arialyy.aria.core.command.normal.NormalCmdFactory; import com.arialyy.aria.core.command.normal.NormalCmdFactory;
import com.arialyy.aria.core.common.ProxyHelper; import com.arialyy.aria.core.common.ProxyHelper;
import com.arialyy.aria.core.inf.AbsReceiver; import com.arialyy.aria.core.inf.AbsReceiver;
@ -39,6 +40,16 @@ import java.util.Set;
public class UploadReceiver extends AbsReceiver { public class UploadReceiver extends AbsReceiver {
private static final String TAG = "UploadReceiver"; private static final String TAG = "UploadReceiver";
/**
* 设置最大上传速度单位kb
*
* @param maxSpeed 为0表示不限速
*/
public UploadReceiver setMaxSpeed(int maxSpeed) {
AriaManager.getInstance(AriaManager.APP).getUploadConfig().setMaxSpeed(maxSpeed);
return this;
}
/** /**
* 加载HTTP单文件上传任务 * 加载HTTP单文件上传任务
* *
@ -78,7 +89,7 @@ public class UploadReceiver extends AbsReceiver {
* @return {@code true}存在{@code false} 不存在 * @return {@code true}存在{@code false} 不存在
*/ */
public boolean taskExists(String filePath) { public boolean taskExists(String filePath) {
return DbEntity.findFirst(UploadEntity.class, "filePath=?", filePath) != null; return DbEntity.checkDataExist(UploadTaskEntity.class, "key=?", filePath);
} }
/** /**
@ -112,7 +123,7 @@ public class UploadReceiver extends AbsReceiver {
public void stopAllTask() { public void stopAllTask() {
AriaManager.getInstance(AriaManager.APP) AriaManager.getInstance(AriaManager.APP)
.setCmd(NormalCmdFactory.getInstance() .setCmd(NormalCmdFactory.getInstance()
.createCmd(targetName, new UploadTaskEntity(), NormalCmdFactory.TASK_STOP_ALL, .createCmd(new UploadTaskEntity(), NormalCmdFactory.TASK_STOP_ALL,
ICmd.TASK_TYPE_UPLOAD)) ICmd.TASK_TYPE_UPLOAD))
.exe(); .exe();
} }
@ -125,10 +136,11 @@ public class UploadReceiver extends AbsReceiver {
*/ */
public void removeAllTask(boolean removeFile) { public void removeAllTask(boolean removeFile) {
final AriaManager am = AriaManager.getInstance(AriaManager.APP); final AriaManager am = AriaManager.getInstance(AriaManager.APP);
CancelAllCmd cancelCmd =
am.setCmd(CommonUtil.createNormalCmd(targetName, new UploadTaskEntity(), (CancelAllCmd) CommonUtil.createNormalCmd(new UploadTaskEntity(),
NormalCmdFactory.TASK_CANCEL_ALL, ICmd.TASK_TYPE_UPLOAD)).exe(); NormalCmdFactory.TASK_CANCEL_ALL, ICmd.TASK_TYPE_UPLOAD);
cancelCmd.removeFile = removeFile;
am.setCmd(cancelCmd).exe();
Set<String> keys = am.getReceiver().keySet(); Set<String> keys = am.getReceiver().keySet();
for (String key : keys) { for (String key : keys) {
am.getReceiver().remove(key); am.getReceiver().remove(key);
@ -148,11 +160,17 @@ public class UploadReceiver extends AbsReceiver {
ALog.e(TAG, String.format("【%s】观察者为空", targetName)); ALog.e(TAG, String.format("【%s】观察者为空", targetName));
return; return;
} }
Set<String> cCounter = ProxyHelper.getInstance().uploadCounter; Set<Integer> set = ProxyHelper.getInstance().checkProxyType(obj.getClass());
if (cCounter != null && cCounter.contains(targetName)) { if (set != null && !set.isEmpty()) {
for (Integer type : set) {
if (type == ProxyHelper.PROXY_TYPE_UPLOAD) {
UploadSchedulers.getInstance().register(obj); UploadSchedulers.getInstance().register(obj);
} }
} }
} else {
ALog.i(TAG, "没有Aria的注解方法");
}
}
/** /**
* 取消注册如果是Activity或fragmentAria会界面销毁时自动调用该方法 * 取消注册如果是Activity或fragmentAria会界面销毁时自动调用该方法
@ -179,9 +197,14 @@ public class UploadReceiver extends AbsReceiver {
ALog.e(TAG, String.format("【%s】观察者为空", targetName)); ALog.e(TAG, String.format("【%s】观察者为空", targetName));
return; return;
} }
Set<String> dCounter = ProxyHelper.getInstance().uploadCounter;
if (dCounter != null && dCounter.contains(targetName)) { Set<Integer> set = ProxyHelper.getInstance().mProxyCache.get(obj.getClass().getName());
if (set != null) {
for (Integer integer : set) {
if (integer == ProxyHelper.PROXY_TYPE_UPLOAD) {
UploadSchedulers.getInstance().unRegister(obj); UploadSchedulers.getInstance().unRegister(obj);
} }
} }
}
}
} }

@ -17,10 +17,11 @@ package com.arialyy.aria.core.upload;
import android.support.annotation.CheckResult; import android.support.annotation.CheckResult;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import com.arialyy.aria.core.common.RequestEnum; import com.arialyy.aria.core.common.http.HttpHeaderDelegate;
import com.arialyy.aria.core.delegate.HttpHeaderDelegate; import com.arialyy.aria.core.common.http.PostDelegate;
import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.inf.AbsTaskEntity;
import com.arialyy.aria.core.inf.IHttpHeaderTarget; import com.arialyy.aria.core.inf.IHttpHeaderDelegate;
import java.net.Proxy;
import java.util.Map; import java.util.Map;
/** /**
@ -28,8 +29,8 @@ import java.util.Map;
* http 单文件上传 * http 单文件上传
*/ */
public class UploadTarget extends BaseNormalTarget<UploadTarget> public class UploadTarget extends BaseNormalTarget<UploadTarget>
implements IHttpHeaderTarget<UploadTarget> { implements IHttpHeaderDelegate<UploadTarget> {
private HttpHeaderDelegate<UploadTarget, UploadEntity, UploadTaskEntity> mDelegate; private HttpHeaderDelegate<UploadTarget> mDelegate;
UploadTarget(String filePath, String targetName) { UploadTarget(String filePath, String targetName) {
this.mTargetName = targetName; this.mTargetName = targetName;
@ -42,7 +43,14 @@ public class UploadTarget extends BaseNormalTarget<UploadTarget>
//http暂时不支持断点上传 //http暂时不支持断点上传
mTaskEntity.setSupportBP(false); mTaskEntity.setSupportBP(false);
mTaskEntity.setRequestType(AbsTaskEntity.U_HTTP); mTaskEntity.setRequestType(AbsTaskEntity.U_HTTP);
mDelegate = new HttpHeaderDelegate<>(this, mTaskEntity); mDelegate = new HttpHeaderDelegate<>(this);
}
/**
* Post处理
*/
public PostDelegate asPost() {
return new PostDelegate<>(this);
} }
/** /**
@ -86,8 +94,7 @@ public class UploadTarget extends BaseNormalTarget<UploadTarget>
return mDelegate.addHeaders(headers); return mDelegate.addHeaders(headers);
} }
@CheckResult @Override public UploadTarget setUrlProxy(Proxy proxy) {
@Override public UploadTarget setRequestMode(RequestEnum requestEnum) { return mDelegate.setUrlProxy(proxy);
return mDelegate.setRequestMode(requestEnum);
} }
} }

@ -15,7 +15,8 @@
*/ */
package com.arialyy.aria.core.upload.uploader; package com.arialyy.aria.core.upload.uploader;
import com.arialyy.aria.core.common.AbsFtpInfoThread; import aria.apache.commons.net.ftp.FTPFile;
import com.arialyy.aria.core.common.ftp.AbsFtpInfoThread;
import com.arialyy.aria.core.common.CompleteInfo; import com.arialyy.aria.core.common.CompleteInfo;
import com.arialyy.aria.core.common.OnFileInfoCallback; import com.arialyy.aria.core.common.OnFileInfoCallback;
import com.arialyy.aria.core.common.TaskRecord; import com.arialyy.aria.core.common.TaskRecord;
@ -25,7 +26,6 @@ import com.arialyy.aria.core.upload.UploadTaskEntity;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.DbHelper; import com.arialyy.aria.util.DbHelper;
import java.util.ArrayList; import java.util.ArrayList;
import org.apache.commons.net.ftp.FTPFile;
/** /**
* Created by Aria.Lao on 2017/9/26. * Created by Aria.Lao on 2017/9/26.

@ -15,20 +15,21 @@
*/ */
package com.arialyy.aria.core.upload.uploader; package com.arialyy.aria.core.upload.uploader;
import com.arialyy.aria.core.AriaManager; import aria.apache.commons.net.ftp.FTPClient;
import com.arialyy.aria.core.common.AbsFtpThreadTask; import aria.apache.commons.net.ftp.FTPReply;
import aria.apache.commons.net.ftp.OnFtpInputStreamListener;
import com.arialyy.aria.core.common.StateConstance; import com.arialyy.aria.core.common.StateConstance;
import com.arialyy.aria.core.common.SubThreadConfig; import com.arialyy.aria.core.common.SubThreadConfig;
import com.arialyy.aria.core.common.ftp.AbsFtpThreadTask;
import com.arialyy.aria.core.inf.IEventListener; import com.arialyy.aria.core.inf.IEventListener;
import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.upload.UploadEntity;
import com.arialyy.aria.core.upload.UploadTaskEntity; import com.arialyy.aria.core.upload.UploadTaskEntity;
import com.arialyy.aria.exception.AriaIOException;
import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.BufferedRandomAccessFile; import com.arialyy.aria.util.BufferedRandomAccessFile;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.commons.net.ftp.OnFtpInputStreamListener;
/** /**
* Created by Aria.Lao on 2017/7/28. * Created by Aria.Lao on 2017/7/28.
@ -41,14 +42,18 @@ class FtpThreadTask extends AbsFtpThreadTask<UploadEntity, UploadTaskEntity> {
FtpThreadTask(StateConstance constance, IEventListener listener, FtpThreadTask(StateConstance constance, IEventListener listener,
SubThreadConfig<UploadTaskEntity> info) { SubThreadConfig<UploadTaskEntity> info) {
super(constance, listener, info); super(constance, listener, info);
AriaManager manager = AriaManager.getInstance(AriaManager.APP); mConnectTimeOut = mAridManager.getUploadConfig().getConnectTimeOut();
mConnectTimeOut = manager.getUploadConfig().getConnectTimeOut(); mReadTimeOut = mAridManager.getUploadConfig().getIOTimeOut();
mReadTimeOut = manager.getUploadConfig().getIOTimeOut(); mBufSize = mAridManager.getUploadConfig().getBuffSize();
mBufSize = manager.getUploadConfig().getBuffSize(); isNotNetRetry = mAridManager.getUploadConfig().isNotNetRetry();
isNotNetRetry = manager.getUploadConfig().isNotNetRetry();
} }
@Override public void run() { @Override public int getMaxSpeed() {
return mAridManager.getUploadConfig().getMaxSpeed();
}
@Override public FtpThreadTask call() throws Exception {
super.call();
//当前子线程的下载位置 //当前子线程的下载位置
mChildCurrentLocation = mConfig.START_LOCATION; mChildCurrentLocation = mConfig.START_LOCATION;
FTPClient client = null; FTPClient client = null;
@ -58,16 +63,21 @@ class FtpThreadTask extends AbsFtpThreadTask<UploadEntity, UploadTaskEntity> {
String.format("任务【%s】线程__%s__开始上传【开始位置 : %s,结束位置:%s】", mConfig.TEMP_FILE.getName(), String.format("任务【%s】线程__%s__开始上传【开始位置 : %s,结束位置:%s】", mConfig.TEMP_FILE.getName(),
mConfig.THREAD_ID, mConfig.START_LOCATION, mConfig.END_LOCATION)); mConfig.THREAD_ID, mConfig.START_LOCATION, mConfig.END_LOCATION));
client = createClient(); client = createClient();
if (client == null) return; if (client == null) {
return this;
}
initPath(); initPath();
client.makeDirectory(dir); client.makeDirectory(dir);
client.changeWorkingDirectory(dir); client.changeWorkingDirectory(dir);
client.setRestartOffset(mConfig.START_LOCATION); client.setRestartOffset(mConfig.START_LOCATION);
int reply = client.getReplyCode(); int reply = client.getReplyCode();
if (!FTPReply.isPositivePreliminary(reply) && reply != FTPReply.FILE_ACTION_OK) { if (!FTPReply.isPositivePreliminary(reply) && reply != FTPReply.FILE_ACTION_OK) {
fail(mChildCurrentLocation, "上传失败,错误码为:" + reply + ",msg:" + client.getReplyString(), null); fail(mChildCurrentLocation,
new AriaIOException(TAG,
String.format("文件上传错误,错误码为:%s, msg:%s, filePath: %s", reply,
client.getReplyString(), mEntity.getFilePath())));
client.disconnect(); client.disconnect();
return; return this;
} }
file = new BufferedRandomAccessFile(mConfig.TEMP_FILE, "rwd", mBufSize); file = new BufferedRandomAccessFile(mConfig.TEMP_FILE, "rwd", mBufSize);
@ -77,7 +87,7 @@ class FtpThreadTask extends AbsFtpThreadTask<UploadEntity, UploadTaskEntity> {
} }
upload(client, file); upload(client, file);
if (isBreak()) { if (isBreak()) {
return; return this;
} }
ALog.i(TAG, ALog.i(TAG,
String.format("任务【%s】线程__%s__上传完毕", mConfig.TEMP_FILE.getName(), mConfig.THREAD_ID)); String.format("任务【%s】线程__%s__上传完毕", mConfig.TEMP_FILE.getName(), mConfig.THREAD_ID));
@ -90,12 +100,14 @@ class FtpThreadTask extends AbsFtpThreadTask<UploadEntity, UploadTaskEntity> {
} }
if (STATE.isFail()) { if (STATE.isFail()) {
STATE.isRunning = false; STATE.isRunning = false;
mListener.onFail(false); mListener.onFail(false, new TaskException(TAG,
String.format("上传失败,filePath: %s, uploadUrl: %s", mEntity.getFilePath(), mConfig.URL)));
} }
} catch (IOException e) { } catch (IOException e) {
fail(mChildCurrentLocation, String.format("上传失败【%s】", mConfig.URL), e); fail(mChildCurrentLocation, new AriaIOException(TAG,
String.format("上传失败,filePath: %s, uploadUrl: %s", mEntity.getFilePath(), mConfig.URL)));
} catch (Exception e) { } catch (Exception e) {
fail(mChildCurrentLocation, "获取流失败", e); fail(mChildCurrentLocation, new AriaIOException(TAG, null, e));
} finally { } finally {
try { try {
if (file != null) { if (file != null) {
@ -108,6 +120,7 @@ class FtpThreadTask extends AbsFtpThreadTask<UploadEntity, UploadTaskEntity> {
e.printStackTrace(); e.printStackTrace();
} }
} }
return this;
} }
private void initPath() throws UnsupportedEncodingException { private void initPath() throws UnsupportedEncodingException {
@ -122,28 +135,32 @@ class FtpThreadTask extends AbsFtpThreadTask<UploadEntity, UploadTaskEntity> {
throws IOException { throws IOException {
try { try {
ALog.d(TAG, String.format("remotePath【%s】", remotePath)); ALog.d(TAG, String.format("remotePath: %s", remotePath));
client.storeFile(remotePath, new FtpFISAdapter(bis), new OnFtpInputStreamListener() { client.storeFile(remotePath, new FtpFISAdapter(bis), new OnFtpInputStreamListener() {
boolean isStoped = false; boolean isStoped = false;
@Override public void onFtpInputStream(FTPClient client, long totalBytesTransferred, @Override public void onFtpInputStream(FTPClient client, long totalBytesTransferred,
int bytesTransferred, long streamSize) { int bytesTransferred, long streamSize) {
if (isBreak() && !isStoped) {
try { try {
if (isBreak() && !isStoped) {
isStoped = true; isStoped = true;
client.abor(); client.abor();
} catch (IOException e) {
e.printStackTrace();
} }
if (mSpeedBandUtil != null) {
mSpeedBandUtil.limitNextBytes(bytesTransferred);
} }
progress(bytesTransferred); progress(bytesTransferred);
} catch (IOException e) {
e.printStackTrace();
}
} }
}); });
} catch (IOException e) { } catch (IOException e) {
if (e.getMessage().contains("IOException caught while copying")) { if (e.getMessage().contains("AriaIOException caught while copying")) {
e.printStackTrace(); e.printStackTrace();
} else { } else {
fail(mChildCurrentLocation, "上传失败", e); fail(mChildCurrentLocation,
new AriaIOException(TAG, String.format("上传失败,filePath: %s", mEntity.getFilePath()), e));
} }
} }
@ -151,8 +168,9 @@ class FtpThreadTask extends AbsFtpThreadTask<UploadEntity, UploadTaskEntity> {
if (!FTPReply.isPositiveCompletion(reply)) { if (!FTPReply.isPositiveCompletion(reply)) {
if (reply != FTPReply.TRANSFER_ABORTED) { if (reply != FTPReply.TRANSFER_ABORTED) {
fail(mChildCurrentLocation, fail(mChildCurrentLocation,
String.format("上传文件错误,错误码为:%s,msg:%s", reply, client.getReplyString()), new AriaIOException(TAG,
null); String.format("文件上传错误,错误码为:%s, msg:%s, filePath: %s", reply,
client.getReplyString(), mEntity.getFilePath())));
} }
if (client.isConnected()) { if (client.isConnected()) {
client.disconnect(); client.disconnect();

@ -15,13 +15,14 @@
*/ */
package com.arialyy.aria.core.upload.uploader; package com.arialyy.aria.core.upload.uploader;
import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.core.common.AbsThreadTask; import com.arialyy.aria.core.common.AbsThreadTask;
import com.arialyy.aria.core.common.StateConstance; import com.arialyy.aria.core.common.StateConstance;
import com.arialyy.aria.core.common.SubThreadConfig; import com.arialyy.aria.core.common.SubThreadConfig;
import com.arialyy.aria.core.inf.IUploadListener; import com.arialyy.aria.core.inf.IUploadListener;
import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.upload.UploadEntity;
import com.arialyy.aria.core.upload.UploadTaskEntity; import com.arialyy.aria.core.upload.UploadTaskEntity;
import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
@ -52,19 +53,20 @@ class HttpThreadTask extends AbsThreadTask<UploadEntity, UploadTaskEntity> {
HttpThreadTask(StateConstance constance, IUploadListener listener, HttpThreadTask(StateConstance constance, IUploadListener listener,
SubThreadConfig<UploadTaskEntity> uploadInfo) { SubThreadConfig<UploadTaskEntity> uploadInfo) {
super(constance, listener, uploadInfo); super(constance, listener, uploadInfo);
AriaManager manager = AriaManager.getInstance(AriaManager.APP); mConnectTimeOut = mAridManager.getUploadConfig().getConnectTimeOut();
mConnectTimeOut = manager.getUploadConfig().getConnectTimeOut(); mReadTimeOut = mAridManager.getUploadConfig().getIOTimeOut();
mReadTimeOut = manager.getUploadConfig().getIOTimeOut(); mBufSize = mAridManager.getUploadConfig().getBuffSize();
mBufSize = manager.getUploadConfig().getBuffSize(); isNotNetRetry = mAridManager.getUploadConfig().isNotNetRetry();
isNotNetRetry = manager.getUploadConfig().isNotNetRetry();
} }
@Override public void run() { @Override public HttpThreadTask call() throws Exception {
super.call();
File uploadFile = new File(mEntity.getFilePath()); File uploadFile = new File(mEntity.getFilePath());
if (!uploadFile.exists()) { if (!uploadFile.exists()) {
ALog.e(TAG, String.format("【%s】,文件不存在。", mEntity.getFilePath())); fail(new TaskException(TAG,
fail(); String.format("上传失败,文件不存在;filePath: %s, url: %s", mEntity.getFilePath(),
return; mEntity.getUrl())));
return this;
} }
mListener.onPre(); mListener.onPre();
URL url; URL url;
@ -103,13 +105,15 @@ class HttpThreadTask extends AbsThreadTask<UploadEntity, UploadTaskEntity> {
mListener.onComplete(); mListener.onComplete();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
fail(); fail(new TaskException(TAG,
String.format("上传失败,filePath: %s, url: %s", mEntity.getFilePath(), mEntity.getUrl()), e));
} }
return this;
} }
private void fail() { private void fail(BaseException e1) {
try { try {
mListener.onFail(true); mListener.onFail(true, e1);
STATE.isRunning = false; STATE.isRunning = false;
if (mOutputStream != null) { if (mOutputStream != null) {
mOutputStream.close(); mOutputStream.close();
@ -167,6 +171,9 @@ class HttpThreadTask extends AbsThreadTask<UploadEntity, UploadTaskEntity> {
if (STATE.isCancel) { if (STATE.isCancel) {
break; break;
} }
if (mSpeedBandUtil != null) {
mSpeedBandUtil.limitNextBytes(bytesRead);
}
} }
mOutputStream.flush(); mOutputStream.flush();
@ -198,7 +205,7 @@ class HttpThreadTask extends AbsThreadTask<UploadEntity, UploadTaskEntity> {
if (status == HttpURLConnection.HTTP_OK) { if (status == HttpURLConnection.HTTP_OK) {
BufferedReader reader = new BufferedReader(new InputStreamReader(mHttpConn.getInputStream())); BufferedReader reader = new BufferedReader(new InputStreamReader(mHttpConn.getInputStream()));
String line; String line;
while ((line = reader.readLine()) != null) { while (isLive() && (line = reader.readLine()) != null) {
response.append(line); response.append(line);
} }
reader.close(); reader.close();
@ -212,4 +219,8 @@ class HttpThreadTask extends AbsThreadTask<UploadEntity, UploadTaskEntity> {
mOutputStream.close(); mOutputStream.close();
return response.toString(); return response.toString();
} }
@Override public int getMaxSpeed() {
return mAridManager.getUploadConfig().getMaxSpeed();
}
} }

@ -22,6 +22,8 @@ import com.arialyy.aria.core.inf.AbsTaskEntity;
import com.arialyy.aria.core.inf.IUploadListener; import com.arialyy.aria.core.inf.IUploadListener;
import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.upload.UploadEntity;
import com.arialyy.aria.core.upload.UploadTaskEntity; import com.arialyy.aria.core.upload.UploadTaskEntity;
import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.util.CheckUtil; import com.arialyy.aria.util.CheckUtil;
/** /**
@ -60,8 +62,8 @@ public class SimpleUploadUtil implements IUtil, Runnable {
} }
} }
@Override public void onFail(String url, String errorMsg, boolean needRetry) { @Override public void onFail(String url, BaseException e, boolean needRetry) {
mListener.onFail(needRetry); mListener.onFail(needRetry, e);
} }
}).start(); }).start();
break; break;
@ -99,7 +101,7 @@ public class SimpleUploadUtil implements IUtil, Runnable {
mUploader.cancel(); mUploader.cancel();
} }
@Override public void setMaxSpeed(double maxSpeed) { @Override public void setMaxSpeed(int speed) {
mUploader.setMaxSpeed(maxSpeed); mUploader.setMaxSpeed(speed);
} }
} }

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save