diff --git a/Aria/build.gradle b/Aria/build.gradle index 7b8287a2..3d928fe3 100644 --- a/Aria/build.gradle +++ b/Aria/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.library' android { compileSdkVersion 23 - buildToolsVersion '25.0.2' + buildToolsVersion '25.0.3' defaultConfig { minSdkVersion 9 @@ -22,8 +22,7 @@ dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.1.1' - compile project(':AriaCompiler') -// compile project(':AriaAnnotations') +// compile project(':AriaCompiler') + compile project(':AriaAnnotations') //gradle 打包时打开这个 } -//apply from: 'bintray-release.gradle' -//apply from: 'jcenter.gradle' +apply from: 'bintray-release.gradle' diff --git a/Aria/jcenter.gradle b/Aria/jcenter.gradle index efbb3ea1..8ce3129a 100644 --- a/Aria/jcenter.gradle +++ b/Aria/jcenter.gradle @@ -76,7 +76,7 @@ artifacts { //################################# jcenter 上传配置 start ######################################### bintray { // user = hasProperty("bintrayUser") ? getProperty("bintrayUser") : getProperty("BINTRAY_USER") -// key = hasProperty("bintrayKey") ? getProperty("bintrayKey") : getProperty("BINTRAY_KEY") +// groupName = hasProperty("bintrayKey") ? getProperty("bintrayKey") : getProperty("BINTRAY_KEY") user = BINTRAY_USER key = BINTRAY_KEY configurations = ['archives'] diff --git a/Aria/src/main/java/com/arialyy/aria/core/Aria.java b/Aria/src/main/java/com/arialyy/aria/core/Aria.java index 27e19d2e..5dc70cfe 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/Aria.java +++ b/Aria/src/main/java/com/arialyy/aria/core/Aria.java @@ -59,62 +59,6 @@ import com.arialyy.aria.core.upload.UploadTask; * */ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) public class Aria { - /** - * 不支持断点 - */ - public static final String ACTION_SUPPORT_BREAK_POINT = "ACTION_SUPPORT_BREAK_POINT"; - /** - * 预处理完成 - */ - public static final String ACTION_PRE = "ACTION_PRE"; - /** - * 下载开始前事件 - */ - public static final String ACTION_POST_PRE = "ACTION_POST_PRE"; - /** - * 开始下载事件 - */ - public static final String ACTION_START = "ACTION_START"; - /** - * 恢复下载事件 - */ - public static final String ACTION_RESUME = "ACTION_RESUME"; - /** - * 正在下载事件 - */ - public static final String ACTION_RUNNING = "ACTION_RUNNING"; - /** - * 停止下载事件 - */ - public static final String ACTION_STOP = "ACTION_STOP"; - /** - * 取消下载事件 - */ - public static final String ACTION_CANCEL = "ACTION_CANCEL"; - /** - * 下载完成事件 - */ - public static final String ACTION_COMPLETE = "ACTION_COMPLETE"; - /** - * 下载失败事件 - */ - public static final String ACTION_FAIL = "ACTION_FAIL"; - /** - * 下载实体 - */ - public static final String DOWNLOAD_ENTITY = "DOWNLOAD_ENTITY"; - /** - * 上传实体 - */ - public static final String UPLOAD_ENTITY = "UPLOAD_ENTITY"; - /** - * 位置 - */ - public static final String CURRENT_LOCATION = "CURRENT_LOCATION"; - /** - * 速度 - */ - public static final String CURRENT_SPEED = "CURRENT_SPEED"; private Aria() { } diff --git a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java index dacb0aea..41bb1525 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java +++ b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java @@ -29,14 +29,13 @@ import android.text.TextUtils; import android.util.Log; import android.widget.PopupWindow; import com.arialyy.aria.core.download.DownloadReceiver; -import com.arialyy.aria.core.inf.ICmd; +import com.arialyy.aria.core.command.ICmd; import com.arialyy.aria.core.inf.IReceiver; import com.arialyy.aria.core.upload.UploadReceiver; import com.arialyy.aria.orm.DbUtil; import com.arialyy.aria.util.CommonUtil; import java.io.File; import java.io.IOException; -import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -86,6 +85,26 @@ import org.xml.sax.SAXException; return mReceivers; } + /** + * 设置上传任务的执行队列类型 + * + * @param mod {@link com.arialyy.aria.core.QueueMod} + */ + public AriaManager setUploadQueueMod(QueueMod mod) { + mUConfig.setQueueMod(mod.tag); + return this; + } + + /** + * 设置下载任务的执行队列类型 + * + * @param mod {@link com.arialyy.aria.core.QueueMod} + */ + public AriaManager setDownloadQueueMod(QueueMod mod) { + mDConfig.setQueueMod(mod.tag); + return this; + } + /** * 如果需要在代码中修改下载配置,请使用以下方法 *
diff --git a/Aria/src/main/java/com/arialyy/aria/core/ConfigHelper.java b/Aria/src/main/java/com/arialyy/aria/core/ConfigHelper.java
index e4c94262..d0df8509 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/ConfigHelper.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/ConfigHelper.java
@@ -54,9 +54,6 @@ class ConfigHelper extends DefaultHandler {
         case "threadNum":
           loadThreadNum(value);
           break;
-        case "openBroadcast":
-          loadBroadcast(value);
-          break;
         case "maxTaskNum":
           loadMaxQueue(value);
           break;
@@ -86,16 +83,33 @@ class ConfigHelper extends DefaultHandler {
         case "maxSpeed":
           loadMaxSpeed(value);
           break;
+        case "queueMod":
+          loadQueueMod(value);
+          break;
       }
     }
   }
 
+  private void loadQueueMod(String value) {
+    String mod = "now";
+    if (!TextUtils.isEmpty(value) && (value.equalsIgnoreCase("now") || value.equalsIgnoreCase(
+        "wait"))) {
+      mod = value;
+    }
+    if (isDownloadConfig) {
+      mDownloadConfig.queueMod = mod;
+    }
+    if (isUploadConfig) {
+      mUploadConfig.queueMod = mod;
+    }
+  }
+
   private void loadMaxSpeed(String value) {
     double maxSpeed = 0.0;
     if (!TextUtils.isEmpty(value)) {
       maxSpeed = Double.parseDouble(value);
     }
-    if (isDownloadConfig){
+    if (isDownloadConfig) {
       mDownloadConfig.msxSpeed = maxSpeed;
     }
   }
@@ -207,16 +221,6 @@ class ConfigHelper extends DefaultHandler {
     }
   }
 
-  private void loadBroadcast(String value) {
-    boolean open = Boolean.parseBoolean(value);
-    if (isDownloadConfig) {
-      mDownloadConfig.isOpenBreadCast = open;
-    }
-    if (isUploadConfig) {
-      mUploadConfig.isOpenBreadCast = open;
-    }
-  }
-
   private void loadThreadNum(String value) {
     int num = 3;
     if (!TextUtils.isEmpty(value)) {
diff --git a/Aria/src/main/java/com/arialyy/aria/core/QueueMod.java b/Aria/src/main/java/com/arialyy/aria/core/QueueMod.java
new file mode 100644
index 00000000..db9e7b6c
--- /dev/null
+++ b/Aria/src/main/java/com/arialyy/aria/core/QueueMod.java
@@ -0,0 +1,31 @@
+package com.arialyy.aria.core;
+
+/**
+ * Created by Aria.Lao on 2017/6/21.
+ * 执行队列类型
+ */
+public enum QueueMod {
+  /**
+   * 等待模式,
+   * 如果执行队列已经满了,再对其它任务(TASK_A)使用start命令执行任务时
+   * 1、TASK_A添加到缓存队列中,当执行队列中的任务完成时,系统会将自动执行缓存队列中的TASK_A
+   * 2、如果再次对TASK_A使用start命令,TASK_A将会立刻执行
+   */
+  WAIT("wait"),
+
+  /**
+   * 立刻执行模式
+   * 如果执行队列已经满了,再次使用start命令执行任务时,该任务会添加到执行队列队尾,而原来执行队列的队首任务会停止
+   */
+  NOW("now");
+
+  String tag;
+
+  public String getTag() {
+    return tag;
+  }
+
+  QueueMod(String tag) {
+    this.tag = tag;
+  }
+}
diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/AbsCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/AbsCmd.java
index 1107dbfd..d337a453 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/command/AbsCmd.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/command/AbsCmd.java
@@ -16,43 +16,20 @@
 
 package com.arialyy.aria.core.command;
 
-import com.arialyy.aria.core.download.DownloadTaskEntity;
 import com.arialyy.aria.core.inf.AbsTaskEntity;
-import com.arialyy.aria.core.queue.DownloadTaskQueue;
 import com.arialyy.aria.core.queue.ITaskQueue;
-import com.arialyy.aria.core.inf.ICmd;
-import com.arialyy.aria.core.queue.UploadTaskQueue;
-import com.arialyy.aria.core.upload.UploadTaskEntity;
-import com.arialyy.aria.util.CheckUtil;
-import com.arialyy.aria.util.CommonUtil;
 
 /**
- * Created by lyy on 2016/8/22.
- * 下载命令
+ * Created by AriaL on 2017/6/29.
  */
-public abstract class AbsCmd implements ICmd {
-  ITaskQueue mQueue;
-  T mEntity;
-  String TAG;
-  String mTargetName;
+public abstract class AbsCmd implements ICmd{
+  protected ITaskQueue mQueue;
+  protected T mTaskEntity;
+  protected String TAG;
+  protected String mTargetName;
   /**
-   * 能否执行命令
+   * 是否是下载任务的命令
+   * {@code true} 下载任务的命令,{@code false} 上传任务的命令
    */
-  boolean canExeCmd = true;
-
-  /**
-   * @param targetName 产生任务的对象名
-   */
-  AbsCmd(String targetName, T entity) {
-    canExeCmd = CheckUtil.checkCmdEntity(entity,
-        !(this instanceof CancelCmd) || !(this instanceof StopCmd));
-    mTargetName = targetName;
-    mEntity = entity;
-    TAG = CommonUtil.getClassName(this);
-    if (entity instanceof DownloadTaskEntity) {
-      mQueue = DownloadTaskQueue.getInstance();
-    } else if (entity instanceof UploadTaskEntity) {
-      mQueue = UploadTaskQueue.getInstance();
-    }
-  }
-}
\ No newline at end of file
+  protected boolean isDownloadCmd = true;
+}
diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/AbsCmdFactory.java b/Aria/src/main/java/com/arialyy/aria/core/command/AbsCmdFactory.java
new file mode 100644
index 00000000..8ed0d498
--- /dev/null
+++ b/Aria/src/main/java/com/arialyy/aria/core/command/AbsCmdFactory.java
@@ -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.command;
+
+import com.arialyy.aria.core.inf.AbsTaskEntity;
+
+/**
+ * Created by AriaL on 2017/6/29.
+ * 抽象命令工厂
+ */
+public abstract class AbsCmdFactory {
+
+  /**
+   * @param target 创建任务的对象
+   * @param entity 下载实体
+   */
+  public abstract  CMD createCmd(String target, T entity, int type);
+}
diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/ICmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/ICmd.java
similarity index 90%
rename from Aria/src/main/java/com/arialyy/aria/core/inf/ICmd.java
rename to Aria/src/main/java/com/arialyy/aria/core/command/ICmd.java
index 482cfb0c..dc938037 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/inf/ICmd.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/command/ICmd.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.arialyy.aria.core.inf;
+package com.arialyy.aria.core.command;
 
 /**
  * Created by lyy on 2017/2/9.
@@ -23,5 +23,5 @@ public interface ICmd {
   /**
    * 执行命令
    */
-  public abstract void executeCmd();
+  void executeCmd();
 }
diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/group/AbsGroupCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/group/AbsGroupCmd.java
new file mode 100644
index 00000000..5d24fa67
--- /dev/null
+++ b/Aria/src/main/java/com/arialyy/aria/core/command/group/AbsGroupCmd.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.arialyy.aria.core.command.group;
+
+import com.arialyy.aria.core.command.AbsCmd;
+import com.arialyy.aria.core.download.DownloadGroupTaskEntity;
+import com.arialyy.aria.core.download.DownloadTaskEntity;
+import com.arialyy.aria.core.inf.AbsTaskEntity;
+import com.arialyy.aria.core.queue.DownloadGroupTaskQueue;
+import com.arialyy.aria.core.queue.DownloadTaskQueue;
+import com.arialyy.aria.core.queue.UploadTaskQueue;
+import com.arialyy.aria.core.upload.UploadTaskEntity;
+import com.arialyy.aria.util.CommonUtil;
+
+/**
+ * Created by AriaL on 2017/6/29.
+ * 任务组命令
+ */
+abstract class AbsGroupCmd extends AbsCmd {
+
+  /**
+   * @param targetName 创建任务的对象名
+   */
+  AbsGroupCmd(String targetName, T entity) {
+    mTargetName = targetName;
+    mTaskEntity = entity;
+    TAG = CommonUtil.getClassName(this);
+    if (entity instanceof DownloadGroupTaskEntity) {
+      mQueue = DownloadGroupTaskQueue.getInstance();
+      isDownloadCmd = true;
+    }
+  }
+}
diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/StartCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/group/GroupCancelCmd.java
similarity index 56%
rename from Aria/src/main/java/com/arialyy/aria/core/command/StartCmd.java
rename to Aria/src/main/java/com/arialyy/aria/core/command/group/GroupCancelCmd.java
index 82821ea8..5b6e5cf9 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/command/StartCmd.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/command/group/GroupCancelCmd.java
@@ -13,34 +13,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.arialyy.aria.core.command.group;
 
-package com.arialyy.aria.core.command;
-
-import android.text.TextUtils;
-import com.arialyy.aria.core.inf.ITask;
 import com.arialyy.aria.core.inf.AbsTaskEntity;
 
 /**
- * Created by lyy on 2016/8/22.
- * 开始命令
+ * Created by AriaL on 2017/6/29.
+ * 删除任务组
  */
-class StartCmd extends AbsCmd {
-
-  StartCmd(String targetName, T entity) {
+class GroupCancelCmd extends AbsGroupCmd {
+  /**
+   * @param targetName 创建任务的对象名
+   */
+  GroupCancelCmd(String targetName, T entity) {
     super(targetName, entity);
   }
 
   @Override public void executeCmd() {
-    if (!canExeCmd) return;
-    ITask task = mQueue.getTask(mEntity.getEntity());
-    if (task == null) {
-      task = mQueue.createTask(mTargetName, mEntity);
-    }
-    if (task != null) {
-      if (!TextUtils.isEmpty(mTargetName)) {
-        task.setTargetName(mTargetName);
-      }
-      mQueue.startTask(task);
-    }
+
   }
-}
\ No newline at end of file
+}
diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/group/GroupCmdFactory.java b/Aria/src/main/java/com/arialyy/aria/core/command/group/GroupCmdFactory.java
new file mode 100644
index 00000000..a818d688
--- /dev/null
+++ b/Aria/src/main/java/com/arialyy/aria/core/command/group/GroupCmdFactory.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.arialyy.aria.core.command.group;
+
+import com.arialyy.aria.core.AriaManager;
+import com.arialyy.aria.core.command.AbsCmdFactory;
+import com.arialyy.aria.core.inf.AbsTaskEntity;
+
+/**
+ * Created by AriaL on 2017/6/29.
+ */
+class GroupCmdFactory extends AbsCmdFactory {
+  /**
+   * 启动任务
+   */
+  public static final int TASK_START = 0xa1;
+  /**
+   * 停止任务
+   */
+  public static final int TASK_STOP = 0xa2;
+  /**
+   * 取消任务
+   */
+  public static final int TASK_CANCEL = 0xa3;
+
+  private static volatile GroupCmdFactory INSTANCE = null;
+
+  private GroupCmdFactory() {
+
+  }
+
+  public static GroupCmdFactory getInstance() {
+    if (INSTANCE == null) {
+      synchronized (AriaManager.LOCK) {
+        INSTANCE = new GroupCmdFactory();
+      }
+    }
+    return INSTANCE;
+  }
+
+  /**
+   * @param target 创建任务的对象
+   * @param entity 下载实体
+   * @param type 命令类型{@link #TASK_START}、{@link #TASK_CANCEL}、{@link #TASK_STOP}
+   */
+  public  AbsGroupCmd createCmd(String target, T entity, int type) {
+    switch (type) {
+      case TASK_START:
+        return new GroupStartCmd<>(target, entity);
+      case TASK_STOP:
+        return new GroupStopCmd<>(target, entity);
+      case TASK_CANCEL:
+        return new GroupCancelCmd<>(target, entity);
+      default:
+        return null;
+    }
+  }
+}
diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/group/GroupStartCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/group/GroupStartCmd.java
new file mode 100644
index 00000000..4fde6115
--- /dev/null
+++ b/Aria/src/main/java/com/arialyy/aria/core/command/group/GroupStartCmd.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.arialyy.aria.core.command.group;
+
+import android.text.TextUtils;
+import com.arialyy.aria.core.AriaManager;
+import com.arialyy.aria.core.QueueMod;
+import com.arialyy.aria.core.inf.AbsGroupTask;
+import com.arialyy.aria.core.inf.AbsTaskEntity;
+import com.arialyy.aria.core.inf.IEntity;
+
+/**
+ * Created by AriaL on 2017/6/29.
+ * 任务组开始命令,该命令负责开始下载或恢复下载的操作
+ */
+class GroupStartCmd extends AbsGroupCmd {
+  /**
+   * @param targetName 创建任务的对象名
+   */
+  GroupStartCmd(String targetName, T entity) {
+    super(targetName, entity);
+  }
+
+  @Override public void executeCmd() {
+    String mod;
+    int maxTaskNum;
+    AriaManager manager = AriaManager.getInstance(AriaManager.APP);
+    if (isDownloadCmd) {
+      mod = manager.getDownloadConfig().getQueueMod();
+      maxTaskNum = manager.getDownloadConfig().getMaxTaskNum();
+    } else {
+      mod = manager.getUploadConfig().getQueueMod();
+      maxTaskNum = manager.getUploadConfig().getMaxTaskNum();
+    }
+
+    AbsGroupTask task = (AbsGroupTask) mQueue.getTask(mTaskEntity.getEntity());
+    if (task == null) {
+      task = (AbsGroupTask) mQueue.createTask(mTargetName, mTaskEntity);
+      if (!TextUtils.isEmpty(mTargetName)) {
+        task.setTargetName(mTargetName);
+      }
+      // 任务不存在时,根据配置不同,对任务执行操作
+      if (mod.equals(QueueMod.NOW.getTag())) {
+        mQueue.startTask(task);
+      } else if (mod.equals(QueueMod.WAIT.getTag())) {
+        if (mQueue.getCurrentExePoolNum() < maxTaskNum) {
+          mQueue.startTask(task);
+        }
+      }
+    } else {
+      // 任务不存在时,根据配置不同,对任务执行操作
+      if (!task.isRunning()
+          && mod.equals(QueueMod.WAIT.getTag())
+          && task.getState() == IEntity.STATE_WAIT) {
+        mQueue.startTask(task);
+      }
+    }
+  }
+}
diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/group/GroupStopCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/group/GroupStopCmd.java
new file mode 100644
index 00000000..85c400bd
--- /dev/null
+++ b/Aria/src/main/java/com/arialyy/aria/core/command/group/GroupStopCmd.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.arialyy.aria.core.command.group;
+
+import com.arialyy.aria.core.inf.AbsTaskEntity;
+
+/**
+ * Created by AriaL on 2017/6/29.
+ * 停止任务组的命令
+ */
+class GroupStopCmd extends AbsGroupCmd{
+  /**
+   * @param targetName 创建任务的对象名
+   */
+  GroupStopCmd(String targetName, T entity) {
+    super(targetName, entity);
+  }
+
+  @Override public void executeCmd() {
+
+  }
+}
diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/normal/AbsNormalCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/normal/AbsNormalCmd.java
new file mode 100644
index 00000000..675a918a
--- /dev/null
+++ b/Aria/src/main/java/com/arialyy/aria/core/command/normal/AbsNormalCmd.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.arialyy.aria.core.command.normal;
+
+import com.arialyy.aria.core.command.AbsCmd;
+import com.arialyy.aria.core.download.DownloadGroupTaskEntity;
+import com.arialyy.aria.core.download.DownloadTaskEntity;
+import com.arialyy.aria.core.inf.AbsTaskEntity;
+import com.arialyy.aria.core.queue.DownloadGroupTaskQueue;
+import com.arialyy.aria.core.queue.DownloadTaskQueue;
+import com.arialyy.aria.core.queue.UploadTaskQueue;
+import com.arialyy.aria.core.upload.UploadTaskEntity;
+import com.arialyy.aria.util.CheckUtil;
+import com.arialyy.aria.util.CommonUtil;
+
+/**
+ * Created by lyy on 2016/8/22.
+ * 下载命令
+ */
+public abstract class AbsNormalCmd extends AbsCmd {
+
+  /**
+   * 能否执行命令
+   */
+  boolean canExeCmd = true;
+
+  /**
+   * @param targetName 产生任务的对象名
+   */
+  AbsNormalCmd(String targetName, T entity) {
+    //canExeCmd = CheckUtil.checkCmdEntity(entity,
+    //    !(this instanceof CancelCmd) || !(this instanceof StopCmd));
+    mTargetName = targetName;
+    mTaskEntity = entity;
+    TAG = CommonUtil.getClassName(this);
+    if (entity instanceof DownloadTaskEntity) {
+      mQueue = DownloadTaskQueue.getInstance();
+      isDownloadCmd = true;
+    } else if (entity instanceof UploadTaskEntity) {
+      mQueue = UploadTaskQueue.getInstance();
+      isDownloadCmd = false;
+    } else if (entity instanceof DownloadGroupTaskEntity) {
+      mQueue = DownloadGroupTaskQueue.getInstance();
+      isDownloadCmd = true;
+    }
+  }
+}
\ No newline at end of file
diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/AddCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/normal/AddCmd.java
similarity index 76%
rename from Aria/src/main/java/com/arialyy/aria/core/command/AddCmd.java
rename to Aria/src/main/java/com/arialyy/aria/core/command/normal/AddCmd.java
index fe6d481b..e848813b 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/command/AddCmd.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/command/normal/AddCmd.java
@@ -14,18 +14,18 @@
  * limitations under the License.
  */
 
-package com.arialyy.aria.core.command;
+package com.arialyy.aria.core.command.normal;
 
 import android.util.Log;
+import com.arialyy.aria.core.inf.AbsTask;
 import com.arialyy.aria.core.inf.IEntity;
-import com.arialyy.aria.core.inf.ITask;
 import com.arialyy.aria.core.inf.AbsTaskEntity;
 
 /**
  * Created by lyy on 2016/8/22.
  * 添加任务的命令
  */
-class AddCmd extends AbsCmd {
+class AddCmd extends AbsNormalCmd {
 
   AddCmd(String targetName, T entity) {
     super(targetName, entity);
@@ -33,10 +33,10 @@ class AddCmd extends AbsCmd {
 
   @Override public void executeCmd() {
     if (!canExeCmd) return;
-    ITask task = mQueue.getTask(mEntity.getEntity());
+    AbsTask task = mQueue.getTask(mTaskEntity.getEntity());
     if (task == null) {
-      mEntity.getEntity().setState(IEntity.STATE_WAIT);
-      mQueue.createTask(mTargetName, mEntity);
+      mTaskEntity.getEntity().setState(IEntity.STATE_WAIT);
+      mQueue.createTask(mTargetName, mTaskEntity);
     } else {
       Log.w(TAG, "添加命令执行失败,【该任务已经存在】");
     }
diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/normal/CancelAllCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/normal/CancelAllCmd.java
new file mode 100644
index 00000000..375b2ffd
--- /dev/null
+++ b/Aria/src/main/java/com/arialyy/aria/core/command/normal/CancelAllCmd.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.arialyy.aria.core.command.normal;
+
+import com.arialyy.aria.core.download.DownloadGroupEntity;
+import com.arialyy.aria.core.download.DownloadTaskEntity;
+import com.arialyy.aria.core.inf.AbsTaskEntity;
+import com.arialyy.aria.core.upload.UploadTaskEntity;
+import com.arialyy.aria.orm.DbEntity;
+import com.arialyy.aria.util.CommonUtil;
+import java.util.List;
+
+/**
+ * Created by AriaL on 2017/6/27.
+ * 删除所有任务,并且删除所有回掉
+ */
+final class CancelAllCmd extends AbsNormalCmd {
+  /**
+   * @param targetName 产生任务的对象名
+   */
+  CancelAllCmd(String targetName, T entity) {
+    super(targetName, entity);
+  }
+
+  @Override public void executeCmd() {
+    mQueue.removeAllTask();
+    if (mTaskEntity instanceof DownloadTaskEntity) {
+      handleDownloadRemove();
+    } else {
+      handleUploadRemove();
+    }
+  }
+
+  /**
+   * 处理上传的删除
+   */
+  private void handleUploadRemove() {
+    List allEntity = DbEntity.findAllData(UploadTaskEntity.class);
+    for (UploadTaskEntity entity : allEntity) {
+      CommonUtil.delUploadTaskConfig(mTaskEntity.removeFile, entity);
+    }
+  }
+
+  /**
+   * 处理下载的删除
+   */
+  private void handleDownloadRemove() {
+    List allEntity = DbEntity.findAllData(DownloadTaskEntity.class);
+    for (DownloadTaskEntity entity : allEntity) {
+      CommonUtil.delDownloadTaskConfig(mTaskEntity.removeFile, entity);
+    }
+  }
+}
diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/CancelCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/normal/CancelCmd.java
similarity index 75%
rename from Aria/src/main/java/com/arialyy/aria/core/command/CancelCmd.java
rename to Aria/src/main/java/com/arialyy/aria/core/command/normal/CancelCmd.java
index ad2aecd8..ff859066 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/command/CancelCmd.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/command/normal/CancelCmd.java
@@ -14,32 +14,33 @@
  * limitations under the License.
  */
 
-package com.arialyy.aria.core.command;
+package com.arialyy.aria.core.command.normal;
 
 import android.text.TextUtils;
-import com.arialyy.aria.core.inf.ITask;
+import com.arialyy.aria.core.inf.AbsNormalTask;
+import com.arialyy.aria.core.inf.AbsTask;
 import com.arialyy.aria.core.inf.AbsTaskEntity;
 
 /**
  * Created by lyy on 2016/9/20.
  * 取消命令
  */
-class CancelCmd extends AbsCmd {
+class CancelCmd extends AbsNormalCmd {
   CancelCmd(String targetName, T entity) {
     super(targetName, entity);
   }
 
   @Override public void executeCmd() {
     if (!canExeCmd) return;
-    ITask task = mQueue.getTask(mEntity.getEntity());
+    AbsTask task = mQueue.getTask(mTaskEntity.getEntity());
     if (task == null) {
-      task = mQueue.createTask(mTargetName, mEntity);
+      task = mQueue.createTask(mTargetName, mTaskEntity);
     }
     if (task != null) {
       if (!TextUtils.isEmpty(mTargetName)) {
         task.setTargetName(mTargetName);
       }
-      mQueue.cancelTask(task);
+      mQueue.removeTask(task);
     }
   }
 }
\ No newline at end of file
diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/HighestPriorityCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/normal/HighestPriorityCmd.java
similarity index 78%
rename from Aria/src/main/java/com/arialyy/aria/core/command/HighestPriorityCmd.java
rename to Aria/src/main/java/com/arialyy/aria/core/command/normal/HighestPriorityCmd.java
index 74999611..e0efa2f7 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/command/HighestPriorityCmd.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/command/normal/HighestPriorityCmd.java
@@ -13,11 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.arialyy.aria.core.command;
+package com.arialyy.aria.core.command.normal;
 
 import android.text.TextUtils;
-import com.arialyy.aria.core.inf.ITask;
+import com.arialyy.aria.core.download.DownloadTask;
+import com.arialyy.aria.core.inf.AbsNormalTask;
 import com.arialyy.aria.core.inf.AbsTaskEntity;
+import com.arialyy.aria.core.queue.DownloadTaskQueue;
 
 /**
  * Created by lyy on 2017/6/2.
@@ -28,8 +30,10 @@ import com.arialyy.aria.core.inf.AbsTaskEntity;
  * 4、用户手动暂停或任务完成后,第二次重新执行该任务,该命令将失效
  * 5、如果下载队列中已经满了,则会停止队尾的任务,当高优先级任务完成后,该队尾任务将自动执行
  * 6、把任务设置为最高优先级任务后,将自动执行任务,不需要重新调用start()启动任务
+ *
+ * 目前只只支持单下载任务的最高优先级任务
  */
-final class HighestPriorityCmd extends AbsCmd {
+final class HighestPriorityCmd extends AbsNormalCmd {
   /**
    * @param targetName 产生任务的对象名
    */
@@ -39,15 +43,15 @@ final class HighestPriorityCmd extends AbsCmd {
 
   @Override public void executeCmd() {
     if (!canExeCmd) return;
-    ITask task = mQueue.getTask(mEntity.getEntity());
+    DownloadTask task = (DownloadTask) mQueue.getTask(mTaskEntity.getEntity());
     if (task == null) {
-      task = mQueue.createTask(mTargetName, mEntity);
+      task = (DownloadTask) mQueue.createTask(mTargetName, mTaskEntity);
     }
     if (task != null) {
       if (!TextUtils.isEmpty(mTargetName)) {
         task.setTargetName(mTargetName);
       }
-      mQueue.setTaskHighestPriority(task);
+      ((DownloadTaskQueue) mQueue).setTaskHighestPriority(task);
     }
   }
 }
diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/CmdFactory.java b/Aria/src/main/java/com/arialyy/aria/core/command/normal/NormalCmdFactory.java
similarity index 79%
rename from Aria/src/main/java/com/arialyy/aria/core/command/CmdFactory.java
rename to Aria/src/main/java/com/arialyy/aria/core/command/normal/NormalCmdFactory.java
index ae725b51..85aa33fc 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/command/CmdFactory.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/command/normal/NormalCmdFactory.java
@@ -14,16 +14,17 @@
  * limitations under the License.
  */
 
-package com.arialyy.aria.core.command;
+package com.arialyy.aria.core.command.normal;
 
 import com.arialyy.aria.core.AriaManager;
+import com.arialyy.aria.core.command.AbsCmdFactory;
 import com.arialyy.aria.core.inf.AbsTaskEntity;
 
 /**
  * Created by Lyy on 2016/9/23.
  * 命令工厂
  */
-public class CmdFactory {
+public class NormalCmdFactory extends AbsCmdFactory {
   /**
    * 创建任务
    */
@@ -56,17 +57,20 @@ public class CmdFactory {
    * 恢复所有停止的任务
    */
   public static final int TASK_RESUME_ALL = 0x130;
+  /**
+   * 删除所有任务,
+   */
+  public static final int TASK_CANCEL_ALL = 0x131;
+  private static volatile NormalCmdFactory INSTANCE = null;
 
-  private static volatile CmdFactory INSTANCE = null;
-
-  private CmdFactory() {
+  private NormalCmdFactory() {
 
   }
 
-  public static CmdFactory getInstance() {
+  public static NormalCmdFactory getInstance() {
     if (INSTANCE == null) {
       synchronized (AriaManager.LOCK) {
-        INSTANCE = new CmdFactory();
+        INSTANCE = new NormalCmdFactory();
       }
     }
     return INSTANCE;
@@ -78,7 +82,7 @@ public class CmdFactory {
    * @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}
    */
-  public  AbsCmd createCmd(String target, T entity, int type) {
+  public  AbsNormalCmd createCmd(String target, T entity, int type) {
     switch (type) {
       case TASK_CREATE:
         return new AddCmd<>(target, entity);
@@ -95,6 +99,8 @@ public class CmdFactory {
         return new StopAllCmd<>(target, entity);
       case TASK_RESUME_ALL:
         return new ResumeAllCmd<>(target, entity);
+      case TASK_CANCEL_ALL:
+        return new CancelAllCmd<>(target, entity);
       default:
         return null;
     }
diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/ResumeAllCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/normal/ResumeAllCmd.java
similarity index 73%
rename from Aria/src/main/java/com/arialyy/aria/core/command/ResumeAllCmd.java
rename to Aria/src/main/java/com/arialyy/aria/core/command/normal/ResumeAllCmd.java
index c7110077..1233ebe1 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/command/ResumeAllCmd.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/command/normal/ResumeAllCmd.java
@@ -1,11 +1,11 @@
-package com.arialyy.aria.core.command;
+package com.arialyy.aria.core.command.normal;
 
 import android.util.Log;
 import com.arialyy.aria.core.download.DownloadEntity;
 import com.arialyy.aria.core.download.DownloadTaskEntity;
+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.ITask;
 import com.arialyy.aria.orm.DbEntity;
 import java.util.List;
 
@@ -15,7 +15,7 @@ import java.util.List;
  * 1.如果执行队列没有满,则开始下载任务,直到执行队列满
  * 2.如果队列执行队列已经满了,则将所有任务添加到等待队列中
  */
-final class ResumeAllCmd extends AbsCmd {
+final class ResumeAllCmd extends AbsNormalCmd {
   /**
    * @param targetName 产生任务的对象名
    */
@@ -27,9 +27,9 @@ final class ResumeAllCmd extends AbsCmd {
     List allEntity =
         DbEntity.findDatas(DownloadEntity.class, "state=?", IEntity.STATE_STOP + "");
     for (DownloadEntity entity : allEntity) {
-      int exeNum = mQueue.getExeTaskNum();
+      int exeNum = mQueue.getCurrentExePoolNum();
       if (exeNum == 0 || exeNum < mQueue.getMaxTaskNum()) {
-        ITask task = createTask(entity);
+        AbsTask task = createTask(entity);
         mQueue.startTask(task);
       } else {
         entity.setState(IEntity.STATE_WAIT);
@@ -38,10 +38,11 @@ final class ResumeAllCmd extends AbsCmd {
     }
   }
 
-  private ITask createTask(DownloadEntity entity) {
-    ITask task = mQueue.getTask(entity);
+  private AbsTask createTask(DownloadEntity entity) {
+    AbsTask task = mQueue.getTask(entity);
     if (task == null) {
-      DownloadTaskEntity taskEntity = new DownloadTaskEntity(entity);
+      DownloadTaskEntity taskEntity = new DownloadTaskEntity();
+      taskEntity.entity = entity;
       task = mQueue.createTask(mTargetName, taskEntity);
     } else {
       Log.w(TAG, "添加命令执行失败,【该任务已经存在】");
diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/normal/StartCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/normal/StartCmd.java
new file mode 100644
index 00000000..7da4a5ca
--- /dev/null
+++ b/Aria/src/main/java/com/arialyy/aria/core/command/normal/StartCmd.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.arialyy.aria.core.command.normal;
+
+import android.text.TextUtils;
+import com.arialyy.aria.core.AriaManager;
+import com.arialyy.aria.core.QueueMod;
+import com.arialyy.aria.core.download.DownloadGroupTask;
+import com.arialyy.aria.core.download.DownloadTask;
+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.queue.DownloadGroupTaskQueue;
+import com.arialyy.aria.core.queue.DownloadTaskQueue;
+import com.arialyy.aria.core.scheduler.DQueueMapping;
+import com.arialyy.aria.orm.Primary;
+
+/**
+ * Created by lyy on 2016/8/22.
+ * 开始命令
+ * 队列模型{@link QueueMod#NOW}、{@link QueueMod#WAIT}
+ */
+class StartCmd extends AbsNormalCmd {
+
+  StartCmd(String targetName, T entity) {
+    super(targetName, entity);
+  }
+
+  @Override public void executeCmd() {
+    if (!canExeCmd) return;
+    String mod;
+    int maxTaskNum;
+    AriaManager manager = AriaManager.getInstance(AriaManager.APP);
+    if (isDownloadCmd) {
+      mod = manager.getDownloadConfig().getQueueMod();
+      maxTaskNum = manager.getDownloadConfig().getMaxTaskNum();
+    } else {
+      mod = manager.getUploadConfig().getQueueMod();
+      maxTaskNum = manager.getUploadConfig().getMaxTaskNum();
+    }
+
+    AbsTask task = mQueue.getTask(mTaskEntity.getEntity());
+    if (task == null) {
+      task = mQueue.createTask(mTargetName, mTaskEntity);
+      if (!TextUtils.isEmpty(mTargetName)) {
+        task.setTargetName(mTargetName);
+      }
+      // 任务不存在时,根据配置不同,对任务执行操作
+      if (mod.equals(QueueMod.NOW.getTag())) {
+        mQueue.startTask(task);
+      } else if (mod.equals(QueueMod.WAIT.getTag())) {
+        if (mQueue.getCurrentExePoolNum() < maxTaskNum
+            || task.getState() == IEntity.STATE_STOP
+            || task.getState() == IEntity.STATE_COMPLETE) {
+          mQueue.startTask(task);
+        }
+      }
+    } else {
+      // 任务不存在时,根据配置不同,对任务执行操作
+      if (!task.isRunning()) {
+        mQueue.startTask(task);
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/StopAllCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/normal/StopAllCmd.java
similarity index 75%
rename from Aria/src/main/java/com/arialyy/aria/core/command/StopAllCmd.java
rename to Aria/src/main/java/com/arialyy/aria/core/command/normal/StopAllCmd.java
index 2c16378e..431adfdd 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/command/StopAllCmd.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/command/normal/StopAllCmd.java
@@ -1,4 +1,4 @@
-package com.arialyy.aria.core.command;
+package com.arialyy.aria.core.command.normal;
 
 import com.arialyy.aria.core.inf.AbsTaskEntity;
 
@@ -6,7 +6,7 @@ import com.arialyy.aria.core.inf.AbsTaskEntity;
  * Created by AriaL on 2017/6/13.
  * 停止所有任务的命令,并清空所有等待队列
  */
-final class StopAllCmd extends AbsCmd {
+final class StopAllCmd extends AbsNormalCmd {
   /**
    * @param targetName 产生任务的对象名
    */
diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/StopCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/normal/StopCmd.java
similarity index 78%
rename from Aria/src/main/java/com/arialyy/aria/core/command/StopCmd.java
rename to Aria/src/main/java/com/arialyy/aria/core/command/normal/StopCmd.java
index 1e1fb295..9d0b0811 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/command/StopCmd.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/command/normal/StopCmd.java
@@ -14,19 +14,19 @@
  * limitations under the License.
  */
 
-package com.arialyy.aria.core.command;
+package com.arialyy.aria.core.command.normal;
 
 import android.text.TextUtils;
 import android.util.Log;
+import com.arialyy.aria.core.inf.AbsTask;
 import com.arialyy.aria.core.inf.IEntity;
-import com.arialyy.aria.core.inf.ITask;
 import com.arialyy.aria.core.inf.AbsTaskEntity;
 
 /**
  * Created by lyy on 2016/9/20.
  * 停止命令
  */
-class StopCmd extends AbsCmd {
+class StopCmd extends AbsNormalCmd {
 
   StopCmd(String targetName, T entity) {
     super(targetName, entity);
@@ -34,10 +34,10 @@ class StopCmd extends AbsCmd {
 
   @Override public void executeCmd() {
     if (!canExeCmd) return;
-    ITask task = mQueue.getTask(mEntity.getEntity());
+    AbsTask task = mQueue.getTask(mTaskEntity.getEntity());
     if (task == null) {
-      if (mEntity.getEntity().getState() == IEntity.STATE_RUNNING) {
-        task = mQueue.createTask(mTargetName, mEntity);
+      if (mTaskEntity.getEntity().getState() == IEntity.STATE_RUNNING) {
+        task = mQueue.createTask(mTargetName, mTaskEntity);
         mQueue.stopTask(task);
       } else {
         Log.w(TAG, "停止命令执行失败,【调度器中没有该任务】");
diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DListener.java b/Aria/src/main/java/com/arialyy/aria/core/download/DListener.java
new file mode 100644
index 00000000..2d31b0d8
--- /dev/null
+++ b/Aria/src/main/java/com/arialyy/aria/core/download/DListener.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.arialyy.aria.core.download;
+
+import android.os.Handler;
+import com.arialyy.aria.core.AriaManager;
+import com.arialyy.aria.core.download.downloader.DownloadListener;
+import com.arialyy.aria.core.inf.AbsEntity;
+import com.arialyy.aria.core.inf.AbsTask;
+import com.arialyy.aria.core.inf.IEntity;
+import com.arialyy.aria.core.scheduler.ISchedulers;
+import com.arialyy.aria.util.CommonUtil;
+import java.lang.ref.WeakReference;
+
+/**
+ * 下载监听类
+ */
+class DListener>
+    extends DownloadListener {
+  private WeakReference outHandler;
+  private long lastLen = 0;   //上一次发送长度
+  private boolean isFirst = true;
+  private ENTITY entity;
+  private TASK task;
+  private boolean isConvertSpeed = false;
+  boolean isWait = false;
+
+  DListener(TASK task, Handler outHandler) {
+    this.outHandler = new WeakReference<>(outHandler);
+    this.task = new WeakReference<>(task).get();
+    this.entity = this.task.getEntity();
+    final AriaManager manager = AriaManager.getInstance(AriaManager.APP);
+    isConvertSpeed = manager.getDownloadConfig().isConvertSpeed();
+  }
+
+  @Override public void onPre() {
+    saveData(IEntity.STATE_PRE, -1);
+    sendInState2Target(ISchedulers.PRE);
+  }
+
+  @Override public void onPostPre(long fileSize) {
+    entity.setFileSize(fileSize);
+    entity.setConvertFileSize(CommonUtil.formatFileSize(fileSize));
+    saveData(IEntity.STATE_POST_PRE, -1);
+    sendInState2Target(ISchedulers.POST_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) {
+    entity.setCurrentProgress(currentLocation);
+    long speed = currentLocation - lastLen;
+    if (isFirst) {
+      speed = 0;
+      isFirst = false;
+    }
+    handleSpeed(speed);
+    sendInState2Target(ISchedulers.RUNNING);
+    lastLen = 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, entity.getFileSize());
+    handleSpeed(0);
+    sendInState2Target(ISchedulers.COMPLETE);
+  }
+
+  @Override public void onFail() {
+    entity.setFailNum(entity.getFailNum() + 1);
+    saveData(IEntity.STATE_FAIL, -1);
+    handleSpeed(0);
+    sendInState2Target(ISchedulers.FAIL);
+  }
+
+  private void handleSpeed(long speed) {
+    if (isConvertSpeed) {
+      entity.setConvertSpeed(CommonUtil.formatFileSize(speed) + "/s");
+    } else {
+      entity.setSpeed(speed);
+    }
+  }
+
+  /**
+   * 将任务状态发送给下载器
+   *
+   * @param state {@link ISchedulers#START}
+   */
+  private void sendInState2Target(int state) {
+    if (outHandler.get() != null) {
+      outHandler.get().obtainMessage(state, task).sendToTarget();
+    }
+  }
+
+  private void saveData(int state, long location) {
+    if (state == IEntity.STATE_CANCEL) {
+      entity.deleteData();
+    } else if (state == IEntity.STATE_COMPLETE) {
+      entity.setState(state);
+      entity.setComplete(true);
+      entity.setCompleteTime(System.currentTimeMillis());
+      entity.setCurrentProgress(entity.getFileSize());
+      entity.update();
+    } else {
+      entity.setState(state);
+      if (location != -1) {
+        entity.setCurrentProgress(location);
+      }
+      entity.update();
+    }
+  }
+}
\ No newline at end of file
diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java
index 5c0e18bf..f710c50f 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java
@@ -18,25 +18,107 @@ package com.arialyy.aria.core.download;
 
 import android.os.Parcel;
 import android.os.Parcelable;
-import com.arialyy.aria.core.inf.AbsEntity;
-import com.arialyy.aria.orm.Ignore;
+import com.arialyy.aria.core.inf.AbsNormalEntity;
+import com.arialyy.aria.core.inf.AbsTaskEntity;
+import com.arialyy.aria.orm.Primary;
 
 /**
  * Created by lyy on 2015/12/25.
  * 下载实体
- * !!! 注意:CREATOR要进行@Ignore注解
- * !!!并且需要Parcelable时需要手动填写rowID;
  */
-public class DownloadEntity extends AbsEntity implements Parcelable {
+public class DownloadEntity extends AbsNormalEntity implements Parcelable {
   private String downloadUrl = ""; //下载路径
-  private String downloadPath = ""; //保存路径
-  private boolean isDownloadComplete = false;   //是否下载完成
+  @Primary private String downloadPath = ""; //保存路径
   private boolean isRedirect = false; //是否重定向
   private String redirectUrl = ""; //重定向链接
 
+  /**
+   * 所属任务组
+   */
+  private String groupName = "";
+
+  /**
+   * 通过{@link AbsTaskEntity#md5Key}从服务器的返回信息中获取的文件md5信息,如果服务器没有返回,则不会设置该信息
+   * 如果你已经设置了该任务的MD5信息,Aria也不会从服务器返回的信息中获取该信息
+   */
+  private String md5Code = "";
+
+  /**
+   * 通过{@link AbsTaskEntity#dispositionKey}从服务器的返回信息中获取的文件描述信息
+   */
+  private String disposition = "";
+
+  /**
+   * 从disposition获取到的文件名,如果可以获取到,则会赋值到这个字段
+   */
+  private String serverFileName = "";
+
+  @Override public String getKey() {
+    return downloadUrl;
+  }
+
   public DownloadEntity() {
   }
 
+  @Override public String toString() {
+    return "DownloadEntity{"
+        + "downloadUrl='"
+        + downloadUrl
+        + '\''
+        + ", downloadPath='"
+        + downloadPath
+        + '\''
+        + ", isRedirect="
+        + isRedirect
+        + ", redirectUrl='"
+        + redirectUrl
+        + '\''
+        + ", groupName='"
+        + groupName
+        + '\''
+        + ", md5Code='"
+        + md5Code
+        + '\''
+        + ", disposition='"
+        + disposition
+        + '\''
+        + ", serverFileName='"
+        + serverFileName
+        + '\''
+        + '}';
+  }
+
+  public String getMd5Code() {
+    return md5Code;
+  }
+
+  public void setMd5Code(String md5Code) {
+    this.md5Code = md5Code;
+  }
+
+  public String getDisposition() {
+    return disposition;
+  }
+
+  public void setDisposition(String disposition) {
+    this.disposition = disposition;
+  }
+
+  public String getServerFileName() {
+    return serverFileName;
+  }
+
+  public void setServerFileName(String serverFileName) {
+    this.serverFileName = serverFileName;
+  }
+
+  public String getGroupName() {
+    return groupName;
+  }
+
+  public void setGroupName(String groupName) {
+    this.groupName = groupName;
+  }
 
   public String getDownloadUrl() {
     return downloadUrl;
@@ -56,14 +138,6 @@ public class DownloadEntity extends AbsEntity implements Parcelable {
     return this;
   }
 
-  public boolean isDownloadComplete() {
-    return isDownloadComplete;
-  }
-
-  public void setDownloadComplete(boolean downloadComplete) {
-    isDownloadComplete = downloadComplete;
-  }
-
   @Override public DownloadEntity clone() throws CloneNotSupportedException {
     return (DownloadEntity) super.clone();
   }
@@ -92,21 +166,27 @@ public class DownloadEntity extends AbsEntity implements Parcelable {
     super.writeToParcel(dest, flags);
     dest.writeString(this.downloadUrl);
     dest.writeString(this.downloadPath);
-    dest.writeByte(this.isDownloadComplete ? (byte) 1 : (byte) 0);
     dest.writeByte(this.isRedirect ? (byte) 1 : (byte) 0);
     dest.writeString(this.redirectUrl);
+    dest.writeString(this.groupName);
+    dest.writeString(this.md5Code);
+    dest.writeString(this.disposition);
+    dest.writeString(this.serverFileName);
   }
 
   protected DownloadEntity(Parcel in) {
     super(in);
     this.downloadUrl = in.readString();
     this.downloadPath = in.readString();
-    this.isDownloadComplete = in.readByte() != 0;
     this.isRedirect = in.readByte() != 0;
     this.redirectUrl = in.readString();
+    this.groupName = in.readString();
+    this.md5Code = in.readString();
+    this.disposition = in.readString();
+    this.serverFileName = in.readString();
   }
 
-  @Ignore public static final Creator CREATOR = new Creator() {
+  public static final Creator CREATOR = new Creator() {
     @Override public DownloadEntity createFromParcel(Parcel source) {
       return new DownloadEntity(source);
     }
diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java
new file mode 100644
index 00000000..da96b5ff
--- /dev/null
+++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.arialyy.aria.core.download;
+
+import android.os.Parcel;
+import com.arialyy.aria.core.inf.AbsGroupEntity;
+import com.arialyy.aria.orm.NormalList;
+import com.arialyy.aria.orm.OneToMany;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by AriaL on 2017/6/29.
+ * 下载任务组实体
+ */
+public class DownloadGroupEntity extends AbsGroupEntity {
+
+  @OneToMany(table = DownloadEntity.class, key = "groupName") private List subtask =
+      new ArrayList<>();
+
+  /**
+   * 子任务链接组
+   */
+  @NormalList(clazz = String.class) private List urls = new ArrayList<>();
+
+  /**
+   * 任务组下载文件的文件夹地址
+   *
+   * @see DownloadGroupTarget#setDownloadDirPath(String)
+   */
+  private String dirPath = "";
+
+  public List getSubTask() {
+    return subtask;
+  }
+
+  void setSubTasks(List subTasks) {
+    this.subtask = subTasks;
+  }
+
+  public String getDirPath() {
+    return dirPath;
+  }
+
+  public void setDirPath(String dirPath) {
+    this.dirPath = dirPath;
+  }
+
+  public List getUrls() {
+    return urls;
+  }
+
+  void setUrls(List urls) {
+    this.urls = urls;
+  }
+
+  void setGroupName(String key) {
+    this.groupName = key;
+  }
+
+  public DownloadGroupEntity() {
+  }
+
+  @Override public int describeContents() {
+    return 0;
+  }
+
+  @Override public void writeToParcel(Parcel dest, int flags) {
+    super.writeToParcel(dest, flags);
+    dest.writeTypedList(this.subtask);
+    dest.writeString(this.dirPath);
+  }
+
+  protected DownloadGroupEntity(Parcel in) {
+    super(in);
+    this.subtask = in.createTypedArrayList(DownloadEntity.CREATOR);
+    this.dirPath = in.readString();
+  }
+
+  public static final Creator CREATOR = new Creator() {
+    @Override public DownloadGroupEntity createFromParcel(Parcel source) {
+      return new DownloadGroupEntity(source);
+    }
+
+    @Override public DownloadGroupEntity[] newArray(int size) {
+      return new DownloadGroupEntity[size];
+    }
+  };
+}
diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTarget.java
new file mode 100644
index 00000000..e77e1c06
--- /dev/null
+++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTarget.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.arialyy.aria.core.download;
+
+import android.text.TextUtils;
+import com.arialyy.aria.core.inf.AbsGroupTarget;
+import com.arialyy.aria.orm.DbEntity;
+import com.arialyy.aria.orm.DbUtil;
+import com.arialyy.aria.util.CheckUtil;
+import com.arialyy.aria.util.CommonUtil;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by AriaL on 2017/6/29.
+ */
+public class DownloadGroupTarget
+    extends AbsGroupTarget {
+  private List mUrls = new ArrayList<>();
+  private final String TAG = "DownloadGroupTarget";
+  /**
+   * 子任务文件名
+   */
+  private List mSubTaskFileName = new ArrayList<>();
+  private String mGroupName;
+  /**
+   * 是否已经设置了文件路径
+   */
+  private boolean isSetDirPathed = false;
+
+  DownloadGroupTarget(DownloadGroupEntity groupEntity, String targetName) {
+    this.mTargetName = targetName;
+    if (groupEntity.getUrls() != null && !groupEntity.getUrls().isEmpty()) {
+      this.mUrls.addAll(groupEntity.getUrls());
+    }
+    init(groupEntity.getGroupName());
+  }
+
+  DownloadGroupTarget(List urls, String targetName) {
+    this.mTargetName = targetName;
+    this.mUrls = urls;
+    init(CommonUtil.getMd5Code(urls));
+  }
+
+  private void init(String key) {
+    mGroupName = key;
+    mTaskEntity = DbEntity.findFirst(DownloadGroupTaskEntity.class, "key=?", key);
+    if (mTaskEntity == null) {
+      mTaskEntity = new DownloadGroupTaskEntity();
+      mTaskEntity.key = key;
+      mTaskEntity.entity = getDownloadGroupEntity();
+      mTaskEntity.insert();
+    }
+    if (mTaskEntity.entity == null) {
+      mTaskEntity.entity = getDownloadGroupEntity();
+    }
+    mEntity = mTaskEntity.entity;
+  }
+
+  /**
+   * 查询任务组实体,如果数据库不存在该实体,则新创建一个新的任务组实体
+   */
+  private DownloadGroupEntity getDownloadGroupEntity() {
+    DownloadGroupEntity entity =
+        DbEntity.findFirst(DownloadGroupEntity.class, "groupName=?", mGroupName);
+    if (entity == null) {
+      entity = new DownloadGroupEntity();
+      entity.setGroupName(mGroupName);
+      entity.setUrls(mUrls);
+      entity.insert();
+    }
+    return entity;
+  }
+
+  /**
+   * 设置任务组别名
+   */
+  public DownloadGroupTarget setGroupAlias(String alias) {
+    if (TextUtils.isEmpty(alias)) return this;
+    mEntity.setAlias(alias);
+    mEntity.update();
+    return this;
+  }
+
+  /**
+   * 如果你是使用{@link DownloadReceiver#load(DownloadGroupEntity)}进行下载操作,那么你需要设置任务组的下载地址
+   */
+  public DownloadGroupTarget setGroupUrl(List urls) {
+    CheckUtil.checkDownloadUrls(urls);
+    mUrls.clear();
+    mUrls.addAll(urls);
+    mEntity.setGroupName(CommonUtil.getMd5Code(urls));
+    mEntity.update();
+    return this;
+  }
+
+  /**
+   * 设置任务组的文件夹路径,在Aria中,任务组的所有子任务都会下载到以任务组组名的文件夹中。
+   * 如:groupDirPath = "/mnt/sdcard/download/group_test"
+   * 
+   *   {@code
+   *      + mnt
+   *        + sdcard
+   *          + download
+   *            + group_test
+   *              - task1.apk
+   *              - task2.apk
+   *              - task3.apk
+   *              ....
+   *
+   *   }
+   * 
+ * + * @param groupDirPath 任务组保存文件夹路径 + */ + public DownloadGroupTarget setDownloadDirPath(String groupDirPath) { + if (TextUtils.isEmpty(groupDirPath)) { + throw new NullPointerException("任务组文件夹保存路径不能为null"); + } + + isSetDirPathed = true; + if (mEntity.getDirPath().equals(groupDirPath)) return this; + + File file = new File(groupDirPath); + if (file.exists() && file.isFile()) { + throw new IllegalArgumentException("路径不能为文件"); + } + if (!file.exists()) { + file.mkdirs(); + } + + mEntity.setDirPath(groupDirPath); + if (!TextUtils.isEmpty(mEntity.getDirPath())) { + reChangeDirPath(groupDirPath); + } else { + mEntity.setSubTasks(createSubTask()); + } + mEntity.update(); + return this; + } + + /** + * 改变任务组文件夹路径,修改文件夹路径会将子任务所有路径更换 + * + * @param newDirPath 新的文件夹路径 + */ + private void reChangeDirPath(String newDirPath) { + List subTask = mEntity.getSubTask(); + if (subTask != null && !subTask.isEmpty()) { + for (DownloadEntity entity : subTask) { + String oldPath = entity.getDownloadPath(); + String newPath = newDirPath + "/" + entity.getFileName(); + File file = new File(oldPath); + file.renameTo(new File(newPath)); + DbEntity.exeSql("UPDATE DownloadEntity SET downloadPath='" + + newPath + + "' WHERE downloadPath='" + + oldPath + + "'"); + DbEntity.exeSql( + "UPDATE DownloadTaskEntity SET key='" + newPath + "' WHERE key='" + oldPath + "'"); + } + } else { + mEntity.setSubTasks(createSubTask()); + } + } + + /** + * 设置子任务文件名,该方法必须在{@link #setDownloadDirPath(String)}之后调用,否则不生效 + */ + public DownloadGroupTarget setSubTaskFileName(List subTaskFileName) { + if (subTaskFileName == null || subTaskFileName.isEmpty()) return this; + mSubTaskFileName.addAll(subTaskFileName); + if (mUrls.size() != subTaskFileName.size()) { + throw new IllegalArgumentException("下载链接数必须要和保存路径的数量一致"); + } + if (isSetDirPathed) { + List entities = mEntity.getSubTask(); + int i = 0; + for (DownloadEntity entity : entities) { + String newName = mSubTaskFileName.get(i); + updateSubFileName(entity, newName); + i++; + } + } + return this; + } + + /** + * 更新子任务文件名 + */ + private void updateSubFileName(DownloadEntity entity, String newName) { + if (!newName.equals(entity.getFileName())) { + String oldPath = mEntity.getDirPath() + "/" + entity.getFileName(); + String newPath = mEntity.getDirPath() + "/" + newName; + File file = new File(oldPath); + if (file.exists()) { + file.renameTo(new File(newPath)); + } + DbEntity.exeSql( + "UPDATE DownloadTaskEntity SET key='" + newPath + "' WHERE key='" + oldPath + "'"); + entity.setDownloadPath(newPath); + entity.setFileName(newName); + entity.update(); + } + } + + /** + * 创建子任务 + */ + private List createSubTask() { + List list = new ArrayList<>(); + for (int i = 0, len = mUrls.size(); i < len; i++) { + DownloadEntity entity = new DownloadEntity(); + entity.setDownloadUrl(mUrls.get(i)); + String fileName = mSubTaskFileName.isEmpty() ? createFileName(entity.getDownloadUrl()) + : mSubTaskFileName.get(i); + entity.setDownloadPath(mEntity.getDirPath() + "/" + fileName); + entity.setGroupName(mGroupName); + entity.setGroupChild(true); + entity.setFileName(fileName); + entity.insert(); + list.add(entity); + } + return list; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTask.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTask.java new file mode 100644 index 00000000..1a375bba --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTask.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.download; + +import android.os.Handler; +import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.download.downloader.DownloadGroupUtil; +import com.arialyy.aria.core.download.downloader.IDownloadUtil; +import com.arialyy.aria.core.inf.AbsGroupTask; +import com.arialyy.aria.core.scheduler.ISchedulers; +import com.arialyy.aria.util.CheckUtil; + +/** + * Created by AriaL on 2017/6/27. + * 任务组任务 + */ +public class DownloadGroupTask extends AbsGroupTask { + private final String TAG = "DownloadGroupTask"; + private DListener mListener; + private IDownloadUtil mUtil; + + private DownloadGroupTask(DownloadGroupTaskEntity taskEntity, Handler outHandler) { + mTaskEntity = taskEntity; + mEntity = taskEntity.getEntity(); + mOutHandler = outHandler; + mContext = AriaManager.APP; + mListener = new DListener<>(this, mOutHandler); + mUtil = new DownloadGroupUtil(mListener, mTaskEntity); + } + + @Override public boolean isRunning() { + return mUtil.isDownloading(); + } + + @Override public void start() { + mUtil.startDownload(); + } + + @Override public void stop() { + if (!mUtil.isDownloading()) { + if (mOutHandler != null) { + mOutHandler.obtainMessage(ISchedulers.STOP, this).sendToTarget(); + } + } + mUtil.stopDownload(); + } + + @Override public void cancel() { + if (!mUtil.isDownloading()) { + if (mOutHandler != null) { + mOutHandler.obtainMessage(ISchedulers.CANCEL, this).sendToTarget(); + } + } + mUtil.cancelDownload(); + } + + public static class Builder { + DownloadGroupTaskEntity taskEntity; + Handler outHandler; + String targetName; + + public Builder(String targetName, DownloadGroupTaskEntity taskEntity) { + CheckUtil.checkTaskEntity(taskEntity); + this.targetName = targetName; + this.taskEntity = taskEntity; + } + + /** + * 设置自定义Handler处理下载状态时间 + * + * @param schedulers {@link ISchedulers} + */ + public DownloadGroupTask.Builder setOutHandler(ISchedulers schedulers) { + this.outHandler = new Handler(schedulers); + return this; + } + + public DownloadGroupTask build() { + DownloadGroupTask task = new DownloadGroupTask(taskEntity, outHandler); + task.setTargetName(targetName); + taskEntity.save(); + return task; + } + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTaskEntity.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTaskEntity.java new file mode 100644 index 00000000..4987a1d0 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTaskEntity.java @@ -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.download; + +import com.arialyy.aria.core.inf.AbsTaskEntity; +import com.arialyy.aria.orm.OneToOne; + +/** + * Created by AriaL on 2017/7/1. + */ +public class DownloadGroupTaskEntity extends AbsTaskEntity { + + @OneToOne(table = DownloadGroupEntity.class, key = "groupName") public DownloadGroupEntity entity; + + @Override public DownloadGroupEntity getEntity() { + return entity; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java index 8f29f02f..ff289bd7 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java @@ -17,12 +17,14 @@ package com.arialyy.aria.core.download; import android.support.annotation.NonNull; import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.core.inf.ICmd; +import com.arialyy.aria.core.inf.AbsEntity; +import com.arialyy.aria.core.inf.AbsReceiver; import com.arialyy.aria.core.inf.IReceiver; -import com.arialyy.aria.core.command.CmdFactory; -import com.arialyy.aria.core.command.AbsCmd; +import com.arialyy.aria.core.command.normal.NormalCmdFactory; +import com.arialyy.aria.core.scheduler.DownloadGroupSchedulers; import com.arialyy.aria.core.scheduler.DownloadSchedulers; import com.arialyy.aria.core.scheduler.ISchedulerListener; +import com.arialyy.aria.core.upload.ProxyHelper; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.CheckUtil; import com.arialyy.aria.util.CommonUtil; @@ -34,10 +36,8 @@ import java.util.Set; * Created by lyy on 2016/12/5. * 下载功能接收器 */ -public class DownloadReceiver implements IReceiver { - private static final String TAG = "DownloadReceiver"; - public String targetName; - public Object obj; +public class DownloadReceiver extends AbsReceiver { + private final String TAG = "DownloadReceiver"; public ISchedulerListener listener; /** @@ -51,43 +51,51 @@ public class DownloadReceiver implements IReceiver { } /** - * {@link #load(String)},请使用该方法 + * 使用下载实体执行下载操作 */ - @Deprecated public DownloadTarget load(DownloadEntity entity) { + public DownloadTarget load(DownloadEntity entity) { return new DownloadTarget(entity, targetName); } /** - * 读取下载链接 + * 加载下载地址 */ public DownloadTarget load(@NonNull String downloadUrl) { CheckUtil.checkDownloadUrl(downloadUrl); - DownloadEntity entity = - DownloadEntity.findData(DownloadEntity.class, "downloadUrl=?", downloadUrl); - if (entity == null) { - entity = new DownloadEntity(); - } - entity.setDownloadUrl(downloadUrl); - return new DownloadTarget(entity, targetName); + return new DownloadTarget(downloadUrl, targetName); } /** - * 添加调度器回调 + * 加载下载地址,如果任务组的中的下载地址改变了,则任务从新的一个任务组 + */ + public DownloadGroupTarget load(List urls) { + CheckUtil.checkDownloadUrls(urls); + return new DownloadGroupTarget(urls, targetName); + } + + /** + * 使用任务组实体执行任务组的实体执行任务组的下载操作 * - * @see #register() + * @param groupEntity 如果加载的任务实体没有子项的下载地址, + * 那么你需要使用{@link DownloadGroupTarget#setGroupUrl(List)}设置子项的下载地址 */ - @Deprecated public DownloadReceiver addSchedulerListener( - ISchedulerListener listener) { - this.listener = listener; - DownloadSchedulers.getInstance().addSchedulerListener(targetName, listener); - return this; + public DownloadGroupTarget load(DownloadGroupEntity groupEntity) { + return new DownloadGroupTarget(groupEntity, targetName); } /** * 将当前类注册到Aria */ public DownloadReceiver register() { - DownloadSchedulers.getInstance().register(obj); + String className = obj.getClass().getName(); + Set dCounter = ProxyHelper.getInstance().downloadCounter; + Set dgCounter = ProxyHelper.getInstance().downloadGroupCounter; + if (dCounter != null && dCounter.contains(className)) { + DownloadSchedulers.getInstance().register(obj); + } + if (dgCounter != null && dgCounter.contains(className)) { + DownloadGroupSchedulers.getInstance().register(obj); + } return this; } @@ -95,7 +103,27 @@ public class DownloadReceiver implements IReceiver { * 取消注册 */ @Override public void unRegister() { - DownloadSchedulers.getInstance().unRegister(obj); + String className = obj.getClass().getName(); + Set dCounter = ProxyHelper.getInstance().downloadCounter; + Set dgCounter = ProxyHelper.getInstance().downloadGroupCounter; + if (dCounter != null && dCounter.contains(className)) { + DownloadSchedulers.getInstance().unRegister(obj); + } + if (dgCounter != null && dgCounter.contains(className)) { + DownloadGroupSchedulers.getInstance().unRegister(obj); + } + } + + /** + * 添加调度器回调 + * + * @see #register() + */ + @Deprecated public DownloadReceiver addSchedulerListener( + ISchedulerListener listener) { + this.listener = listener; + DownloadSchedulers.getInstance().addSchedulerListener(targetName, listener); + return this; } /** @@ -119,27 +147,74 @@ public class DownloadReceiver implements IReceiver { */ public DownloadEntity getDownloadEntity(String downloadUrl) { CheckUtil.checkDownloadUrl(downloadUrl); - return DownloadEntity.findData(DownloadEntity.class, "downloadUrl=?", downloadUrl); + return DbEntity.findFirst(DownloadEntity.class, "downloadUrl=? and isGroupChild='false'", + downloadUrl); + } + + /** + * 通过下载链接获取保存在数据库的下载任务实体 + */ + public DownloadTaskEntity getDownloadTask(String downloadUrl) { + CheckUtil.checkDownloadUrl(downloadUrl); + return DbEntity.findFirst(DownloadTaskEntity.class, "groupName=? and isGroupTask='false'", + downloadUrl); + } + + /** + * 通过下载链接获取保存在数据库的下载任务组实体 + */ + public DownloadGroupTaskEntity getDownloadGroupTask(List urls) { + CheckUtil.checkDownloadUrls(urls); + String hashCode = CommonUtil.getMd5Code(urls); + return DbEntity.findFirst(DownloadGroupTaskEntity.class, "key=?", hashCode); } /** * 下载任务是否存在 */ @Override public boolean taskExists(String downloadUrl) { - return DownloadEntity.findData(DownloadEntity.class, "downloadUrl=?", downloadUrl) != null; + return DownloadEntity.findFirst(DownloadEntity.class, "downloadUrl=?", downloadUrl) != null; } - @Override public List getTaskList() { - return DownloadEntity.findAllData(DownloadEntity.class); + /** + * 获取普通下载任务列表 + */ + @Override public List getSimpleTaskList() { + return DownloadEntity.findDatas(DownloadEntity.class, "isGroupChild=? and downloadPath!=''", + "false"); + } + + /** + * 获取任务组列表 + */ + public List getGroupTaskList() { + return DownloadEntity.findAllData(DownloadGroupEntity.class); + } + + /** + * 获取普通任务和任务组的任务列表 + */ + public List getTotleTaskList() { + List list = new ArrayList<>(); + List simpleTask = getSimpleTaskList(); + List groupTask = getGroupTaskList(); + if (simpleTask != null && !simpleTask.isEmpty()) { + list.addAll(simpleTask); + } + if (groupTask != null && !groupTask.isEmpty()) { + list.addAll(groupTask); + } + return list; } /** * 停止所有正在下载的任务,并清空等待队列。 */ @Override public void stopAllTask() { - final AriaManager ariaManager = AriaManager.getInstance(AriaManager.APP); - ariaManager.setCmd(CmdFactory.getInstance() - .createCmd(targetName, new DownloadTaskEntity(), CmdFactory.TASK_STOP_ALL)).exe(); + AriaManager.getInstance(AriaManager.APP) + .setCmd(NormalCmdFactory.getInstance() + .createCmd(targetName, new DownloadTaskEntity(), NormalCmdFactory.TASK_STOP_ALL)) + .exe(); } /** @@ -148,23 +223,23 @@ public class DownloadReceiver implements IReceiver { * 2.如果队列执行队列已经满了,则将所有任务添加到等待队列中 */ public void resumeAllTask() { - final AriaManager ariaManager = AriaManager.getInstance(AriaManager.APP); - ariaManager.setCmd(CmdFactory.getInstance() - .createCmd(targetName, new DownloadTaskEntity(), CmdFactory.TASK_RESUME_ALL)).exe(); + AriaManager.getInstance(AriaManager.APP) + .setCmd(NormalCmdFactory.getInstance() + .createCmd(targetName, new DownloadTaskEntity(), NormalCmdFactory.TASK_RESUME_ALL)) + .exe(); } /** * 删除所有任务 + * + * @param removeFile {@code true} 删除已经下载完成的任务,不仅删除下载记录,还会删除已经下载完成的文件,{@code false} + * 如果文件已经下载完成,只删除下载记录 */ - @Override public void removeAllTask() { + @Override public void removeAllTask(boolean removeFile) { final AriaManager ariaManager = AriaManager.getInstance(AriaManager.APP); - List allEntity = DbEntity.findAllData(DownloadEntity.class); - List cancelCmds = new ArrayList<>(); - for (DownloadEntity entity : allEntity) { - cancelCmds.add( - CommonUtil.createCmd(targetName, new DownloadTaskEntity(entity), CmdFactory.TASK_CANCEL)); - } - ariaManager.setCmds(cancelCmds).exe(); + ariaManager.setCmd(CommonUtil.createCmd(targetName, new DownloadTaskEntity(), + NormalCmdFactory.TASK_CANCEL_ALL)).exe(); + Set keys = ariaManager.getReceiver().keySet(); for (String key : keys) { IReceiver receiver = ariaManager.getReceiver().get(key); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTarget.java index 05e6c2d9..c77e853c 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTarget.java @@ -17,31 +17,60 @@ package com.arialyy.aria.core.download; import android.support.annotation.NonNull; import android.text.TextUtils; -import android.util.Log; -import com.arialyy.aria.core.RequestEnum; -import com.arialyy.aria.core.inf.AbsTarget; +import com.arialyy.aria.core.inf.AbsNormalTarget; +import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.queue.DownloadTaskQueue; +import com.arialyy.aria.orm.DbEntity; import java.io.File; -import java.util.Map; /** * Created by lyy on 2016/12/5. * https://github.com/AriaLyy/Aria */ -public class DownloadTarget extends AbsTarget { +public class DownloadTarget + extends AbsNormalTarget { DownloadTarget(DownloadEntity entity, String targetName) { - this.entity = entity; - this.targetName = targetName; - taskEntity = new DownloadTaskEntity(entity); + this(entity.getDownloadUrl(), targetName); } - @Override public void pause() { - super.pause(); + DownloadTarget(String url, String targetName) { + mTargetName = targetName; + DownloadEntity entity = getEntity(url); + mTaskEntity = DbEntity.findFirst(DownloadTaskEntity.class, "key=? and isGroupTask='false'", + entity.getDownloadPath()); + if (mTaskEntity == null) { + mTaskEntity = new DownloadTaskEntity(); + mTaskEntity.key = entity.getDownloadPath(); + mTaskEntity.entity = entity; + mTaskEntity.save(); + } + if (mTaskEntity.entity == null) { + mTaskEntity.entity = entity; + } + mEntity = entity; } - @Override public void resume() { - super.resume(); + /** + * 如果任务存在,但是下载实体不存在,则通过下载地址获取下载实体 + * + * @param downloadUrl 下载地址 + */ + private DownloadEntity getEntity(String downloadUrl) { + DownloadEntity entity = + DownloadEntity.findFirst(DownloadEntity.class, "downloadUrl=? and isGroupChild='false'", + downloadUrl); + if (entity == null) { + entity = new DownloadEntity(); + entity.setDownloadUrl(downloadUrl); + entity.setGroupChild(false); + entity.save(); + } + File file = new File(entity.getDownloadPath()); + if (!file.exists()) { + entity.setState(IEntity.STATE_WAIT); + } + return entity; } /** @@ -65,53 +94,11 @@ public class DownloadTarget extends AbsTarget headers) { - super._addHeaders(headers); - return this; - } - /** * 下载任务是否存在 */ @Override public boolean taskExists() { - return DownloadTaskQueue.getInstance().getTask(entity.getDownloadUrl()) != null; - } - - /** - * 设置请求类型 - * - * @param requestEnum {@link RequestEnum} - */ - public DownloadTarget setRequestMode(RequestEnum requestEnum) { - super._setRequestMode(requestEnum); - return this; + return DownloadTaskQueue.getInstance().getTask(mEntity.getDownloadUrl()) != null; } /** @@ -122,8 +109,11 @@ public class DownloadTarget extends AbsTarget { +public class DownloadTask extends AbsNormalTask { public static final String TAG = "DownloadTask"; - private IDownloadListener mListener; - private Handler mOutHandler; - private IDownloadUtil mUtil; - private boolean isWait = false; + private DListener mListener; + private SimpleDownloadUtil mUtil; private DownloadTask(DownloadTaskEntity taskEntity, Handler outHandler) { - mEntity = taskEntity.downloadEntity; + mEntity = taskEntity.getEntity(); mOutHandler = outHandler; mContext = AriaManager.APP; - mListener = new DListener(mContext, this, mOutHandler); - mUtil = new DownloadUtil(mContext, taskEntity, mListener); + mListener = new DListener<>(this, mOutHandler); + mUtil = new SimpleDownloadUtil(taskEntity, mListener); } /** @@ -98,7 +90,6 @@ public class DownloadTask extends AbsTask { * 暂停任务,并让任务处于等待状态 */ @Override public void stopAndWait() { - super.stopAndWait(); stop(true); } @@ -115,13 +106,10 @@ public class DownloadTask extends AbsTask { * 开始下载 */ @Override public void start() { - isWait = false; + mListener.isWait = false; if (mUtil.isDownloading()) { Log.d(TAG, "任务正在下载"); } else { - if (mListener == null || isWait) { - mListener = new DListener(mContext, this, mOutHandler); - } mUtil.startDownload(); } } @@ -134,20 +122,15 @@ public class DownloadTask extends AbsTask { } private void stop(boolean isWait) { - this.isWait = isWait; + mListener.isWait = isWait; if (mUtil.isDownloading()) { mUtil.stopDownload(); } else { mEntity.setState(isWait ? IEntity.STATE_WAIT : IEntity.STATE_STOP); - mEntity.save(); + mEntity.update(); if (mOutHandler != null) { - mOutHandler.obtainMessage(DownloadSchedulers.STOP, this).sendToTarget(); + mOutHandler.obtainMessage(ISchedulers.STOP, this).sendToTarget(); } - // 发送停止下载的广播 - Intent intent = CommonUtil.createIntent(mContext.getPackageName(), Aria.ACTION_STOP); - intent.putExtra(Aria.CURRENT_LOCATION, mEntity.getCurrentProgress()); - intent.putExtra(Aria.DOWNLOAD_ENTITY, mEntity); - mContext.sendBroadcast(intent); } } @@ -155,20 +138,13 @@ public class DownloadTask extends AbsTask { * 取消下载 */ @Override public void cancel() { - if (!mEntity.isDownloadComplete()) { + if (!mEntity.isComplete()) { if (!mUtil.isDownloading()) { if (mOutHandler != null) { - mOutHandler.obtainMessage(DownloadSchedulers.CANCEL, this).sendToTarget(); + mOutHandler.obtainMessage(ISchedulers.CANCEL, this).sendToTarget(); } - //发送取消下载的广播 - Intent intent = CommonUtil.createIntent(mContext.getPackageName(), Aria.ACTION_CANCEL); - intent.putExtra(Aria.DOWNLOAD_ENTITY, mEntity); - mContext.sendBroadcast(intent); } mUtil.cancelDownload(); - mUtil.delConfigFile(); - mUtil.delTempFile(); - mEntity.deleteData(); } } @@ -178,7 +154,6 @@ public class DownloadTask extends AbsTask { String targetName; public Builder(String targetName, DownloadTaskEntity taskEntity) { - CheckUtil.checkTaskEntity(taskEntity); this.targetName = targetName; this.taskEntity = taskEntity; } @@ -196,162 +171,9 @@ public class DownloadTask extends AbsTask { public DownloadTask build() { DownloadTask task = new DownloadTask(taskEntity, outHandler); task.setTargetName(targetName); - taskEntity.downloadEntity.save(); + taskEntity.getEntity().save(); + taskEntity.save(); return task; } } - - /** - * 下载监听类 - */ - private static class DListener extends DownloadListener { - WeakReference outHandler; - WeakReference wTask; - Context context; - Intent sendIntent; - long lastLen = 0; //上一次发送长度 - long lastTime = 0; - long INTERVAL_TIME = 1000; //1m更新周期 - boolean isFirst = true; - DownloadEntity downloadEntity; - DownloadTask task; - boolean isOpenBroadCast = false; - boolean isConvertSpeed = false; - - DListener(Context context, DownloadTask task, Handler outHandler) { - this.context = context; - this.outHandler = new WeakReference<>(outHandler); - this.wTask = new WeakReference<>(task); - this.task = wTask.get(); - this.downloadEntity = this.task.getDownloadEntity(); - sendIntent = CommonUtil.createIntent(context.getPackageName(), Aria.ACTION_RUNNING); - sendIntent.putExtra(Aria.DOWNLOAD_ENTITY, downloadEntity); - final AriaManager manager = AriaManager.getInstance(context); - isOpenBroadCast = manager.getDownloadConfig().isOpenBreadCast(); - isConvertSpeed = manager.getDownloadConfig().isConvertSpeed(); - } - - @Override public void supportBreakpoint(boolean support) { - super.supportBreakpoint(support); - if (!support) { - sendInState2Target(ISchedulers.SUPPORT_BREAK_POINT); - sendIntent(Aria.ACTION_SUPPORT_BREAK_POINT, -1); - } - } - - @Override public void onPre() { - super.onPre(); - downloadEntity.setState(IEntity.STATE_PRE); - sendInState2Target(ISchedulers.PRE); - sendIntent(Aria.ACTION_PRE, -1); - } - - @Override public void onPostPre(long fileSize) { - super.onPostPre(fileSize); - downloadEntity.setFileSize(fileSize); - downloadEntity.setState(IEntity.STATE_POST_PRE); - sendInState2Target(ISchedulers.POST_PRE); - sendIntent(Aria.ACTION_POST_PRE, -1); - } - - @Override public void onResume(long resumeLocation) { - super.onResume(resumeLocation); - downloadEntity.setState(IEntity.STATE_RUNNING); - sendInState2Target(ISchedulers.RESUME); - sendIntent(Aria.ACTION_RESUME, resumeLocation); - } - - @Override public void onStart(long startLocation) { - super.onStart(startLocation); - downloadEntity.setState(IEntity.STATE_RUNNING); - sendInState2Target(ISchedulers.START); - sendIntent(Aria.ACTION_START, startLocation); - } - - @Override public void onProgress(long currentLocation) { - super.onProgress(currentLocation); - if (System.currentTimeMillis() - lastTime > INTERVAL_TIME) { - long speed = currentLocation - lastLen; - sendIntent.putExtra(Aria.CURRENT_LOCATION, currentLocation); - sendIntent.putExtra(Aria.CURRENT_SPEED, speed); - lastTime = System.currentTimeMillis(); - if (isFirst) { - speed = 0; - isFirst = false; - } - handleSpeed(speed); - downloadEntity.setCurrentProgress(currentLocation); - lastLen = currentLocation; - sendInState2Target(ISchedulers.RUNNING); - context.sendBroadcast(sendIntent); - } - } - - @Override public void onStop(long stopLocation) { - super.onStop(stopLocation); - downloadEntity.setState(task.isWait ? IEntity.STATE_WAIT : IEntity.STATE_STOP); - handleSpeed(0); - sendInState2Target(ISchedulers.STOP); - sendIntent(Aria.ACTION_STOP, stopLocation); - } - - @Override public void onCancel() { - super.onCancel(); - downloadEntity.setState(IEntity.STATE_CANCEL); - handleSpeed(0); - sendInState2Target(ISchedulers.CANCEL); - sendIntent(Aria.ACTION_CANCEL, -1); - downloadEntity.deleteData(); - } - - @Override public void onComplete() { - super.onComplete(); - downloadEntity.setState(IEntity.STATE_COMPLETE); - downloadEntity.setDownloadComplete(true); - handleSpeed(0); - sendInState2Target(ISchedulers.COMPLETE); - sendIntent(Aria.ACTION_COMPLETE, downloadEntity.getFileSize()); - } - - @Override public void onFail() { - super.onFail(); - downloadEntity.setFailNum(downloadEntity.getFailNum() + 1); - downloadEntity.setState(IEntity.STATE_FAIL); - handleSpeed(0); - sendInState2Target(ISchedulers.FAIL); - sendIntent(Aria.ACTION_FAIL, -1); - } - - private void handleSpeed(long speed) { - if (isConvertSpeed) { - downloadEntity.setConvertSpeed(CommonUtil.formatFileSize(speed) + "/s"); - } else { - downloadEntity.setSpeed(speed); - } - } - - /** - * 将任务状态发送给下载器 - * - * @param state {@link DownloadSchedulers#START} - */ - private void sendInState2Target(int state) { - if (outHandler.get() != null) { - outHandler.get().obtainMessage(state, task).sendToTarget(); - } - } - - private void sendIntent(String action, long location) { - downloadEntity.setDownloadComplete(action.equals(Aria.ACTION_COMPLETE)); - downloadEntity.setCurrentProgress(location); - downloadEntity.update(); - if (!isOpenBroadCast) return; - Intent intent = CommonUtil.createIntent(context.getPackageName(), action); - intent.putExtra(Aria.DOWNLOAD_ENTITY, downloadEntity); - if (location != -1) { - intent.putExtra(Aria.CURRENT_LOCATION, location); - } - context.sendBroadcast(intent); - } - } } \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTaskEntity.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTaskEntity.java index 75f6a17b..f5b9dbc0 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTaskEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTaskEntity.java @@ -16,22 +16,30 @@ package com.arialyy.aria.core.download; import com.arialyy.aria.core.inf.AbsTaskEntity; +import com.arialyy.aria.orm.OneToOne; /** * Created by lyy on 2017/1/23. * 下载任务实体 */ -public class DownloadTaskEntity extends AbsTaskEntity { +public class DownloadTaskEntity extends AbsTaskEntity { - public DownloadEntity downloadEntity; + @OneToOne(table = DownloadEntity.class, key = "downloadPath") public DownloadEntity entity; - public DownloadTaskEntity(){} + /** + * 所属的任务组组名,如果不属于任务组,则为null + */ + public String groupName = ""; - public DownloadTaskEntity(DownloadEntity downloadEntity) { - this.downloadEntity = downloadEntity; + /** + * 该任务是否属于任务组 + */ + public boolean isGroupTask = false; + + public DownloadTaskEntity() { } @Override public DownloadEntity getEntity() { - return downloadEntity; + return entity; } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadUtil.java deleted file mode 100644 index 2f08ba86..00000000 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadUtil.java +++ /dev/null @@ -1,493 +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.download; - -import android.content.Context; -import android.util.Log; -import android.util.SparseArray; -import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.util.BufferedRandomAccessFile; -import com.arialyy.aria.util.CheckUtil; -import com.arialyy.aria.util.CommonUtil; -import java.io.File; -import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.Properties; -import java.util.Set; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -/** - * Created by lyy on 2015/8/25. - * 下载工具类 - */ -class DownloadUtil implements IDownloadUtil, Runnable { - private static final String TAG = "DownloadUtil"; - /** - * 线程数 - */ - private int THREAD_NUM; - /** - * 小于1m的文件不启用多线程 - */ - private static final long SUB_LEN = 1024 * 1024; - //下载监听 - private IDownloadListener mListener; - private int mConnectTimeOut = 0; //连接超时时间 - private boolean isNewTask = true; - private boolean isSupportBreakpoint = true; - private Context mContext; - private DownloadEntity mDownloadEntity; - private DownloadTaskEntity mDownloadTaskEntity; - private ExecutorService mFixedThreadPool; - private File mDownloadFile; //下载的文件 - private File mConfigFile;//下载信息配置文件 - private SparseArray mTask = new SparseArray<>(); - private DownloadStateConstance CONSTANCE; - - DownloadUtil(Context context, DownloadTaskEntity entity, IDownloadListener downloadListener) { - mDownloadEntity = entity.downloadEntity; - mContext = context.getApplicationContext(); - mDownloadTaskEntity = entity; - mListener = downloadListener; - // 线程下载数改变后,新的下载才会生效 - //mFixedThreadPool = Executors.newFixedThreadPool(Integer.MAX_VALUE); - CONSTANCE = new DownloadStateConstance(); - init(); - } - - private void init() { - mConnectTimeOut = AriaManager.getInstance(mContext).getDownloadConfig().getConnectTimeOut(); - mDownloadFile = new File(mDownloadTaskEntity.downloadEntity.getDownloadPath()); - //读取已完成的线程数 - mConfigFile = new File(mContext.getFilesDir().getPath() - + AriaManager.DOWNLOAD_TEMP_DIR - + mDownloadFile.getName() - + ".properties"); - try { - if (!mConfigFile.exists()) { //记录文件被删除,则重新下载 - isNewTask = true; - CommonUtil.createFile(mConfigFile.getPath()); - } else { - isNewTask = !mDownloadFile.exists(); - } - } catch (Exception e) { - e.printStackTrace(); - failDownload("下载失败,记录文件被删除"); - } - } - - public IDownloadListener getListener() { - return mListener; - } - - /** - * 获取当前下载位置 - */ - @Override public long getCurrentLocation() { - return CONSTANCE.CURRENT_LOCATION; - } - - @Override public boolean isDownloading() { - return CONSTANCE.isDownloading; - } - - public void setMaxSpeed(double maxSpeed) { - for (int i = 0; i < THREAD_NUM; i++) { - SingleThreadTask task = (SingleThreadTask) mTask.get(i); - if (task != null) { - task.setMaxSpeed(maxSpeed); - } - } - } - - /** - * 取消下载 - */ - @Override public void cancelDownload() { - CONSTANCE.isCancel = true; - CONSTANCE.isDownloading = false; - if (mFixedThreadPool != null) { - mFixedThreadPool.shutdown(); - } - for (int i = 0; i < THREAD_NUM; i++) { - SingleThreadTask task = (SingleThreadTask) mTask.get(i); - if (task != null) { - task.cancel(); - } - } - } - - /** - * 停止下载 - */ - @Override public void stopDownload() { - CONSTANCE.isStop = true; - CONSTANCE.isDownloading = false; - if (mFixedThreadPool != null) { - mFixedThreadPool.shutdown(); - } - for (int i = 0; i < THREAD_NUM; i++) { - SingleThreadTask task = (SingleThreadTask) mTask.get(i); - if (task != null) { - task.stop(); - } - } - } - - /** - * 删除下载记录文件 - */ - @Override public void delConfigFile() { - if (mContext != null && mDownloadEntity != null) { - File dFile = new File(mDownloadEntity.getDownloadPath()); - File config = - new File(mContext.getFilesDir().getPath() + "/temp/" + dFile.getName() + ".properties"); - if (config.exists()) { - config.delete(); - } - } - } - - /** - * 删除temp文件 - */ - @Override public void delTempFile() { - if (mContext != null && mDownloadEntity != null) { - File dFile = new File(mDownloadEntity.getDownloadPath()); - if (dFile.exists()) { - dFile.delete(); - } - } - } - - /** - * 多线程断点续传下载文件,开始下载 - */ - @Override public void startDownload() { - CONSTANCE.cleanState(); - mListener.onPre(); - new Thread(this).start(); - } - - @Override public void resumeDownload() { - startDownload(); - } - - private void failDownload(String msg) { - Log.e(TAG, msg); - CONSTANCE.isDownloading = false; - stopDownload(); - mListener.onFail(); - } - - @Override public void run() { - try { - URL url = new URL(mDownloadEntity.getDownloadUrl()); - HttpURLConnection conn = ConnectionHelp.handleConnection(url); - conn = ConnectionHelp.setConnectParam(mDownloadTaskEntity, conn); - conn.setRequestProperty("Range", "bytes=" + 0 + "-"); - conn.setConnectTimeout(mConnectTimeOut); - conn.connect(); - handleConnect(conn); - } catch (IOException e) { - failDownload("下载失败【downloadUrl:" - + mDownloadEntity.getDownloadUrl() - + "】\n【filePath:" - + mDownloadFile.getPath() - + "】" - + CommonUtil.getPrintException(e)); - } - } - - /** - * 处理状态吗 - */ - private void handleConnect(HttpURLConnection conn) throws IOException { - int len = conn.getContentLength(); - //if (len < 0) { //网络被劫持时会出现这个问题 - // failDownload("下载失败,网络被劫持"); - // return; - //} - int code = conn.getResponseCode(); - //https://zh.wikipedia.org/wiki/HTTP%E7%8A%B6%E6%80%81%E7%A0%81 - //206支持断点 - if (code == HttpURLConnection.HTTP_PARTIAL) { - if (!checkLen(len)) return; - isSupportBreakpoint = true; - mListener.supportBreakpoint(true); - handleBreakpoint(conn); - } else if (code == HttpURLConnection.HTTP_OK) { - //在conn.setRequestProperty("Range", "bytes=" + 0 + "-");下,200为不支持断点状态 - if (!checkLen(len)) return; - isSupportBreakpoint = false; - mListener.supportBreakpoint(false); - Log.w(TAG, "该下载链接不支持断点下载"); - handleBreakpoint(conn); - } else if (code == HttpURLConnection.HTTP_NOT_FOUND) { - Log.w(TAG, "任务【" + mDownloadEntity.getDownloadUrl() + "】下载失败,错误码:404"); - mListener.onCancel(); - } else if (code == HttpURLConnection.HTTP_MOVED_TEMP - || code == HttpURLConnection.HTTP_MOVED_PERM - || code == HttpURLConnection.HTTP_SEE_OTHER) { - handle302Turn(conn); - } else { - failDownload("任务【" + mDownloadEntity.getDownloadUrl() + "】下载失败,错误码:" + code); - } - } - - /** - * 检查长度是否合法 - * - * @param len 从服务器获取的文件长度 - * @return true, 合法 - */ - private boolean checkLen(long len) { - if (len < 0) { - failDownload("任务【" + mDownloadEntity.getDownloadUrl() + "】下载失败,文件长度小于0"); - return false; - } - return true; - } - - /** - * 处理30x跳转 - */ - private void handle302Turn(HttpURLConnection conn) throws IOException { - String newUrl = conn.getHeaderField(mDownloadTaskEntity.redirectUrlKey); - Log.d(TAG, "30x跳转,新url为【" + newUrl + "】"); - mDownloadEntity.setRedirect(true); - mDownloadEntity.setRedirectUrl(newUrl); - mDownloadEntity.update(); - String cookies = conn.getHeaderField("Set-Cookie"); - conn = (HttpURLConnection) new URL(newUrl).openConnection(); - conn = ConnectionHelp.setConnectParam(mDownloadTaskEntity, conn); - conn.setRequestProperty("Cookie", cookies); - conn.setRequestProperty("Range", "bytes=" + 0 + "-"); - conn.setConnectTimeout(mConnectTimeOut); - conn.connect(); - - handleConnect(conn); - } - - /** - * 处理断点 - */ - private void handleBreakpoint(HttpURLConnection conn) throws IOException { - //不支持断点只能单线程下载 - if (!isSupportBreakpoint) { - handleNoSupportBreakpointDownload(conn); - return; - } - int fileLength = conn.getContentLength(); - Properties pro = createConfigFile(fileLength); - int blockSize = fileLength / THREAD_NUM; - int[] recordL = new int[THREAD_NUM]; - int rl = 0; - for (int i = 0; i < THREAD_NUM; i++) { - recordL[i] = -1; - } - for (int i = 0; i < THREAD_NUM; i++) { - long startL = i * blockSize, endL = (i + 1) * blockSize; - Object state = pro.getProperty(mDownloadFile.getName() + "_state_" + i); - if (state != null && Integer.parseInt(state + "") == 1) { //该线程已经完成 - if (resumeRecordLocation(i, startL, endL)) return; - continue; - } - //分配下载位置 - Object record = pro.getProperty(mDownloadFile.getName() + "_record_" + i); - //如果有记录,则恢复下载 - if (!isNewTask && record != null && Long.parseLong(record + "") > 0) { - Long r = Long.parseLong(record + ""); - CONSTANCE.CURRENT_LOCATION += r - startL; - Log.d(TAG, "任务【" + mDownloadEntity.getFileName() + "】线程__" + i + "__恢复下载"); - mListener.onChildResume(r); - startL = r; - recordL[rl] = i; - rl++; - } else { - handleNewTask(fileLength); - } - if (isNewTask) { - recordL[rl] = i; - rl++; - } - if (i == (THREAD_NUM - 1)) { - //最后一个线程的结束位置即为文件的总长度 - endL = fileLength; - } - addSingleTask(i, startL, endL, fileLength); - } - startSingleTask(recordL); - } - - /** - * 处理不支持断点的下载 - */ - private void handleNoSupportBreakpointDownload(HttpURLConnection conn) { - ConfigEntity entity = new ConfigEntity(); - long len = conn.getContentLength(); - entity.FILE_SIZE = len; - entity.DOWNLOAD_URL = mDownloadEntity.isRedirect() ? mDownloadEntity.getRedirectUrl() - : mDownloadEntity.getDownloadUrl(); - entity.TEMP_FILE = mDownloadFile; - entity.THREAD_ID = 0; - entity.START_LOCATION = 0; - entity.END_LOCATION = entity.FILE_SIZE; - entity.CONFIG_FILE_PATH = mConfigFile.getPath(); - entity.isSupportBreakpoint = isSupportBreakpoint; - entity.DOWNLOAD_TASK_ENTITY = mDownloadTaskEntity; - THREAD_NUM = 1; - CONSTANCE.THREAD_NUM = THREAD_NUM; - SingleThreadTask task = new SingleThreadTask(CONSTANCE, mListener, entity); - mTask.put(0, task); - mFixedThreadPool.execute(task); - mListener.onPostPre(len); - mListener.onStart(0); - } - - /** - * 创建配置文件 - */ - private Properties createConfigFile(long fileLength) throws IOException { - Properties pro = null; - //必须建一个文件 - CommonUtil.createFile(mDownloadFile.getPath()); - BufferedRandomAccessFile file = - new BufferedRandomAccessFile(new File(mDownloadFile.getPath()), "rwd", 8192); - //设置文件长度 - file.setLength(fileLength); - mListener.onPostPre(fileLength); - //分配每条线程的下载区间 - pro = CommonUtil.loadConfig(mConfigFile); - if (pro.isEmpty()) { - handleNewTask(fileLength); - } else { - Set keys = pro.keySet(); - int num = 0; - for (Object key : keys) { - if (String.valueOf(key).contains("_record_")) { - num++; - } - } - if (num == 0) { - handleNewTask(fileLength); - return pro; - } - THREAD_NUM = num; - for (int i = 0; i < THREAD_NUM; i++) { - if (pro.getProperty(mDownloadFile.getName() + "_record_" + i) == null) { - Object state = pro.getProperty(mDownloadFile.getName() + "_state_" + i); - if (state != null && Integer.parseInt(state + "") == 1) { - continue; - } - handleNewTask(fileLength); - return pro; - } - } - isNewTask = false; - } - return pro; - } - - /** - * 处理新任务 - */ - private void handleNewTask(long fileLength) { - isNewTask = true; - THREAD_NUM = fileLength < SUB_LEN ? 1 - : AriaManager.getInstance(mContext).getDownloadConfig().getThreadNum(); - } - - /** - * 恢复记录地址 - * - * @return true 表示下载完成 - */ - private boolean resumeRecordLocation(int i, long startL, long endL) { - CONSTANCE.CURRENT_LOCATION += endL - startL; - Log.d(TAG, "++++++++++ 线程_" + i + "_已经下载完成 ++++++++++"); - CONSTANCE.COMPLETE_THREAD_NUM++; - CONSTANCE.STOP_NUM++; - CONSTANCE.CANCEL_NUM++; - if (CONSTANCE.isComplete()) { - if (mConfigFile.exists()) { - mConfigFile.delete(); - } - mListener.onComplete(); - CONSTANCE.isDownloading = false; - return true; - } - return false; - } - - /** - * 创建单线程任务 - */ - private void addSingleTask(int i, long startL, long endL, long fileLength) { - ConfigEntity entity = new ConfigEntity(); - entity.FILE_SIZE = fileLength; - entity.DOWNLOAD_URL = mDownloadEntity.isRedirect() ? mDownloadEntity.getRedirectUrl() - : mDownloadEntity.getDownloadUrl(); - entity.TEMP_FILE = mDownloadFile; - entity.THREAD_ID = i; - entity.START_LOCATION = startL; - entity.END_LOCATION = endL; - entity.CONFIG_FILE_PATH = mConfigFile.getPath(); - entity.isSupportBreakpoint = isSupportBreakpoint; - entity.DOWNLOAD_TASK_ENTITY = mDownloadTaskEntity; - CONSTANCE.THREAD_NUM = THREAD_NUM; - SingleThreadTask task = new SingleThreadTask(CONSTANCE, mListener, entity); - mTask.put(i, task); - } - - /** - * 启动单线程下载任务 - */ - private void startSingleTask(int[] recordL) { - if (CONSTANCE.CURRENT_LOCATION > 0) { - mListener.onResume(CONSTANCE.CURRENT_LOCATION); - } else { - mListener.onStart(CONSTANCE.CURRENT_LOCATION); - } - mFixedThreadPool = Executors.newFixedThreadPool(recordL.length); - for (int l : recordL) { - if (l == -1) continue; - Runnable task = mTask.get(l); - if (task != null) { - mFixedThreadPool.execute(task); - } - } - } - - /** - * 子线程下载信息类 - */ - final static class ConfigEntity { - //文件大小 - int THREAD_ID; - long FILE_SIZE; - long START_LOCATION; - long END_LOCATION; - File TEMP_FILE; - String DOWNLOAD_URL; - String CONFIG_FILE_PATH; - DownloadTaskEntity DOWNLOAD_TASK_ENTITY; - boolean isSupportBreakpoint = true; - } -} \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/ChildThreadConfigEntity.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/ChildThreadConfigEntity.java new file mode 100644 index 00000000..94b865ea --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/ChildThreadConfigEntity.java @@ -0,0 +1,24 @@ +package com.arialyy.aria.core.download.downloader; + +import com.arialyy.aria.core.download.DownloadTaskEntity; +import java.io.File; + +/** + * 子线程下载信息类 + */ +final class ChildThreadConfigEntity { + //线程Id + int THREAD_ID; + //下载文件大小 + long FILE_SIZE; + //子线程启动下载位置 + long START_LOCATION; + //子线程结束下载位置 + long END_LOCATION; + //下载路径 + File TEMP_FILE; + String DOWNLOAD_URL; + String CONFIG_FILE_PATH; + DownloadTaskEntity DOWNLOAD_TASK_ENTITY; + boolean IS_SUPPORT_BREAK_POINT = true; +} \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/ConnectionHelp.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/ConnectionHelp.java similarity index 97% rename from Aria/src/main/java/com/arialyy/aria/core/download/ConnectionHelp.java rename to Aria/src/main/java/com/arialyy/aria/core/download/downloader/ConnectionHelp.java index e05f0aee..028db87c 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/ConnectionHelp.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/ConnectionHelp.java @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download; +package com.arialyy.aria.core.download.downloader; +import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.util.SSLContextUtil; import java.io.IOException; import java.net.HttpURLConnection; diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/DownloadGroupUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/DownloadGroupUtil.java new file mode 100644 index 00000000..d8a44363 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/DownloadGroupUtil.java @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.download.downloader; + +import android.util.SparseArray; +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.download.DownloadGroupTaskEntity; +import com.arialyy.aria.core.download.DownloadTaskEntity; +import com.arialyy.aria.core.inf.IEntity; +import com.arialyy.aria.orm.DbEntity; +import com.arialyy.aria.util.CommonUtil; +import java.io.File; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * Created by AriaL on 2017/6/30. + * 任务组下载工具 + */ +public class DownloadGroupUtil implements IDownloadUtil { + private final String TAG = "DownloadGroupUtil"; + /** + * 任务组所有任务总大小 + */ + private long mTotalSize = 0; + private long mCurrentLocation = 0; + private ExecutorService mInfoPool; + private ExecutorService mExePool; + private IDownloadListener mListener; + private DownloadGroupTaskEntity mTaskEntity; + private boolean isRunning = true; + private Timer mTimer; + /** + * 初始化完成的任务书数 + */ + private int mInitNum = 0; + /** + * 初始化失败的任务数 + */ + private int mInitFailNum = 0; + /** + * 保存所有没有下载完成的任务,key为下载地址 + */ + private Map mExeMap = new HashMap<>(); + + /** + * 下载失败的映射表,key为下载地址 + */ + private Map mFailMap = new HashMap<>(); + + /** + * 下载器映射表,key为下载地址 + */ + private Map mDownloaderMap = new HashMap<>(); + + /** + * 文件信息回调组 + */ + private SparseArray mFileInfoCallbacks = new SparseArray<>(); + /** + * 该任务组对应的所有任务 + */ + private Map mTasksMap = new HashMap<>(); + //已经完成的任务数 + private int mCompleteNum = 0; + //失败的任务数 + private int mFailNum = 0; + //实际的下载任务数 + private int mActualTaskNum = 0; + + public DownloadGroupUtil(IDownloadListener listener, DownloadGroupTaskEntity taskEntity) { + mListener = listener; + mTaskEntity = taskEntity; + mInfoPool = Executors.newCachedThreadPool(); + mExePool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); + mActualTaskNum = mTaskEntity.entity.getSubTask().size(); + List tasks = + DbEntity.findDatas(DownloadTaskEntity.class, "groupName=?", mTaskEntity.key); + if (tasks != null && !tasks.isEmpty()) { + for (DownloadTaskEntity te : tasks) { + mTasksMap.put(te.getEntity().getDownloadUrl(), te); + } + } + for (DownloadEntity entity : mTaskEntity.entity.getSubTask()) { + File file = new File(entity.getDownloadPath()); + if (entity.isComplete() && file.exists()) { + mTotalSize += entity.getFileSize(); + mCompleteNum++; + mInitNum++; + mCurrentLocation += entity.getFileSize(); + } else { + mExeMap.put(entity.getDownloadUrl(), createChildDownloadTask(entity)); + mCurrentLocation += entity.getCurrentProgress(); + } + } + } + + @Override public long getFileSize() { + return mTotalSize; + } + + @Override public long getCurrentLocation() { + return mCurrentLocation; + } + + @Override public boolean isDownloading() { + return isRunning; + } + + @Override public void cancelDownload() { + isRunning = false; + closeTimer(); + mListener.onCancel(); + if (!mInfoPool.isShutdown()) { + mInfoPool.shutdown(); + } + if (!mExePool.isShutdown()) { + mExePool.shutdown(); + } + + Set keys = mDownloaderMap.keySet(); + for (String key : keys) { + Downloader dt = mDownloaderMap.get(key); + if (dt != null) { + dt.cancelDownload(); + } + } + delDownloadInfo(); + mTaskEntity.deleteData(); + } + + /** + * 删除所有子任务的下载信息 + */ + private void delDownloadInfo() { + List tasks = + DbEntity.findDatas(DownloadTaskEntity.class, "groupName=?", mTaskEntity.key); + if (tasks == null || tasks.isEmpty()) return; + for (DownloadTaskEntity taskEntity : tasks) { + CommonUtil.delDownloadTaskConfig(taskEntity.removeFile, taskEntity); + } + } + + @Override public void stopDownload() { + isRunning = false; + closeTimer(); + mListener.onStop(mCurrentLocation); + if (!mInfoPool.isShutdown()) { + mInfoPool.shutdown(); + } + if (!mExePool.isShutdown()) { + mExePool.shutdown(); + } + + Set keys = mDownloaderMap.keySet(); + for (String key : keys) { + Downloader dt = mDownloaderMap.get(key); + if (dt != null) { + dt.stopDownload(); + } + } + } + + @Override public void startDownload() { + isRunning = true; + Set keys = mExeMap.keySet(); + mListener.onPre(); + for (String key : keys) { + DownloadTaskEntity taskEntity = mExeMap.get(key); + if (taskEntity != null) { + mInfoPool.execute(createFileInfoThread(taskEntity)); + } + } + } + + @Override public void resumeDownload() { + startDownload(); + mListener.onResume(mCurrentLocation); + } + + /** + * 创建文件信息获取线程 + */ + private FileInfoThread createFileInfoThread(DownloadTaskEntity taskEntity) { + FileInfoThread.OnFileInfoCallback callback = mFileInfoCallbacks.get(taskEntity.hashCode()); + + if (callback == null) { + callback = new FileInfoThread.OnFileInfoCallback() { + int failNum = 0; + + @Override public void onComplete(String url, int code) { + DownloadTaskEntity te = mExeMap.get(url); + if (te != null) { + mTotalSize += te.getEntity().getFileSize(); + startChildDownload(te); + } + mInitNum++; + if (mInitNum + mInitFailNum == mTaskEntity.getEntity().getSubTask().size()) { + startRunningFlow(); + } + } + + @Override public void onFail(String url, String errorMsg) { + DownloadTaskEntity te = mExeMap.get(url); + if (te != null) { + mFailMap.put(url, te); + mFileInfoCallbacks.put(te.hashCode(), this); + } + //404链接不重试下载 + if (failNum < 10 && !errorMsg.contains("错误码:404") && !errorMsg.contains( + "UnknownHostException")) { + mInfoPool.execute(createFileInfoThread(te)); + } else { + mInitFailNum++; + mActualTaskNum--; + if (mActualTaskNum < 0) mActualTaskNum = 0; + } + failNum++; + if (mInitNum + mInitFailNum == mTaskEntity.getEntity().getSubTask().size()) { + startRunningFlow(); + } + } + }; + } + return new FileInfoThread(taskEntity, callback); + } + + private void closeTimer() { + if (mTimer != null) { + mTimer.purge(); + mTimer.cancel(); + } + } + + /** + * 开始进度流程 + */ + private void startRunningFlow() { + closeTimer(); + mListener.onPostPre(mTotalSize); + mListener.onStart(mCurrentLocation); + mTimer = new Timer(true); + mTimer.schedule(new TimerTask() { + @Override public void run() { + if (mCurrentLocation >= 0) { + mListener.onProgress(mCurrentLocation); + } + } + }, 0, 1000); + } + + /** + * 启动子任务下载器 + */ + private void startChildDownload(DownloadTaskEntity taskEntity) { + ChildDownloadListener listener = new ChildDownloadListener(taskEntity); + Downloader dt = new Downloader(listener, taskEntity); + mDownloaderMap.put(taskEntity.getEntity().getDownloadUrl(), dt); + if (mExePool.isShutdown()) return; + mExePool.execute(dt); + } + + /** + * 创建子任务下载信息 + */ + private DownloadTaskEntity createChildDownloadTask(DownloadEntity entity) { + DownloadTaskEntity taskEntity = mTasksMap.get(entity.getDownloadUrl()); + if (taskEntity != null) { + taskEntity.entity = entity; + return taskEntity; + } + taskEntity = new DownloadTaskEntity(); + taskEntity.entity = entity; + taskEntity.headers = mTaskEntity.headers; + taskEntity.requestEnum = mTaskEntity.requestEnum; + taskEntity.redirectUrlKey = mTaskEntity.redirectUrlKey; + taskEntity.removeFile = mTaskEntity.removeFile; + taskEntity.groupName = mTaskEntity.key; + taskEntity.isGroupTask = true; + taskEntity.key = entity.getDownloadPath(); + taskEntity.save(); + return taskEntity; + } + + /** + * 子任务事件监听 + */ + private class ChildDownloadListener extends DownloadListener { + + DownloadTaskEntity taskEntity; + DownloadEntity entity; + + long lastLen = 0; + + ChildDownloadListener(DownloadTaskEntity entity) { + this.taskEntity = entity; + this.entity = taskEntity.getEntity(); + } + + @Override public void onPre() { + saveData(IEntity.STATE_PRE, -1); + } + + @Override public void onPostPre(long fileSize) { + entity.setFileSize(fileSize); + entity.setConvertFileSize(CommonUtil.formatFileSize(fileSize)); + saveData(IEntity.STATE_POST_PRE, -1); + } + + @Override public void onResume(long resumeLocation) { + saveData(IEntity.STATE_POST_PRE, IEntity.STATE_RUNNING); + lastLen = resumeLocation; + } + + @Override public void onStart(long startLocation) { + saveData(IEntity.STATE_POST_PRE, IEntity.STATE_RUNNING); + lastLen = startLocation; + } + + @Override public void onProgress(long currentLocation) { + long speed = currentLocation - lastLen; + mCurrentLocation += speed; + lastLen = currentLocation; + entity.setCurrentProgress(currentLocation); + handleSpeed(speed); + } + + @Override public void onStop(long stopLocation) { + saveData(IEntity.STATE_STOP, stopLocation); + handleSpeed(0); + } + + @Override public void onCancel() { + saveData(IEntity.STATE_CANCEL, -1); + handleSpeed(0); + } + + @Override public void onComplete() { + saveData(IEntity.STATE_COMPLETE, entity.getFileSize()); + mCompleteNum++; + if (mCompleteNum + mFailNum >= mActualTaskNum) { + closeTimer(); + mListener.onComplete(); + } + handleSpeed(0); + } + + @Override public void onFail() { + entity.setFailNum(entity.getFailNum() + 1); + saveData(IEntity.STATE_FAIL, -1); + handleSpeed(0); + reTry(); + } + + /** + * 失败后重试下载,如果失败次数超过5次,不再重试 + */ + private void reTry() { + if (entity.getFailNum() < 5) { + Downloader dt = mDownloaderMap.get(entity.getDownloadUrl()); + dt.startDownload(); + } else { + mFailNum++; + } + } + + private void handleSpeed(long speed) { + entity.setSpeed(speed); + entity.setConvertSpeed(speed <= 0 ? "" : CommonUtil.formatFileSize(speed) + "/s"); + } + + private void saveData(int state, long location) { + entity.setState(state); + entity.setComplete(state == IEntity.STATE_COMPLETE); + if (entity.isComplete()) { + entity.setCompleteTime(System.currentTimeMillis()); + entity.setCurrentProgress(entity.getFileSize()); + } else { + entity.setCurrentProgress(location); + } + entity.update(); + } + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadListener.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/DownloadListener.java similarity index 92% rename from Aria/src/main/java/com/arialyy/aria/core/download/DownloadListener.java rename to Aria/src/main/java/com/arialyy/aria/core/download/downloader/DownloadListener.java index a2cbf237..f8d2349e 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadListener.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/DownloadListener.java @@ -14,12 +14,13 @@ * limitations under the License. */ -package com.arialyy.aria.core.download; +package com.arialyy.aria.core.download.downloader; + /** * @author lyy */ -class DownloadListener implements IDownloadListener { +public class DownloadListener implements IDownloadListener { @Override public void onResume(long resumeLocation) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/Downloader.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/Downloader.java new file mode 100644 index 00000000..a7fa8721 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/Downloader.java @@ -0,0 +1,420 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.download.downloader; + +import android.content.Context; +import android.util.Log; +import android.util.SparseArray; +import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.download.DownloadTaskEntity; +import com.arialyy.aria.orm.DbEntity; +import com.arialyy.aria.util.BufferedRandomAccessFile; +import com.arialyy.aria.util.CommonUtil; +import java.io.File; +import java.io.IOException; +import java.util.Properties; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * Created by AriaL on 2017/7/1. + * 文件下载器 + */ +class Downloader implements Runnable, IDownloadUtil { + private final String TAG = "Downloader"; + private IDownloadListener mListener; + private DownloadTaskEntity mTaskEntity; + private DownloadEntity mEntity; + private ExecutorService mFixedThreadPool; + private File mConfigFile;//下载信息配置文件 + private Context mContext; + private File mTempFile; //下载的文件 + private boolean isNewTask = true; + private int mThreadNum, mRealThreadNum; + private StateConstance mConstance; + private SparseArray mTask = new SparseArray<>(); + + /** + * 小于1m的文件不启用多线程 + */ + private static final long SUB_LEN = 1024 * 1024; + private Timer mTimer; + + Downloader(IDownloadListener listener, DownloadTaskEntity taskEntity) { + mListener = listener; + mTaskEntity = taskEntity; + mEntity = mTaskEntity.getEntity(); + mContext = AriaManager.APP; + mConstance = new StateConstance(); + } + + void setMaxSpeed(double maxSpeed) { + for (int i = 0; i < mThreadNum; i++) { + SingleThreadTask task = (SingleThreadTask) mTask.get(i); + if (task != null) { + task.setMaxSpeed(maxSpeed); + } + } + } + + public StateConstance getConstance() { + return mConstance; + } + + @Override public void run() { + startFlow(); + } + + /** + * 开始下载流程 + */ + private void startFlow() { + checkTask(); + mListener.onPostPre(mEntity.getFileSize()); + mConstance.cleanState(); + mConstance.isDownloading = true; + if (!mTaskEntity.isSupportBP) { + mThreadNum = 1; + mConstance.THREAD_NUM = mThreadNum; + handleNoSupportBreakpointDownload(); + } else { + mThreadNum = isNewTask ? (mEntity.getFileSize() <= SUB_LEN ? 1 + : AriaManager.getInstance(mContext).getDownloadConfig().getThreadNum()) : mRealThreadNum; + mConstance.THREAD_NUM = mThreadNum; + mFixedThreadPool = Executors.newFixedThreadPool(mThreadNum); + handleBreakpoint(); + } + startTimer(); + } + + /** + * 启动进度获取定时器 + */ + private void startTimer() { + mTimer = new Timer(true); + mTimer.schedule(new TimerTask() { + @Override public void run() { + if (mConstance.isComplete()) { + closeTimer(); + } else if (mConstance.CURRENT_LOCATION >= 0) { + mListener.onProgress(mConstance.CURRENT_LOCATION); + } + } + }, 0, 1000); + } + + private void closeTimer() { + if (mTimer != null) { + mTimer.purge(); + mTimer.cancel(); + } + } + + @Override public long getFileSize() { + return mEntity.getFileSize(); + } + + /** + * 获取当前下载位置 + */ + @Override public long getCurrentLocation() { + return mConstance.CURRENT_LOCATION; + } + + @Override public boolean isDownloading() { + return mConstance.isDownloading; + } + + @Override public void cancelDownload() { + closeTimer(); + mConstance.isCancel = true; + mConstance.isDownloading = false; + if (mFixedThreadPool != null) { + mFixedThreadPool.shutdown(); + } + for (int i = 0; i < mThreadNum; i++) { + SingleThreadTask task = (SingleThreadTask) mTask.get(i); + if (task != null) { + task.cancel(); + } + } + CommonUtil.delDownloadTaskConfig(mTaskEntity.removeFile, mTaskEntity); + } + + @Override public void stopDownload() { + closeTimer(); + if (mConstance.isComplete()) return; + mConstance.isStop = true; + mConstance.isDownloading = false; + if (mFixedThreadPool != null) { + mFixedThreadPool.shutdown(); + } + for (int i = 0; i < mThreadNum; i++) { + SingleThreadTask task = (SingleThreadTask) mTask.get(i); + if (task != null) { + task.stop(); + } + } + } + + /** + * 直接调用的时候会自动启动线程执行 + */ + @Override public void startDownload() { + new Thread(this).start(); + } + + @Override public void resumeDownload() { + startDownload(); + } + + /** + * 返回该下载器的 + */ + public IDownloadListener getListener() { + return mListener; + } + + /** + * 检查任务是否是新任务,新任务条件: + * 1、文件不存在 + * 2、下载记录文件不存在 + * 3、下载记录文件缺失或不匹配 + * 4、数据库记录不存在 + * 5、不支持断点,则是新任务 + */ + private void checkTask() { + if (!mTaskEntity.isSupportBP) { + isNewTask = true; + return; + } + mConfigFile = new File(mContext.getFilesDir().getPath() + + AriaManager.DOWNLOAD_TEMP_DIR + + mEntity.getFileName() + + ".properties"); + mTempFile = new File(mEntity.getDownloadPath()); + if (!mConfigFile.exists()) { //记录文件被删除,则重新下载 + isNewTask = true; + CommonUtil.createFile(mConfigFile.getPath()); + } else if (!mTempFile.exists()) { + isNewTask = true; + } else if (DbEntity.findFirst(DownloadEntity.class, "downloadUrl=?", mEntity.getDownloadUrl()) + == null) { + isNewTask = true; + } else { + isNewTask = checkConfigFile(); + } + } + + /** + * 检查记录文件,如果是新任务返回{@code true},否则返回{@code false} + */ + private boolean checkConfigFile() { + Properties pro = CommonUtil.loadConfig(mConfigFile); + if (pro.isEmpty()) { + return true; + } + Set keys = pro.keySet(); + int num = 0; + for (Object key : keys) { + if (String.valueOf(key).contains("_record_")) { + num++; + } + } + if (num == 0) { + return true; + } + mRealThreadNum = num; + for (int i = 0; i < mRealThreadNum; i++) { + if (pro.getProperty(mTempFile.getName() + "_record_" + i) == null) { + Object state = pro.getProperty(mTempFile.getName() + "_state_" + i); + if (state != null && Integer.parseInt(state + "") == 1) { + continue; + } + return true; + } + } + return false; + } + + /** + * 恢复记录地址 + * + * @return true 表示下载完成 + */ + private boolean resumeRecordLocation(int i, long startL, long endL) { + mConstance.CURRENT_LOCATION += endL - startL; + Log.d(TAG, "++++++++++ 线程_" + i + "_已经下载完成 ++++++++++"); + mConstance.COMPLETE_THREAD_NUM++; + mConstance.STOP_NUM++; + mConstance.CANCEL_NUM++; + if (mConstance.isComplete()) { + if (mConfigFile.exists()) { + mConfigFile.delete(); + } + mListener.onComplete(); + mConstance.isDownloading = false; + return true; + } + return false; + } + + /** + * 创建单线程任务 + */ + private void addSingleTask(int i, long startL, long endL, long fileLength) { + ChildThreadConfigEntity entity = new ChildThreadConfigEntity(); + entity.FILE_SIZE = fileLength; + entity.DOWNLOAD_URL = + mEntity.isRedirect() ? mEntity.getRedirectUrl() : mEntity.getDownloadUrl(); + entity.TEMP_FILE = mTempFile; + entity.THREAD_ID = i; + entity.START_LOCATION = startL; + entity.END_LOCATION = endL; + entity.CONFIG_FILE_PATH = mConfigFile.getPath(); + entity.IS_SUPPORT_BREAK_POINT = mTaskEntity.isSupportBP; + entity.DOWNLOAD_TASK_ENTITY = mTaskEntity; + SingleThreadTask task = new SingleThreadTask(mConstance, mListener, entity); + mTask.put(i, task); + } + + /** + * 启动单线程下载任务 + */ + private void startSingleTask(int[] recordL) { + if (mConstance.CURRENT_LOCATION > 0) { + mListener.onResume(mConstance.CURRENT_LOCATION); + } else { + mListener.onStart(mConstance.CURRENT_LOCATION); + } + mFixedThreadPool = Executors.newFixedThreadPool(recordL.length); + for (int l : recordL) { + if (l == -1) continue; + Runnable task = mTask.get(l); + if (task != null) { + mFixedThreadPool.execute(task); + } + } + } + + /** + * 处理断点 + */ + private void handleBreakpoint() { + long fileLength = mEntity.getFileSize(); + Properties pro = CommonUtil.loadConfig(mConfigFile); + int blockSize = (int) (fileLength / mThreadNum); + int[] recordL = new int[mThreadNum]; + for (int i = 0; i < mThreadNum; i++) { + recordL[i] = -1; + } + int rl = 0; + if (isNewTask) { + createNewFile(fileLength); + } + for (int i = 0; i < mThreadNum; i++) { + long startL = i * blockSize, endL = (i + 1) * blockSize; + Object state = pro.getProperty(mTempFile.getName() + "_state_" + i); + if (state != null && Integer.parseInt(state + "") == 1) { //该线程已经完成 + if (resumeRecordLocation(i, startL, endL)) return; + continue; + } + //分配下载位置 + Object record = pro.getProperty(mTempFile.getName() + "_record_" + i); + //如果有记录,则恢复下载 + if (!isNewTask && record != null && Long.parseLong(record + "") >= 0) { + Long r = Long.parseLong(record + ""); + mConstance.CURRENT_LOCATION += r - startL; + Log.d(TAG, "任务【" + mEntity.getFileName() + "】线程__" + i + "__恢复下载"); + mListener.onChildResume(r); + startL = r; + recordL[rl] = i; + rl++; + } else { + recordL[rl] = i; + rl++; + } + if (i == (mThreadNum - 1)) { + //最后一个线程的结束位置即为文件的总长度 + endL = fileLength; + } + addSingleTask(i, startL, endL, fileLength); + } + startSingleTask(recordL); + } + + /** + * 创建新的下载文件 + */ + private void createNewFile(long fileLength) { + CommonUtil.createFile(mTempFile.getPath()); + BufferedRandomAccessFile file = null; + try { + file = new BufferedRandomAccessFile(new File(mTempFile.getPath()), "rwd", 8192); + //设置文件长度 + file.setLength(fileLength); + } catch (IOException e) { + failDownload("下载失败【downloadUrl:" + + mEntity.getDownloadUrl() + + "】\n【filePath:" + + mEntity.getDownloadPath() + + "】\n" + + CommonUtil.getPrintException(e)); + } finally { + if (file != null) { + try { + file.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + /** + * 处理不支持断点的下载 + */ + private void handleNoSupportBreakpointDownload() { + ChildThreadConfigEntity entity = new ChildThreadConfigEntity(); + long len = mEntity.getFileSize(); + entity.FILE_SIZE = len; + entity.DOWNLOAD_URL = + mEntity.isRedirect() ? mEntity.getRedirectUrl() : mEntity.getDownloadUrl(); + entity.TEMP_FILE = mTempFile; + entity.THREAD_ID = 0; + entity.START_LOCATION = 0; + entity.END_LOCATION = entity.FILE_SIZE; + entity.CONFIG_FILE_PATH = mConfigFile.getPath(); + entity.IS_SUPPORT_BREAK_POINT = mTaskEntity.isSupportBP; + entity.DOWNLOAD_TASK_ENTITY = mTaskEntity; + SingleThreadTask task = new SingleThreadTask(mConstance, mListener, entity); + mTask.put(0, task); + mFixedThreadPool.execute(task); + mListener.onPostPre(len); + mListener.onStart(0); + } + + private void failDownload(String errorMsg) { + closeTimer(); + Log.e(TAG, errorMsg); + mConstance.isDownloading = false; + mListener.onFail(); + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FileInfoThread.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FileInfoThread.java new file mode 100644 index 00000000..90bb447c --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FileInfoThread.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.download.downloader; + +import android.text.TextUtils; +import android.util.Log; +import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.download.DownloadTaskEntity; +import com.arialyy.aria.util.CommonUtil; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLDecoder; + +/** + * 下载文件信息获取 + */ +class FileInfoThread implements Runnable { + private final String TAG = "FileInfoThread"; + private DownloadEntity mEntity; + private DownloadTaskEntity mTaskEntity; + private int mConnectTimeOut; + private OnFileInfoCallback onFileInfoListener; + + interface OnFileInfoCallback { + /** + * 处理完成 + * + * @param code 状态码 + */ + void onComplete(String url, int code); + + /** + * 请求失败 + * + * @param errorMsg 错误信息 + */ + void onFail(String url, String errorMsg); + } + + FileInfoThread(DownloadTaskEntity taskEntity, OnFileInfoCallback callback) { + this.mTaskEntity = taskEntity; + mEntity = taskEntity.getEntity(); + mConnectTimeOut = + AriaManager.getInstance(AriaManager.APP).getDownloadConfig().getConnectTimeOut(); + onFileInfoListener = callback; + } + + @Override public void run() { + HttpURLConnection conn = null; + try { + URL url = new URL(mEntity.getDownloadUrl()); + conn = ConnectionHelp.handleConnection(url); + conn = ConnectionHelp.setConnectParam(mTaskEntity, conn); + conn.setRequestProperty("Range", "bytes=" + 0 + "-"); + conn.setConnectTimeout(mConnectTimeOut); + conn.connect(); + handleConnect(conn); + } catch (IOException e) { + failDownload("下载失败【downloadUrl:" + + mEntity.getDownloadUrl() + + "】\n【filePath:" + + mEntity.getDownloadPath() + + "】\n" + + CommonUtil.getPrintException(e)); + } finally { + if (conn != null) { + conn.disconnect(); + } + } + } + + private void handleConnect(HttpURLConnection conn) throws IOException { + int len = conn.getContentLength(); + int code = conn.getResponseCode(); + boolean isComplete = false; + if (TextUtils.isEmpty(mEntity.getMd5Code())) { + String md5Code = conn.getHeaderField(mTaskEntity.md5Key); + mEntity.setMd5Code(md5Code); + } + String disposition = conn.getHeaderField(mTaskEntity.dispositionKey); + if (!TextUtils.isEmpty(disposition)) { + mEntity.setDisposition(disposition); + if (disposition.contains(mTaskEntity.dispositionFileKey)) { + String[] infos = disposition.split("="); + mEntity.setServerFileName(URLDecoder.decode(infos[1], "utf-8")); + } + } + + mTaskEntity.code = code; + if (code == HttpURLConnection.HTTP_PARTIAL) { + if (!checkLen(len)) return; + mEntity.setFileSize(len); + mTaskEntity.isSupportBP = true; + isComplete = true; + } else if (code == HttpURLConnection.HTTP_OK) { + if (!checkLen(len)) return; + mEntity.setFileSize(len); + mTaskEntity.isSupportBP = false; + isComplete = true; + } else if (code == HttpURLConnection.HTTP_NOT_FOUND) { + failDownload("任务【" + mEntity.getDownloadUrl() + "】下载失败,错误码:404"); + } else if (code == HttpURLConnection.HTTP_MOVED_TEMP + || code == HttpURLConnection.HTTP_MOVED_PERM + || code == HttpURLConnection.HTTP_SEE_OTHER) { + mTaskEntity.redirectUrl = conn.getHeaderField(mTaskEntity.redirectUrlKey); + mEntity.setRedirect(true); + mEntity.setRedirectUrl(mTaskEntity.redirectUrl); + handle302Turn(conn); + } else { + failDownload("任务【" + mEntity.getDownloadUrl() + "】下载失败,错误码:" + code); + } + if (isComplete) { + if (onFileInfoListener != null) { + onFileInfoListener.onComplete(mEntity.getDownloadUrl(), code); + } + mEntity.update(); + mTaskEntity.update(); + } + } + + /** + * 处理30x跳转 + */ + private void handle302Turn(HttpURLConnection conn) throws IOException { + String newUrl = conn.getHeaderField(mTaskEntity.redirectUrlKey); + Log.d(TAG, "30x跳转,location【 " + mTaskEntity.redirectUrlKey + "】" + "新url为【" + newUrl + "】"); + if (TextUtils.isEmpty(newUrl) || newUrl.equalsIgnoreCase("null")) { + if (onFileInfoListener != null) { + onFileInfoListener.onFail(mEntity.getDownloadUrl(), "获取重定向链接失败"); + } + return; + } + String cookies = conn.getHeaderField("Set-Cookie"); + conn = (HttpURLConnection) new URL(newUrl).openConnection(); + conn = ConnectionHelp.setConnectParam(mTaskEntity, conn); + conn.setRequestProperty("Cookie", cookies); + conn.setRequestProperty("Range", "bytes=" + 0 + "-"); + conn.setConnectTimeout(mConnectTimeOut); + conn.connect(); + handleConnect(conn); + conn.disconnect(); + } + + /** + * 检查长度是否合法 + * + * @param len 从服务器获取的文件长度 + * @return true, 合法 + */ + private boolean checkLen(long len) { + if (len < 0) { + failDownload("任务【" + mEntity.getDownloadUrl() + "】下载失败,文件长度小于0"); + return false; + } + return true; + } + + private void failDownload(String errorMsg) { + Log.e(TAG, errorMsg); + if (onFileInfoListener != null) { + onFileInfoListener.onFail(mEntity.getDownloadUrl(), errorMsg); + } + } +} \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/IDownloadListener.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/IDownloadListener.java new file mode 100644 index 00000000..aa6f3e2d --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/IDownloadListener.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.arialyy.aria.core.download.downloader; + +import com.arialyy.aria.core.inf.IEventListener; + +/** + * 下载监听 + */ +interface IDownloadListener extends IEventListener { + + /** + * 支持断点回调 + * + * @param support true,支持;false 不支持 + */ + void supportBreakpoint(boolean support); + + /** + * 单一线程的结束位置 + */ + void onChildComplete(long finishLocation); + + /** + * 子程恢复下载的位置 + */ + void onChildResume(long resumeLocation); +} \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/IDownloadUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/IDownloadUtil.java similarity index 67% rename from Aria/src/main/java/com/arialyy/aria/core/download/IDownloadUtil.java rename to Aria/src/main/java/com/arialyy/aria/core/download/downloader/IDownloadUtil.java index 471d0048..5d2c7456 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/IDownloadUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/IDownloadUtil.java @@ -14,58 +14,48 @@ * limitations under the License. */ -package com.arialyy.aria.core.download; +package com.arialyy.aria.core.download.downloader; /** * Created by lyy on 2016/10/31. * 抽象的下载接口 */ -interface IDownloadUtil { +public interface IDownloadUtil { + + /** + * 获取文件大小 + */ + long getFileSize(); /** * 获取当前下载位置 */ - public long getCurrentLocation(); + long getCurrentLocation(); /** * 是否正在下载 * * @return true, 正在下载 */ - public boolean isDownloading(); + boolean isDownloading(); /** * 取消下载 */ - public void cancelDownload(); + void cancelDownload(); /** * 停止下载 */ - public void stopDownload(); + void stopDownload(); /** * 开始下载 */ - public void startDownload(); + void startDownload(); /** * 从上次断点恢复下载 */ - public void resumeDownload(); - - /** - * 删除下载记录文件 - */ - public void delConfigFile(); - - /** - * 删除temp文件 - */ - public void delTempFile(); - - /** - * 设置最大下载速度 - */ - public void setMaxSpeed(double maxSpeed); + void resumeDownload(); } \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/SimpleDownloadUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/SimpleDownloadUtil.java new file mode 100644 index 00000000..ac15d85d --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/SimpleDownloadUtil.java @@ -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.download.downloader; + +import android.text.TextUtils; +import android.util.Log; +import com.arialyy.aria.core.download.DownloadTaskEntity; + +/** + * Created by lyy on 2015/8/25. + * 简单的下载工具 + */ +public class SimpleDownloadUtil implements IDownloadUtil, Runnable { + private static final String TAG = "SimpleDownloadUtil"; + private IDownloadListener mListener; + private Downloader mDT; + private DownloadTaskEntity mTaskEntity; + + public SimpleDownloadUtil(DownloadTaskEntity entity, IDownloadListener downloadListener) { + mTaskEntity = entity; + mListener = downloadListener; + mDT = new Downloader(downloadListener, entity); + } + + @Override public long getFileSize() { + return mDT.getFileSize(); + } + + /** + * 获取当前下载位置 + */ + @Override public long getCurrentLocation() { + return mDT.getCurrentLocation(); + } + + @Override public boolean isDownloading() { + return mDT.isDownloading(); + } + + /** + * 取消下载 + */ + @Override public void cancelDownload() { + mDT.cancelDownload(); + } + + /** + * 停止下载 + */ + @Override public void stopDownload() { + mDT.stopDownload(); + } + + /** + * 多线程断点续传下载文件,开始下载 + */ + @Override public void startDownload() { + mListener.onPre(); + new Thread(this).start(); + } + + @Override public void resumeDownload() { + startDownload(); + } + + public void setMaxSpeed(double maxSpeed) { + mDT.setMaxSpeed(maxSpeed); + } + + private void failDownload(String msg) { + Log.e(TAG, msg); + mListener.onFail(); + } + + @Override public void run() { + if (TextUtils.isEmpty(mTaskEntity.redirectUrl)) { + new Thread(new FileInfoThread(mTaskEntity, new FileInfoThread.OnFileInfoCallback() { + @Override public void onComplete(String url, int code) { + mDT.startDownload(); + } + + @Override public void onFail(String url, String errorMsg) { + failDownload(errorMsg); + } + })).start(); + } else { + new Downloader(mListener, mTaskEntity).startDownload(); + } + } +} \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/SingleThreadTask.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/SingleThreadTask.java similarity index 89% rename from Aria/src/main/java/com/arialyy/aria/core/download/SingleThreadTask.java rename to Aria/src/main/java/com/arialyy/aria/core/download/downloader/SingleThreadTask.java index b6233744..8efe4eb3 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/SingleThreadTask.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/SingleThreadTask.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download; +package com.arialyy.aria.core.download.downloader; import android.os.Build; import android.text.TextUtils; @@ -28,9 +28,7 @@ import java.math.BigDecimal; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; -import java.util.HashSet; import java.util.Properties; -import java.util.Set; /** * Created by lyy on 2017/1/18. @@ -38,23 +36,23 @@ import java.util.Set; */ final class SingleThreadTask implements Runnable { private static final String TAG = "SingleThreadTask"; - private DownloadUtil.ConfigEntity mConfigEntity; + private ChildThreadConfigEntity mConfigEntity; private String mConfigFPath; private long mChildCurrentLocation = 0; private int mBufSize; private IDownloadListener mListener; - private DownloadStateConstance CONSTANCE; + private StateConstance CONSTANCE; private long mSleepTime = 0; - SingleThreadTask(DownloadStateConstance constance, IDownloadListener listener, - DownloadUtil.ConfigEntity downloadInfo) { + SingleThreadTask(StateConstance constance, IDownloadListener listener, + ChildThreadConfigEntity downloadInfo) { AriaManager manager = AriaManager.getInstance(AriaManager.APP); CONSTANCE = constance; CONSTANCE.CONNECT_TIME_OUT = manager.getDownloadConfig().getConnectTimeOut(); CONSTANCE.READ_TIME_OUT = manager.getDownloadConfig().getIOTimeOut(); mListener = listener; this.mConfigEntity = downloadInfo; - if (mConfigEntity.isSupportBreakpoint) { + if (mConfigEntity.IS_SUPPORT_BREAK_POINT) { mConfigFPath = downloadInfo.CONFIG_FILE_PATH; } mBufSize = manager.getDownloadConfig().getBuffSize(); @@ -76,12 +74,13 @@ final class SingleThreadTask implements Runnable { } @Override public void run() { - HttpURLConnection conn; - InputStream is; + HttpURLConnection conn = null; + InputStream is = null; + BufferedRandomAccessFile file = null; try { URL url = new URL(mConfigEntity.DOWNLOAD_URL); conn = ConnectionHelp.handleConnection(url); - if (mConfigEntity.isSupportBreakpoint) { + if (mConfigEntity.IS_SUPPORT_BREAK_POINT) { Log.d(TAG, "任务【" + mConfigEntity.TEMP_FILE.getName() + "】线程__" @@ -102,8 +101,7 @@ final class SingleThreadTask implements Runnable { conn.setReadTimeout(CONSTANCE.READ_TIME_OUT); //设置读取流的等待时间,必须设置该参数 is = conn.getInputStream(); //创建可设置位置的文件 - BufferedRandomAccessFile file = - new BufferedRandomAccessFile(mConfigEntity.TEMP_FILE, "rwd", mBufSize); + file = new BufferedRandomAccessFile(mConfigEntity.TEMP_FILE, "rwd", mBufSize); //设置每条线程写入文件的位置 file.seek(mConfigEntity.START_LOCATION); byte[] buffer = new byte[mBufSize]; @@ -121,10 +119,6 @@ final class SingleThreadTask implements Runnable { file.write(buffer, 0, len); progress(len); } - file.close(); - //close 为阻塞的,需要使用线程池来处理 - is.close(); - conn.disconnect(); if (CONSTANCE.isCancel) { return; } @@ -133,7 +127,7 @@ final class SingleThreadTask implements Runnable { return; } //支持断点的处理 - if (mConfigEntity.isSupportBreakpoint) { + if (mConfigEntity.IS_SUPPORT_BREAK_POINT) { Log.i(TAG, "任务【" + mConfigEntity.TEMP_FILE.getName() + "】线程__" @@ -161,6 +155,20 @@ final class SingleThreadTask implements Runnable { failDownload(mChildCurrentLocation, "下载失败【" + mConfigEntity.DOWNLOAD_URL + "】", e); } catch (Exception e) { failDownload(mChildCurrentLocation, "获取流失败", e); + } finally { + try { + if (file != null) { + file.close(); + } + if (is != null) { + is.close(); + } + if (conn != null) { + conn.disconnect(); + } + } catch (IOException e) { + e.printStackTrace(); + } } } @@ -170,7 +178,7 @@ final class SingleThreadTask implements Runnable { protected void stop() { synchronized (AriaManager.LOCK) { try { - if (mConfigEntity.isSupportBreakpoint) { + if (mConfigEntity.IS_SUPPORT_BREAK_POINT) { CONSTANCE.STOP_NUM++; Log.d(TAG, "任务【" + mConfigEntity.TEMP_FILE.getName() @@ -202,7 +210,6 @@ final class SingleThreadTask implements Runnable { synchronized (AriaManager.LOCK) { mChildCurrentLocation += len; CONSTANCE.CURRENT_LOCATION += len; - mListener.onProgress(CONSTANCE.CURRENT_LOCATION); } } @@ -211,7 +218,7 @@ final class SingleThreadTask implements Runnable { */ protected void cancel() { synchronized (AriaManager.LOCK) { - if (mConfigEntity.isSupportBreakpoint) { + if (mConfigEntity.IS_SUPPORT_BREAK_POINT) { CONSTANCE.CANCEL_NUM++; Log.d(TAG, "任务【" + mConfigEntity.TEMP_FILE.getName() @@ -250,7 +257,7 @@ final class SingleThreadTask implements Runnable { if (ex != null) { Log.e(TAG, msg + "\n" + CommonUtil.getPrintException(ex)); } - if (mConfigEntity.isSupportBreakpoint) { + if (mConfigEntity.IS_SUPPORT_BREAK_POINT) { writeConfig(false, currentLocation); if (CONSTANCE.isFail()) { Log.e(TAG, "任务【" + mConfigEntity.TEMP_FILE.getName() + "】下载失败"); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadStateConstance.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/StateConstance.java similarity index 90% rename from Aria/src/main/java/com/arialyy/aria/core/download/DownloadStateConstance.java rename to Aria/src/main/java/com/arialyy/aria/core/download/downloader/StateConstance.java index 8c815ee9..323f65ff 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadStateConstance.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/StateConstance.java @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download; +package com.arialyy.aria.core.download.downloader; /** * Created by lyy on 2017/1/18. * 下载状态常量 */ -final class DownloadStateConstance { +final class StateConstance { int CANCEL_NUM = 0; int STOP_NUM = 0; int FAIL_NUM = 0; @@ -32,7 +32,7 @@ final class DownloadStateConstance { boolean isCancel = false; boolean isStop = false; - DownloadStateConstance() { + StateConstance() { } void cleanState() { @@ -45,10 +45,6 @@ final class DownloadStateConstance { FAIL_NUM = 0; } - void setThreadNum(int threadNum) { - THREAD_NUM = threadNum; - } - /** * 所有子线程是否都已经停止下载 */ diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsEntity.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsEntity.java index e6b4d2b9..eb611d5f 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsEntity.java @@ -21,7 +21,7 @@ import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.orm.Ignore; /** - * Created by AriaL on 2017/6/3. + * Created by AriaL on 2017/6/29. */ public abstract class AbsEntity extends DbEntity implements IEntity, Parcelable { /** @@ -31,11 +31,12 @@ public abstract class AbsEntity extends DbEntity implements IEntity, Parcelable /** * 单位转换后的速度 */ - @Ignore private String convertSpeed = "0b/s"; + @Ignore private String convertSpeed = ""; /** * 下载失败计数,每次开始都重置为0 */ @Ignore private int failNum = 0; + /** * 扩展字段 */ @@ -44,6 +45,11 @@ public abstract class AbsEntity extends DbEntity implements IEntity, Parcelable * 文件大小 */ private long fileSize = 1; + /** + * 转换后的文件大小 + */ + private String convertFileSize = ""; + private int state = STATE_WAIT; /** * 当前下载进度 @@ -53,25 +59,23 @@ public abstract class AbsEntity extends DbEntity implements IEntity, Parcelable * 完成时间 */ private long completeTime; - /** - * 文件名 - */ - private String fileName = ""; - public long getSpeed() { - return speed; + private boolean isComplete = false; + + public boolean isComplete() { + return isComplete; } - public void setSpeed(long speed) { - this.speed = speed; + public void setComplete(boolean complete) { + isComplete = complete; } - public String getConvertSpeed() { - return convertSpeed; + public String getConvertFileSize() { + return convertFileSize; } - public void setConvertSpeed(String convertSpeed) { - this.convertSpeed = convertSpeed; + public void setConvertFileSize(String convertFileSize) { + this.convertFileSize = convertFileSize; } public int getFailNum() { @@ -82,6 +86,22 @@ public abstract class AbsEntity extends DbEntity implements IEntity, Parcelable this.failNum = failNum; } + public long getSpeed() { + return speed; + } + + public void setSpeed(long speed) { + this.speed = speed; + } + + public String getConvertSpeed() { + return convertSpeed; + } + + public void setConvertSpeed(String convertSpeed) { + this.convertSpeed = convertSpeed; + } + public String getStr() { return str; } @@ -122,13 +142,10 @@ public abstract class AbsEntity extends DbEntity implements IEntity, Parcelable this.completeTime = completeTime; } - public String getFileName() { - return fileName; - } - - public void setFileName(String fileName) { - this.fileName = fileName; - } + /** + * 实体唯一标识符 + */ + public abstract String getKey(); public AbsEntity() { } @@ -143,10 +160,11 @@ public abstract class AbsEntity extends DbEntity implements IEntity, Parcelable dest.writeInt(this.failNum); dest.writeString(this.str); dest.writeLong(this.fileSize); + dest.writeString(this.convertFileSize); dest.writeInt(this.state); dest.writeLong(this.currentProgress); dest.writeLong(this.completeTime); - dest.writeString(this.fileName); + dest.writeByte(this.isComplete ? (byte) 1 : (byte) 0); } protected AbsEntity(Parcel in) { @@ -155,9 +173,10 @@ public abstract class AbsEntity extends DbEntity implements IEntity, Parcelable this.failNum = in.readInt(); this.str = in.readString(); this.fileSize = in.readLong(); + this.convertFileSize = in.readString(); this.state = in.readInt(); this.currentProgress = in.readLong(); this.completeTime = in.readLong(); - this.fileName = in.readString(); + this.isComplete = in.readByte() != 0; } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupEntity.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupEntity.java new file mode 100644 index 00000000..35ef1d0d --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupEntity.java @@ -0,0 +1,70 @@ +/* + * 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 android.os.Parcel; +import android.os.Parcelable; +import com.arialyy.aria.orm.Primary; + +/** + * Created by AriaL on 2017/6/3. + */ +public abstract class AbsGroupEntity extends AbsEntity implements Parcelable { + /** + * 组名,组名为任务地址相加的urlMd5 + */ + @Primary protected String groupName = ""; + + /** + * 任务组别名 + */ + private String alias = ""; + + public String getGroupName() { + return groupName; + } + + public String getAlias() { + return alias; + } + + @Override public String getKey() { + return groupName; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + public AbsGroupEntity() { + } + + @Override public int describeContents() { + return 0; + } + + @Override public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeString(this.groupName); + dest.writeString(this.alias); + } + + protected AbsGroupEntity(Parcel in) { + super(in); + this.groupName = in.readString(); + this.alias = in.readString(); + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupTarget.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupTarget.java new file mode 100644 index 00000000..c201a010 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupTarget.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.inf; + +/** + * Created by AriaL on 2017/6/29. + * 任务组超类 + */ +public abstract class AbsGroupTarget + extends AbsTarget { + + +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupTask.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupTask.java new file mode 100644 index 00000000..83bc8993 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupTask.java @@ -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; + + +/** + * Created by AriaL on 2017/6/29. + */ +public abstract class AbsGroupTask + extends AbsTask { + + protected TASK_ENTITY mTaskEntity; + + + @Override public String getKey() { + return mEntity.getGroupName(); + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsNormalEntity.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsNormalEntity.java new file mode 100644 index 00000000..d1425f7f --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsNormalEntity.java @@ -0,0 +1,73 @@ +/* + * 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 android.os.Parcel; +import android.os.Parcelable; +import com.arialyy.aria.orm.DbEntity; +import com.arialyy.aria.orm.Ignore; + +/** + * Created by AriaL on 2017/6/3. + */ +public abstract class AbsNormalEntity extends AbsEntity implements Parcelable { + + /** + * 文件名 + */ + private String fileName = ""; + + /** + * 是否是任务组里面的下载实体 + */ + private boolean isGroupChild = false; + + + public boolean isGroupChild() { + return isGroupChild; + } + + public void setGroupChild(boolean groupChild) { + isGroupChild = groupChild; + } + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public AbsNormalEntity() { + } + + @Override public int describeContents() { + return 0; + } + + @Override public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeString(this.fileName); + dest.writeByte(this.isGroupChild ? (byte) 1 : (byte) 0); + } + + protected AbsNormalEntity(Parcel in) { + super(in); + this.fileName = in.readString(); + this.isGroupChild = in.readByte() != 0; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsNormalTarget.java new file mode 100644 index 00000000..31de1c13 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsNormalTarget.java @@ -0,0 +1,106 @@ +/* + * 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 android.text.TextUtils; +import android.util.Log; +import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.command.normal.NormalCmdFactory; +import com.arialyy.aria.util.CommonUtil; + +/** + * Created by lyy on 2017/2/28. + */ +public abstract class AbsNormalTarget + extends AbsTarget { + + /** + * 将任务设置为最高优先级任务,最高优先级任务有以下特点: + * 1、在下载队列中,有且只有一个最高优先级任务 + * 2、最高优先级任务会一直存在,直到用户手动暂停或任务完成 + * 3、任务调度器不会暂停最高优先级任务 + * 4、用户手动暂停或任务完成后,第二次重新执行该任务,该命令将失效 + * 5、如果下载队列中已经满了,则会停止队尾的任务,当高优先级任务完成后,该队尾任务将自动执行 + * 6、把任务设置为最高优先级任务后,将自动执行任务,不需要重新调用start()启动任务 + */ + protected void setHighestPriority() { + AriaManager.getInstance(AriaManager.APP) + .setCmd( + CommonUtil.createCmd(mTargetName, mTaskEntity, NormalCmdFactory.TASK_HIGHEST_PRIORITY)) + .exe(); + } + + /** + * 重定向后,新url的key,默认为location + */ + protected void _setRedirectUrlKey(String redirectUrlKey) { + if (TextUtils.isEmpty(redirectUrlKey)) { + Log.w("AbsNormalTarget", "重定向后,新url的key不能为null"); + return; + } + mTaskEntity.redirectUrlKey = redirectUrlKey; + } + + /** + * 删除记录 + */ + public void removeRecord() { + mEntity.deleteData(); + } + + /** + * 获取任务文件大小 + * + * @return 文件大小 + */ + public long getFileSize() { + return getSize(); + } + + + /** + * 获取单位转换后的文件大小 + * + * @return 文件大小{@code xxx mb} + */ + public String getConvertFileSize() { + return getConvertSize(); + } + + /** + * 下载任务是否存在 + */ + public boolean taskExists() { + return false; + } + + /** + * 添加任务 + */ + public void add() { + AriaManager.getInstance(AriaManager.APP) + .setCmd(CommonUtil.createCmd(mTargetName, mTaskEntity, NormalCmdFactory.TASK_CREATE)) + .exe(); + } + + /** + * 重新下载 + */ + public void reStart() { + cancel(); + start(); + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsNormalTask.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsNormalTask.java new file mode 100644 index 00000000..a89f5e31 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsNormalTask.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.inf; + +/** + * Created by lyy on 2017/6/3. + */ +public abstract class AbsNormalTask extends AbsTask { + + /** + * 暂停任务,并让任务处于等待状态 + */ + public void stopAndWait() { + + } + + /** + * 最高优先级命令,最高优先级命令有以下属性 + * 1、在下载队列中,有且只有一个最高优先级任务 + * 2、最高优先级任务会一直存在,直到用户手动暂停或任务完成 + * 3、任务调度器不会暂停最高优先级任务 + * 4、用户手动暂停或任务完成后,第二次重新执行该任务,该命令将失效 + * 5、如果下载队列中已经满了,则会停止队尾的任务 + * 6、把任务设置为最高优先级任务后,将自动执行任务,不需要重新调用start()启动任务 + */ + public void setHighestPriority(boolean isHighestPriority) { + isHeighestTask = isHighestPriority; + } + + +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsReceiver.java new file mode 100644 index 00000000..4e67339e --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsReceiver.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.arialyy.aria.core.inf; + +/** + * Created by AriaL on 2017/6/27. + */ + +public abstract class AbsReceiver implements IReceiver{ + public String targetName; + public Object obj; + +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTarget.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTarget.java index 4a286ba5..5356dcb7 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTarget.java @@ -20,146 +20,88 @@ import android.text.TextUtils; import android.util.Log; import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.RequestEnum; -import com.arialyy.aria.core.command.AbsCmd; -import com.arialyy.aria.core.command.CmdFactory; -import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.upload.UploadEntity; +import com.arialyy.aria.core.command.normal.NormalCmdFactory; import com.arialyy.aria.util.CommonUtil; -import java.util.ArrayList; -import java.util.List; import java.util.Map; import java.util.Set; /** - * Created by lyy on 2017/2/28. + * Created by AriaL on 2017/7/3. */ -public abstract class AbsTarget { - protected ENTITY entity; - protected TASK_ENTITY taskEntity; - protected String targetName; +public abstract class AbsTarget + implements ITarget { + protected ENTITY mEntity; + protected TASK_ENTITY mTaskEntity; + protected String mTargetName; /** - * 设置扩展字段,用来保存你的其它数据,如果你的数据比较多,你可以把你的数据转换为JSON字符串,然后再存到Aria中 + * 获取任务进度,如果任务存在,则返回当前进度 * - * @param str 扩展数据 - */ - public AbsTarget setExtendField(String str) { - entity.setStr(str); - return this; - } - - /** - * 获取存放的扩展字段 - * 设置扩展字段{@link #setExtendField(String)} + * @return 该任务进度 */ - public String getExtendField() { - return entity.getStr(); + public long getCurrentProgress() { + return mEntity == null ? -1 : mEntity.getCurrentProgress(); } /** - * 获取任务状态 + * 获取任务文件大小 * - * @return {@link IEntity} - */ - public int getTaskState() { - return entity.getState(); - } - - /** - * 将任务设置为最高优先级任务,最高优先级任务有以下特点: - * 1、在下载队列中,有且只有一个最高优先级任务 - * 2、最高优先级任务会一直存在,直到用户手动暂停或任务完成 - * 3、任务调度器不会暂停最高优先级任务 - * 4、用户手动暂停或任务完成后,第二次重新执行该任务,该命令将失效 - * 5、如果下载队列中已经满了,则会停止队尾的任务,当高优先级任务完成后,该队尾任务将自动执行 - * 6、把任务设置为最高优先级任务后,将自动执行任务,不需要重新调用start()启动任务 - */ - protected void setHighestPriority() { - AriaManager.getInstance(AriaManager.APP) - .setCmd(CommonUtil.createCmd(targetName, taskEntity, CmdFactory.TASK_HIGHEST_PRIORITY)) - .exe(); - } - - /** - * 重定向后,新url的key,默认为location - */ - protected void _setRedirectUrlKey(String redirectUrlKey) { - if (TextUtils.isEmpty(redirectUrlKey)) { - Log.w("AbsTarget", "重定向后,新url的key不能为null"); - return; - } - taskEntity.redirectUrlKey = redirectUrlKey; - } - - /** - * 删除记录 + * @return 文件大小 */ - public void removeRecord() { - entity.deleteData(); + @Override public long getSize() { + return mEntity == null ? 0 : mEntity.getFileSize(); } /** - * 获取任务文件大小 + * 获取单位转换后的文件大小 * - * @return 文件大小 + * @return 文件大小{@code xxx mb} */ - public long getFileSize() { - if (entity instanceof DownloadEntity) { - DownloadEntity entity = (DownloadEntity) this.entity; - return entity.getFileSize(); - } else if (entity instanceof UploadEntity) { - UploadEntity entity = (UploadEntity) this.entity; - return entity.getFileSize(); - } - return 0; + @Override public String getConvertSize() { + return mEntity == null ? "0b" : CommonUtil.formatFileSize(mEntity.getFileSize()); } /** - * 获取单位转换后的文件大小 + * 设置扩展字段,用来保存你的其它数据,如果你的数据比较多,你可以把你的数据转换为JSON字符串,然后再存到Aria中 * - * @return 文件大小{@code xxx mb} + * @param str 扩展数据 */ - public String getConvertFileSize() { - if (entity instanceof DownloadEntity) { - DownloadEntity entity = (DownloadEntity) this.entity; - return CommonUtil.formatFileSize(entity.getFileSize()); - } else if (entity instanceof UploadEntity) { - UploadEntity entity = (UploadEntity) this.entity; - return CommonUtil.formatFileSize(entity.getFileSize()); - } - return "0b"; + public TARGET setExtendField(String str) { + mEntity.setStr(str); + return (TARGET) this; } /** - * 下载任务是否存在 + * 获取存放的扩展字段 + * 设置扩展字段{@link #setExtendField(String)} */ - public boolean taskExists() { - return false; + public String getExtendField() { + return mEntity.getStr(); } /** - * 获取任务进度百分比 + * 获取任务状态 * - * @return 返回任务进度 + * @return {@link IEntity} */ - protected int getPercent() { - return 0; + public int getTaskState() { + return mEntity.getState(); } /** - * 获取任务进度,如果任务存在,则返回当前进度 + * 获取任务进度百分比 * - * @return 该任务进度 + * @return 返回任务进度 */ - public long getCurrentProgress() { - if (entity instanceof DownloadEntity) { - DownloadEntity entity = (DownloadEntity) this.entity; - return entity.getCurrentProgress(); - } else if (entity instanceof UploadEntity) { - UploadEntity entity = (UploadEntity) this.entity; - return entity.getCurrentProgress(); + @Override public int getPercent() { + if (mEntity == null) { + Log.e("AbsTarget", "下载管理器中没有该任务"); + return 0; } - return -1; + if (mEntity.getFileSize() != 0) { + return (int) (mEntity.getCurrentProgress() * 100 / mEntity.getFileSize()); + } + return 0; } /** @@ -168,20 +110,22 @@ public abstract class AbsTarget headers) { + public TARGET addHeaders(Map headers) { if (headers != null && headers.size() > 0) { Set keys = headers.keySet(); for (String key : keys) { - taskEntity.headers.put(key, headers.get(key)); + mTaskEntity.headers.put(key, headers.get(key)); } } + return (TARGET) this; } /** @@ -189,62 +133,76 @@ public abstract class AbsTarget cmds = new ArrayList<>(); - cmds.add(CommonUtil.createCmd(targetName, taskEntity, CmdFactory.TASK_CREATE)); - cmds.add(CommonUtil.createCmd(targetName, taskEntity, CmdFactory.TASK_START)); - AriaManager.getInstance(AriaManager.APP).setCmds(cmds).exe(); - cmds.clear(); + @Deprecated public void pause() { + stop(); } - /** - * 停止下载 - */ - protected void pause() { + @Override public void stop() { AriaManager.getInstance(AriaManager.APP) - .setCmd(CommonUtil.createCmd(targetName, taskEntity, CmdFactory.TASK_STOP)) + .setCmd(CommonUtil.createCmd(mTargetName, mTaskEntity, NormalCmdFactory.TASK_STOP)) .exe(); } /** * 恢复下载 */ - protected void resume() { + @Override public void resume() { AriaManager.getInstance(AriaManager.APP) - .setCmd(CommonUtil.createCmd(targetName, taskEntity, CmdFactory.TASK_START)) + .setCmd(CommonUtil.createCmd(mTargetName, mTaskEntity, NormalCmdFactory.TASK_START)) .exe(); } /** * 取消下载 */ - public void cancel() { + @Override public void cancel() { AriaManager.getInstance(AriaManager.APP) - .setCmd(CommonUtil.createCmd(targetName, taskEntity, CmdFactory.TASK_CANCEL)) + .setCmd(CommonUtil.createCmd(mTargetName, mTaskEntity, NormalCmdFactory.TASK_CANCEL)) .exe(); } /** - * 重新下载 - */ - void reStart() { - cancel(); - start(); + * 创建文件名,如果url链接有后缀名,则使用url中的后缀名 + * + * @return url 的 hashKey + */ + protected String createFileName(String url) { + int end = url.indexOf("?"); + String tempUrl, fileName = ""; + if (end > 0) { + tempUrl = url.substring(0, end); + int tempEnd = tempUrl.lastIndexOf("/"); + if (tempEnd > 0) { + fileName = tempUrl.substring(tempEnd + 1, tempUrl.length()); + } + } else { + int tempEnd = url.lastIndexOf("/"); + if (tempEnd > 0) { + fileName = url.substring(tempEnd + 1, url.length()); + } + } + if (TextUtils.isEmpty(fileName)) { + fileName = CommonUtil.keyToHashKey(url); + } + return fileName; } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTask.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTask.java index 64e6f543..1bbaecc8 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTask.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTask.java @@ -16,84 +16,70 @@ package com.arialyy.aria.core.inf; import android.content.Context; +import android.os.Handler; import com.arialyy.aria.util.CommonUtil; /** - * Created by lyy on 2017/6/3. + * Created by AriaL on 2017/6/29. */ -public abstract class AbsTask - implements ITask { +public abstract class AbsTask implements ITask { protected ENTITY mEntity; + protected Handler mOutHandler; /** * 用于生成该任务对象的hash码 */ private String mTargetName; protected Context mContext; - private boolean isHeighestTask = false; - @Override public void stopAndWait() { + protected boolean isHeighestTask = false; + /** + * 任务是否完成 + * + * @return {@code true} 已经完成,{@code false} 未完成 + */ + public boolean isComplete() { + return mEntity.isComplete(); } /** - * @return 返回原始byte速度,需要你在配置文件中配置 - *
-   *   {@code
-   *    
-   *      
-   *        ...
-   *        
-   *      
-   *
-   *      或在代码中设置
-   *      Aria.get(this).getDownloadConfig().setConvertSpeed(false);
-   *    
-   *   }
-   * 
- * 才能生效 + * 获取当前下载进度 */ - @Override public long getSpeed() { - return mEntity.getSpeed(); + @Override public long getCurrentProgress() { + return mEntity.getCurrentProgress(); } /** - * @return 返回转换单位后的速度,需要你在配置文件中配置,转换完成后为:1b/s、1kb/s、1mb/s、1gb/s、1tb/s - *
-   *   {@code
-   *    
-   *      
-   *        ...
-   *        
-   *      
+   * 获取单位转换后的进度
    *
-   *      或在代码中设置
-   *      Aria.get(this).getDownloadConfig().setConvertSpeed(true);
-   *    
-   *   }
-   * 
- * 才能生效 + * @return 如:已经下载3mb的大小,则返回{@code 3mb} */ - @Override public String getConvertSpeed() { - return mEntity.getConvertSpeed(); + @Override public String getConvertCurrentProgress() { + if (mEntity.getCurrentProgress() == 0) { + return "0b"; + } + return CommonUtil.formatFileSize(mEntity.getCurrentProgress()); } /** - * 最高优先级命令,最高优先级命令有以下属性 - * 1、在下载队列中,有且只有一个最高优先级任务 - * 2、最高优先级任务会一直存在,直到用户手动暂停或任务完成 - * 3、任务调度器不会暂停最高优先级任务 - * 4、用户手动暂停或任务完成后,第二次重新执行该任务,该命令将失效 - * 5、如果下载队列中已经满了,则会停止队尾的任务 - * 6、把任务设置为最高优先级任务后,将自动执行任务,不需要重新调用start()启动任务 + * 转换单位后的文件长度 + * + * @return 如果文件长度为0,则返回0m,否则返回转换后的长度1b、1kb、1mb、1gb、1tb */ - @Override public void setHighestPriority(boolean isHighestPriority) { - isHeighestTask = isHighestPriority; + @Override public String getConvertFileSize() { + if (mEntity.getFileSize() == 0) { + return "0mb"; + } + return CommonUtil.formatFileSize(mEntity.getFileSize()); } - @Override public boolean isHighestPriorityTask() { - return isHeighestTask; + /** + * 获取文件大小 + */ + @Override public long getFileSize() { + return mEntity.getFileSize(); } /** @@ -109,54 +95,69 @@ public abstract class AbsTask + * {@code + * + * + * ... + * + * + * + * 或在代码中设置 + * Aria.get(this).getDownloadConfig().setConvertSpeed(false); + * + * } + * + * 才能生效 */ - @Override public long getCurrentProgress() { - return mEntity.getCurrentProgress(); + @Override public long getSpeed() { + return mEntity.getSpeed(); } /** - * 获取单位转换后的进度 + * @return 返回转换单位后的速度,需要你在配置文件中配置,转换完成后为:1b/s、1kb/s、1mb/s、1gb/s、1tb/s + *
+   *   {@code
+   *    
+   *      
+   *        ...
+   *        
+   *      
    *
-   * @return 如:已经下载3mb的大小,则返回{@code 3mb}
+   *      或在代码中设置
+   *      Aria.get(this).getDownloadConfig().setConvertSpeed(true);
+   *    
+   *   }
+   * 
+ * 才能生效 */ - @Override public String getConvertCurrentProgress() { - if (mEntity.getCurrentProgress() == 0) { - return "0b"; - } - return CommonUtil.formatFileSize(mEntity.getCurrentProgress()); + @Override public String getConvertSpeed() { + return mEntity.getConvertSpeed(); } @Override public ENTITY getEntity() { return mEntity; } - /** - * 删除任务记录,删除后,再次启动该任务的下载时,将重新下载 - */ - @Override public void removeRecord() { - mEntity.deleteData(); - } - public String getTargetName() { return mTargetName; } @@ -164,4 +165,8 @@ public abstract class AbsTask extends DbEntity { + + /** + * Task实体对应的key + */ + @Primary public String key = ""; -public abstract class AbsTaskEntity { /** * http 请求头 */ @@ -35,9 +43,45 @@ public abstract class AbsTaskEntity { public RequestEnum requestEnum = RequestEnum.GET; /** - * 重定向后,新url的key + * 从链接中含有的文件md5码信息所需要的key + */ + public String md5Key = "Content-MD5"; + + /** + * 从链接中获取文件描述信息所需要的key + */ + public String dispositionKey = "Content-Disposition"; + + /** + * 重定向后,从链接中获取新url所需要的key */ public String redirectUrlKey = "location"; - public abstract AbsEntity getEntity(); + /** + * 从Disposition获取的文件名说需要的key + */ + public String dispositionFileKey = "attachment;filename"; + + /** + * 重定向链接 + */ + public String redirectUrl = ""; + + /** + * {@code true} 删除任务数据库记录,并且删除已经下载完成的文件 + * {@code false} 如果任务已经完成,只删除任务数据库记录 + */ + @Ignore public boolean removeFile = false; + + /** + * 是否支持断点, {@code true} 为支持断点 + */ + public boolean isSupportBP = true; + + /** + * 状态码 + */ + public int code; + + public abstract ENTITY getEntity(); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/IEntity.java b/Aria/src/main/java/com/arialyy/aria/core/inf/IEntity.java index 6b60f1bb..156a01ce 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/IEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/IEntity.java @@ -20,42 +20,41 @@ import com.arialyy.aria.orm.Ignore; /** * Created by lyy on 2017/2/23. */ - public interface IEntity { /** * 其它状态 */ - @Ignore public static final int STATE_OTHER = -1; + @Ignore int STATE_OTHER = -1; /** * 失败状态 */ - @Ignore public static final int STATE_FAIL = 0; + @Ignore int STATE_FAIL = 0; /** * 完成状态 */ - @Ignore public static final int STATE_COMPLETE = 1; + @Ignore int STATE_COMPLETE = 1; /** * 停止状态 */ - @Ignore public static final int STATE_STOP = 2; + @Ignore int STATE_STOP = 2; /** - * 未开始状态 + * 等待状态 */ - @Ignore public static final int STATE_WAIT = 3; + @Ignore int STATE_WAIT = 3; /** * 下载中 */ - @Ignore public static final int STATE_RUNNING = 4; + @Ignore int STATE_RUNNING = 4; /** * 预处理 */ - @Ignore public static final int STATE_PRE = 5; + @Ignore int STATE_PRE = 5; /** * 预处理完成 */ - @Ignore public static final int STATE_POST_PRE = 6; + @Ignore int STATE_POST_PRE = 6; /** * 取消下载 */ - @Ignore public static final int STATE_CANCEL = 7; + @Ignore int STATE_CANCEL = 7; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/IDownloadListener.java b/Aria/src/main/java/com/arialyy/aria/core/inf/IEventListener.java similarity index 58% rename from Aria/src/main/java/com/arialyy/aria/core/download/IDownloadListener.java rename to Aria/src/main/java/com/arialyy/aria/core/inf/IEventListener.java index 0e805e35..c9842fac 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/IDownloadListener.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/IEventListener.java @@ -13,73 +13,56 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.arialyy.aria.core.download; +package com.arialyy.aria.core.inf; /** - * 下载监听 + * Created by Aria.Lao on 2017/7/18. + * 基础事件 */ -interface IDownloadListener { - - /** - * 支持断点回调 - * - * @param support true,支持;false 不支持 - */ - public void supportBreakpoint(boolean support); - - /** - * 取消下载 - */ - public void onCancel(); - - /** - * 下载失败 - */ - public void onFail(); +public interface IEventListener { /** * 预处理,有时有些地址链接比较慢,这时可以先在这个地方出来一些界面上的UI,如按钮的状态 */ - public void onPre(); + void onPre(); /** * 预处理完成,准备下载---开始下载之间 */ - public void onPostPre(long fileSize); + void onPostPre(long fileSize); /** - * 下载监听 + * 开始 */ - public void onProgress(long currentLocation); + void onStart(long startLocation); /** - * 单一线程的结束位置 + * 恢复位置 */ - public void onChildComplete(long finishLocation); + void onResume(long resumeLocation); /** - * 开始 + * 下载监听 */ - public void onStart(long startLocation); + void onProgress(long currentLocation); /** - * 子程恢复下载的位置 + * 停止 */ - public void onChildResume(long resumeLocation); + void onStop(long stopLocation); /** - * 恢复位置 + * 下载完成 */ - public void onResume(long resumeLocation); + void onComplete(); /** - * 停止 + * 取消下载 */ - public void onStop(long stopLocation); + void onCancel(); /** - * 下载完成 + * 下载失败 */ - public void onComplete(); -} \ No newline at end of file + void onFail(); +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/IReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/inf/IReceiver.java index 0c8d701c..dc347eee 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/IReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/IReceiver.java @@ -44,7 +44,7 @@ public interface IReceiver { /** * 删除所有任务 */ - public void removeAllTask(); + public void removeAllTask(boolean removeFile); /** * 任务是否存在 @@ -56,5 +56,5 @@ public interface IReceiver { /** * 获取任务列表 */ - public List getTaskList(); + public List getSimpleTaskList(); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/ITarget.java b/Aria/src/main/java/com/arialyy/aria/core/inf/ITarget.java new file mode 100644 index 00000000..6a12071b --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/ITarget.java @@ -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.inf; + +import android.support.annotation.NonNull; +import com.arialyy.aria.core.RequestEnum; +import java.util.Map; + +/** + * Created by AriaL on 2017/6/29. + */ +public interface ITarget { + /** + * 任务文件大小 + */ + long getSize(); + + /** + * 转换后的大小 + */ + String getConvertSize(); + + /** + * 获取任务进度百分比 + */ + int getPercent(); + + /** + * 获取任务进度,如果任务存在,则返回当前进度 + */ + long getCurrentProgress(); + + /** + * 给url请求添加头部 + * + * @param key 头部key + * @param header 头部value + */ + TARGET addHeader(@NonNull String key, @NonNull String header) ; + + /** + * 给url请求添加头部 + */ + TARGET addHeaders(Map headers); + + /** + * 设置请求类型 + * + * @param requestEnum {@link RequestEnum} + */ + TARGET setRequestMode(RequestEnum requestEnum); + + /** + * 开始下载 + */ + void start(); + + /** + * 停止下载 + */ + void stop(); + + /** + * 恢复下载 + */ + void resume(); + + /** + * 取消下载 + */ + void cancel(); + +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/ITask.java b/Aria/src/main/java/com/arialyy/aria/core/inf/ITask.java index c8b4efa2..70bee951 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/ITask.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/ITask.java @@ -18,25 +18,17 @@ package com.arialyy.aria.core.inf; /** * Created by lyy on 2017/2/13. */ - public interface ITask { /** - * 暂停任务,并让任务处于等待状态 + * 获取下载状态 */ - public void stopAndWait(); + int getState(); /** - * 设置任务为最高优先级任务,在下载队列中,有且只有一个最高优先级任务 - */ - public void setHighestPriority(boolean isHighestPriority); - - /** - * 该任务是否是最高优先级任务 - * - * @return {@code true} 任务为最高优先级任务 + * 获取扩展字段 */ - public boolean isHighestPriorityTask(); + String getExtendField(); /** * 唯一标识符,DownloadTask 为下载地址,UploadTask 为文件路径 @@ -44,14 +36,14 @@ public interface ITask { public String getKey(); /** - * 是否真正执行 + * 任务是否正在执行 * * @return true,正在执行; */ public boolean isRunning(); /** - * 获取工具实体 + * 获取信息实体 */ public ENTITY getEntity(); @@ -100,5 +92,4 @@ public interface ITask { public void setTargetName(String targetName); - public void removeRecord(); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java b/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java index 625d28dd..8dab1afb 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java @@ -17,24 +17,51 @@ package com.arialyy.aria.core.queue; import android.util.Log; -import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.download.DownloadTask; import com.arialyy.aria.core.inf.AbsEntity; -import com.arialyy.aria.core.inf.ITask; +import com.arialyy.aria.core.inf.AbsTask; import com.arialyy.aria.core.inf.AbsTaskEntity; -import com.arialyy.aria.core.queue.pool.CachePool; -import com.arialyy.aria.core.queue.pool.ExecutePool; +import com.arialyy.aria.core.inf.IEntity; +import com.arialyy.aria.core.queue.pool.BaseCachePool; +import com.arialyy.aria.core.queue.pool.BaseExecutePool; import java.util.Set; /** * Created by lyy on 2017/2/23. * 任务队列 */ -abstract class AbsTaskQueue +abstract class AbsTaskQueue implements ITaskQueue { private final String TAG = "AbsTaskQueue"; - CachePool mCachePool = new CachePool<>(); - ExecutePool mExecutePool; + BaseCachePool mCachePool; + BaseExecutePool mExecutePool; + + AbsTaskQueue() { + mCachePool = setCachePool(); + mExecutePool = setExecutePool(); + } + + abstract BaseCachePool setCachePool(); + + abstract BaseExecutePool setExecutePool(); + + @Override public boolean taskIsRunning(String key) { + return mExecutePool.getTask(key) != null; + } + + @Override public void removeAllTask() { + Set exeKeys = mExecutePool.getAllTask().keySet(); + for (String key : exeKeys) { + TASK task = mExecutePool.getAllTask().get(key); + if (task != null) task.cancel(); + } + Set cacheKeys = mCachePool.getAllTask().keySet(); + for (String key : cacheKeys) { + mExecutePool.removeTask(key); + } + } /** * 停止所有任务 @@ -55,17 +82,27 @@ abstract class AbsTaskQueue= oldMaxSize) { + for (int i = 0, len = Math.abs(diff); i < len; i++) { + TASK eTask = mExecutePool.pollTask(); + if (eTask != null) { + stopTask(eTask); + } + } + } + mExecutePool.setMaxNum(downloadNum); + if (diff >= 1) { + for (int i = 0; i < diff; i++) { + TASK nextTask = getNextTask(); + if (nextTask != null && nextTask.getState() == IEntity.STATE_WAIT) { + startTask(nextTask); + } + } + } } @Override public TASK getTask(String url) { @@ -109,12 +169,22 @@ abstract class AbsTaskQueue { + private static volatile DownloadGroupTaskQueue INSTANCE = null; + + private final String TAG = "DownloadGroupTaskQueue"; + + public static DownloadGroupTaskQueue getInstance() { + if (INSTANCE == null) { + synchronized (AriaManager.LOCK) { + INSTANCE = new DownloadGroupTaskQueue(); + } + } + return INSTANCE; + } + + private DownloadGroupTaskQueue() { + } + + @Override BaseCachePool setCachePool() { + return DownloadSharePool.getInstance().cachePool; + } + + @Override BaseExecutePool setExecutePool() { + return DownloadSharePool.getInstance().executePool; + } + + @Override public DownloadGroupTask createTask(String targetName, DownloadGroupTaskEntity entity) { + DownloadGroupTask task = null; + if (!TextUtils.isEmpty(targetName)) { + task = (DownloadGroupTask) TaskFactory.getInstance() + .createTask(targetName, entity, DownloadGroupSchedulers.getInstance()); + entity.key = entity.getEntity().getGroupName(); + mCachePool.putTask(task); + + DQueueMapping.getInstance().addType(task.getKey(), DQueueMapping.QUEUE_TYPE_DOWNLOAD_GROUP); + } else { + Log.e(TAG, "target name 为 null!!"); + } + return task; + } + + @Override public String getKey(DownloadGroupEntity entity) { + return entity.getGroupName(); + } + + @Override public int getConfigMaxNum() { + return AriaManager.getInstance(AriaManager.APP).getDownloadConfig().oldMaxTaskNum; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/DownloadTaskQueue.java b/Aria/src/main/java/com/arialyy/aria/core/queue/DownloadTaskQueue.java index 4140433a..24d4344c 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/DownloadTaskQueue.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/DownloadTaskQueue.java @@ -22,8 +22,10 @@ import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadTask; import com.arialyy.aria.core.download.DownloadTaskEntity; -import com.arialyy.aria.core.inf.IEntity; -import com.arialyy.aria.core.queue.pool.ExecutePool; +import com.arialyy.aria.core.queue.pool.BaseCachePool; +import com.arialyy.aria.core.queue.pool.BaseExecutePool; +import com.arialyy.aria.core.queue.pool.DownloadSharePool; +import com.arialyy.aria.core.scheduler.DQueueMapping; import com.arialyy.aria.core.scheduler.DownloadSchedulers; import java.util.LinkedHashSet; import java.util.Map; @@ -48,10 +50,28 @@ public class DownloadTaskQueue } private DownloadTaskQueue() { - mExecutePool = new ExecutePool<>(true); } - @Override public void setTaskHighestPriority(DownloadTask task) { + @Override BaseCachePool setCachePool() { + return DownloadSharePool.getInstance().cachePool; + } + + @Override BaseExecutePool setExecutePool() { + return DownloadSharePool.getInstance().executePool; + } + + @Override public String getKey(DownloadEntity entity) { + return entity.getDownloadUrl(); + } + + @Override public int getConfigMaxNum() { + return AriaManager.getInstance(AriaManager.APP).getDownloadConfig().oldMaxTaskNum; + } + + /** + * 设置任务为最高优先级任务 + */ + public void setTaskHighestPriority(DownloadTask task) { task.setHighestPriority(true); Map exeTasks = mExecutePool.getAllTask(); if (exeTasks != null && !exeTasks.isEmpty()) { @@ -91,66 +111,36 @@ public class DownloadTaskQueue } } - public void setMaxSpeed(double maxSpeed){ + /** + * 最大下载速度 + */ + public void setMaxSpeed(double maxSpeed) { Map tasks = mExecutePool.getAllTask(); Set keys = tasks.keySet(); - for (String key : keys){ + for (String key : keys) { DownloadTask task = tasks.get(key); task.setMaxSpeed(maxSpeed); } } - @Override public void setMaxTaskNum(int downloadNum) { - int oldMaxSize = AriaManager.getInstance(AriaManager.APP).getDownloadConfig().oldMaxTaskNum; - int diff = downloadNum - oldMaxSize; - if (oldMaxSize == downloadNum) { - Log.d(TAG, "设置的下载任务数和配置文件的下载任务数一直,跳过"); - return; - } - //设置的任务数小于配置任务数 - if (diff <= -1 && mExecutePool.size() >= oldMaxSize) { - for (int i = 0, len = Math.abs(diff); i < len; i++) { - DownloadTask eTask = mExecutePool.pollTask(); - if (eTask != null) { - stopTask(eTask); - } - } - } - mExecutePool.setDownloadNum(downloadNum); - if (diff >= 1) { - for (int i = 0; i < diff; i++) { - DownloadTask nextTask = getNextTask(); - if (nextTask != null && nextTask.getDownloadEntity().getState() == IEntity.STATE_WAIT) { - startTask(nextTask); - } - } - } - } - @Override public DownloadTask createTask(String target, DownloadTaskEntity entity) { DownloadTask task = null; if (!TextUtils.isEmpty(target)) { task = (DownloadTask) TaskFactory.getInstance() .createTask(target, entity, DownloadSchedulers.getInstance()); + entity.key = entity.getEntity().getDownloadPath(); mCachePool.putTask(task); + + DQueueMapping.getInstance().addType(task.getKey(), DQueueMapping.QUEUE_TYPE_DOWNLOAD); } else { - Log.e(TAG, "target name 为 null是!!"); + Log.e(TAG, "target name 为 null!!"); } - return task; - } - @Override public DownloadTask getTask(DownloadEntity entity) { - return getTask(entity.getDownloadUrl()); + return task; } - @Override public void removeTask(DownloadEntity entity) { - DownloadTask task = mExecutePool.getTask(entity.getDownloadUrl()); - if (task != null) { - Log.d(TAG, "从执行池删除任务,删除" + (mExecutePool.removeTask(task) ? "成功" : "失败")); - } - task = mCachePool.getTask(entity.getDownloadUrl()); - if (task != null) { - Log.d(TAG, "从缓存池删除任务,删除" + (mCachePool.removeTask(task) ? "成功" : "失败")); - } + @Override public void stopTask(DownloadTask task) { + task.setHighestPriority(false); + super.stopTask(task); } } \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/ITaskQueue.java b/Aria/src/main/java/com/arialyy/aria/core/queue/ITaskQueue.java index 9c9bd7c0..76594a81 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/ITaskQueue.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/ITaskQueue.java @@ -19,8 +19,10 @@ package com.arialyy.aria.core.queue; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.download.DownloadTask; +import com.arialyy.aria.core.inf.AbsEntity; +import com.arialyy.aria.core.inf.AbsNormalTask; +import com.arialyy.aria.core.inf.AbsTask; import com.arialyy.aria.core.inf.IEntity; -import com.arialyy.aria.core.inf.ITask; import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.upload.UploadTask; @@ -30,7 +32,15 @@ import com.arialyy.aria.core.upload.UploadTaskEntity; * Created by lyy on 2016/8/16. * 任务功能接口 */ -public interface ITaskQueue { +public interface ITaskQueue { + + /** + * 通过key判断任务是否正在执行 + * + * @param key 下载链接,或上传文件的路径 + * @return {@code true} 任务正在运行 + */ + boolean taskIsRunning(String key); /** * 停止所有任务 @@ -38,11 +48,9 @@ public interface ITaskQueue {@link DownloadTaskEntity}、{@link UploadTaskEntity} - * @param {@link DownloadSchedulers} - * @return {@link DownloadTask}、{@link UploadTask} + * @param {@link DownloadTaskEntity}、{@link UploadTaskEntity}、{@link + * DownloadGroupTaskEntity} + * @param {@link DownloadSchedulers}、{@link UploadSchedulers} + * @return {@link DownloadTask}、{@link UploadTask}、{@link DownloadGroupTask} */ - ITask createTask(String targetName, - ENTITY entity, SCHEDULER schedulers) { + ITask createTask( + String targetName, TASK_ENTITY entity, SCHEDULER schedulers) { if (entity instanceof DownloadTaskEntity) { return createDownloadTask(targetName, (DownloadTaskEntity) entity, schedulers); } else if (entity instanceof UploadTaskEntity) { return createUploadTask(targetName, (UploadTaskEntity) entity, schedulers); + } else if (entity instanceof DownloadGroupTaskEntity) { + return createDownloadGroupTask(targetName, (DownloadGroupTaskEntity) entity, schedulers); } return null; } + /** + * 创建下载任务主任务 + * + * @param entity 下载任务实体{@link DownloadGroupTask} + * @param schedulers {@link ISchedulers} + */ + private DownloadGroupTask createDownloadGroupTask(String targetName, + DownloadGroupTaskEntity entity, ISchedulers schedulers) { + DownloadGroupTask.Builder builder = new DownloadGroupTask.Builder(targetName, entity); + builder.setOutHandler(schedulers); + return builder.build(); + } + /** * @param entity 上传任务实体{@link UploadTaskEntity} * @param schedulers {@link ISchedulers} diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/UploadTaskQueue.java b/Aria/src/main/java/com/arialyy/aria/core/queue/UploadTaskQueue.java index 2b56ac84..46e3f9b4 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/UploadTaskQueue.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/UploadTaskQueue.java @@ -19,7 +19,9 @@ package com.arialyy.aria.core.queue; import android.text.TextUtils; import android.util.Log; import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.core.queue.pool.ExecutePool; +import com.arialyy.aria.core.queue.pool.BaseCachePool; +import com.arialyy.aria.core.queue.pool.BaseExecutePool; +import com.arialyy.aria.core.queue.pool.UploadSharePool; import com.arialyy.aria.core.scheduler.UploadSchedulers; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.upload.UploadTask; @@ -43,11 +45,22 @@ public class UploadTaskQueue extends AbsTaskQueue(false); } - @Override public void setMaxTaskNum(int newMaxNum) { + @Override BaseCachePool setCachePool() { + return UploadSharePool.getInstance().cachePool; + } + + @Override BaseExecutePool setExecutePool() { + return UploadSharePool.getInstance().executePool; + } + @Override public String getKey(UploadEntity entity) { + return entity.getFilePath(); + } + + @Override public int getConfigMaxNum() { + return AriaManager.getInstance(AriaManager.APP).getUploadConfig().oldMaxTaskNum; } @Override public UploadTask createTask(String targetName, UploadTaskEntity entity) { @@ -55,25 +68,11 @@ public class UploadTaskQueue extends AbsTaskQueue implements IPool { - private static final String TAG = "CachePool"; +public class BaseCachePool implements IPool { + private static final String TAG = "BaseCachePool"; private static final int MAX_NUM = Integer.MAX_VALUE; //最大下载任务数 private static final long TIME_OUT = 1000; private Map mCacheMap; private LinkedBlockingQueue mCacheQueue; - public CachePool() { + public BaseCachePool() { mCacheQueue = new LinkedBlockingQueue<>(MAX_NUM); mCacheMap = new HashMap<>(); } /** - * 获取所有正在执行的任务 + * 获取被缓存的任务 */ public Map getAllTask() { return mCacheMap; @@ -95,7 +96,7 @@ public class CachePool implements IPool { @Override public TASK pollTask() { synchronized (AriaManager.LOCK) { try { - TASK task = null; + TASK task; task = mCacheQueue.poll(TIME_OUT, TimeUnit.MICROSECONDS); if (task != null) { String url = task.getKey(); diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/ExecutePool.java b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/BaseExecutePool.java similarity index 73% rename from Aria/src/main/java/com/arialyy/aria/core/queue/pool/ExecutePool.java rename to Aria/src/main/java/com/arialyy/aria/core/queue/pool/BaseExecutePool.java index 4f9691af..fda42905 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/ExecutePool.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/BaseExecutePool.java @@ -19,11 +19,12 @@ package com.arialyy.aria.core.queue.pool; import android.text.TextUtils; import android.util.Log; import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.inf.AbsTask; import com.arialyy.aria.core.inf.ITask; +import com.arialyy.aria.core.scheduler.DQueueMapping; import com.arialyy.aria.util.CommonUtil; import java.util.HashMap; import java.util.Map; -import java.util.Set; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.TimeUnit; @@ -31,23 +32,28 @@ import java.util.concurrent.TimeUnit; * Created by lyy on 2016/8/15. * 任务执行池,所有当前下载任务都该任务池中,默认下载大小为2 */ -public class ExecutePool implements IPool { - private static final String TAG = "ExecutePool"; - private static final long TIME_OUT = 1000; - private ArrayBlockingQueue mExecuteQueue; - private Map mExecuteMap; - private int mSize; - - public ExecutePool(boolean isDownload) { - if (isDownload) { - mSize = AriaManager.getInstance(AriaManager.APP).getDownloadConfig().getMaxTaskNum(); - } else { - mSize = AriaManager.getInstance(AriaManager.APP).getUploadConfig().getMaxTaskNum(); - } +public class BaseExecutePool implements IPool { + private final String TAG = "BaseExecutePool"; + final long TIME_OUT = 1000; + ArrayBlockingQueue mExecuteQueue; + Map mExecuteMap; + int mSize; + + BaseExecutePool() { + mSize = getMaxSize(); mExecuteQueue = new ArrayBlockingQueue<>(mSize); mExecuteMap = new HashMap<>(); } + /** + * 获取最大任务数配置 + * + * @return {@link AriaManager#getDownloadConfig()} {@link AriaManager#getUploadConfig()},如果不设置,默认返回2 + */ + protected int getMaxSize() { + return 2; + } + /** * 获取所有正在执行的任务 */ @@ -67,10 +73,6 @@ public class ExecutePool implements IPool { return false; } else { if (mExecuteQueue.size() >= mSize) { - Set keys = mExecuteMap.keySet(); - for (String key : keys) { - if (mExecuteMap.get(key).isHighestPriorityTask()) return false; - } if (pollFirstTask()) { return putNewTask(task); } @@ -83,19 +85,19 @@ public class ExecutePool implements IPool { } /** - * 设置执行任务数 + * 设置执行队列最大任务数 * - * @param downloadNum 下载数 + * @param maxNum 下载数 */ - public void setDownloadNum(int downloadNum) { + public void setMaxNum(int maxNum) { try { - ArrayBlockingQueue temp = new ArrayBlockingQueue<>(downloadNum); + ArrayBlockingQueue temp = new ArrayBlockingQueue<>(maxNum); TASK task; while ((task = mExecuteQueue.poll(TIME_OUT, TimeUnit.MICROSECONDS)) != null) { temp.offer(task); } mExecuteQueue = temp; - mSize = downloadNum; + mSize = maxNum; } catch (InterruptedException e) { e.printStackTrace(); } @@ -104,9 +106,9 @@ public class ExecutePool implements IPool { /** * 添加新任务 * - * @param newTask 新下载任务 + * @param newTask 新任务 */ - private boolean putNewTask(TASK newTask) { + boolean putNewTask(TASK newTask) { String url = newTask.getKey(); boolean s = mExecuteQueue.offer(newTask); Log.w(TAG, "任务添加" + (s ? "成功" : "失败,【" + url + "】")); @@ -119,16 +121,13 @@ public class ExecutePool implements IPool { /** * 队列满时,将移除下载队列中的第一个任务 */ - private boolean pollFirstTask() { + boolean pollFirstTask() { try { TASK oldTask = mExecuteQueue.poll(TIME_OUT, TimeUnit.MICROSECONDS); if (oldTask == null) { Log.e(TAG, "移除任务失败"); return false; } - if (oldTask.isHighestPriorityTask()) { - return false; - } oldTask.stop(); String key = CommonUtil.keyToHashKey(oldTask.getKey()); mExecuteMap.remove(key); @@ -142,7 +141,7 @@ public class ExecutePool implements IPool { @Override public TASK pollTask() { synchronized (AriaManager.LOCK) { try { - TASK task = null; + TASK task; task = mExecuteQueue.poll(TIME_OUT, TimeUnit.MICROSECONDS); if (task != null) { String url = task.getKey(); @@ -173,22 +172,22 @@ public class ExecutePool implements IPool { Log.e(TAG, "任务不能为空"); return false; } else { - String key = CommonUtil.keyToHashKey(task.getKey()); - mExecuteMap.remove(key); - return mExecuteQueue.remove(task); + return removeTask(task.getKey()); } } } - @Override public boolean removeTask(String downloadUrl) { + @Override public boolean removeTask(String key) { synchronized (AriaManager.LOCK) { - if (TextUtils.isEmpty(downloadUrl)) { + if (TextUtils.isEmpty(key)) { Log.e(TAG, "请传入有效的任务key"); return false; } - String key = CommonUtil.keyToHashKey(downloadUrl); - TASK task = mExecuteMap.get(key); - mExecuteMap.remove(key); + String convertKey = CommonUtil.keyToHashKey(key); + TASK task = mExecuteMap.get(convertKey); + mExecuteMap.remove(convertKey); + + DQueueMapping.getInstance().removeType(key); return mExecuteQueue.remove(task); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DownloadExecutePool.java b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DownloadExecutePool.java new file mode 100644 index 00000000..6123a62b --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DownloadExecutePool.java @@ -0,0 +1,82 @@ +/* + * 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.queue.pool; + +import android.util.Log; +import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.inf.AbsTask; +import com.arialyy.aria.util.CommonUtil; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +/** + * Created by AriaL on 2017/6/29. + * 单个下载任务的执行池 + */ +public class DownloadExecutePool extends BaseExecutePool { + private final String TAG = "DownloadExecutePool"; + + @Override protected int getMaxSize() { + return AriaManager.getInstance(AriaManager.APP).getDownloadConfig().getMaxTaskNum(); + } + + @Override public boolean putTask(TASK task) { + synchronized (AriaManager.LOCK) { + if (task == null) { + Log.e(TAG, "任务不能为空!!"); + return false; + } + String url = task.getKey(); + if (mExecuteQueue.contains(task)) { + Log.e(TAG, "队列中已经包含了该任务,任务key【" + url + "】"); + return false; + } else { + if (mExecuteQueue.size() >= mSize) { + Set keys = mExecuteMap.keySet(); + for (String key : keys) { + if (mExecuteMap.get(key).isHighestPriorityTask()) return false; + } + if (pollFirstTask()) { + return putNewTask(task); + } + } else { + return putNewTask(task); + } + } + } + return false; + } + + @Override boolean pollFirstTask() { + try { + TASK oldTask = mExecuteQueue.poll(TIME_OUT, TimeUnit.MICROSECONDS); + if (oldTask == null) { + Log.e(TAG, "移除任务失败"); + return false; + } + if (oldTask.isHighestPriorityTask()) { + return false; + } + oldTask.stop(); + String key = CommonUtil.keyToHashKey(oldTask.getKey()); + mExecuteMap.remove(key); + } catch (InterruptedException e) { + e.printStackTrace(); + return false; + } + return true; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DownloadSharePool.java b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DownloadSharePool.java new file mode 100644 index 00000000..cd7625bd --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DownloadSharePool.java @@ -0,0 +1,43 @@ +/* + * 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.queue.pool; + +import com.arialyy.aria.core.AriaManager; + +/** + * Created by Aria.Lao on 2017/7/17. + * 下载任务池,该池子为简单任务和任务组共用 + */ +public class DownloadSharePool { + private static volatile DownloadSharePool INSTANCE; + + public DownloadExecutePool executePool; + public BaseCachePool cachePool; + + private DownloadSharePool() { + executePool = new DownloadExecutePool<>(); + cachePool = new BaseCachePool<>(); + } + + public static DownloadSharePool getInstance() { + if (INSTANCE == null) { + synchronized (AriaManager.LOCK) { + INSTANCE = new DownloadSharePool(); + } + } + return INSTANCE; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/IPool.java b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/IPool.java index 53378e9c..ae6ab6c7 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/IPool.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/IPool.java @@ -16,24 +16,24 @@ package com.arialyy.aria.core.queue.pool; -import com.arialyy.aria.core.inf.ITask; +import com.arialyy.aria.core.inf.AbsTask; /** * Created by lyy on 2016/8/14. * 任务池 */ -public interface IPool { +interface IPool { /** * 将下载任务添加到任务池中 */ - public boolean putTask(T task); + boolean putTask(T task); /** * 按照队列原则取出下载任务 * * @return 返回null或者下载任务 */ - public T pollTask(); + T pollTask(); /** * 通过下载链接获取下载任务,当任务不为空时,队列将删除该下载任务 @@ -41,7 +41,7 @@ public interface IPool { * @param downloadUrl 下载链接 * @return 返回null或者下载任务 */ - public T getTask(String downloadUrl); + T getTask(String downloadUrl); /** * 删除任务池中的下载任务 @@ -49,20 +49,20 @@ public interface IPool { * @param task 下载任务 * @return true:移除成功 */ - public boolean removeTask(T task); + boolean removeTask(T task); /** - * 通过下载链接移除下载任务 + * 通过key除下载任务 * - * @param downloadUrl 下载链接 + * @param key 下载链接 * @return true:移除成功 */ - public boolean removeTask(String downloadUrl); + boolean removeTask(String key); /** * 池子大小 * - * @return 返回缓存池或者当前任务池大小 + * @return 返回缓存池或者执行池大小 */ - public int size(); + int size(); } \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/UploadExecutePool.java b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/UploadExecutePool.java new file mode 100644 index 00000000..b5437f2c --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/UploadExecutePool.java @@ -0,0 +1,28 @@ +/* + * 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.queue.pool; + +import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.inf.AbsTask; + +/** + * Created by Aria.Lao on 2017/7/17. + */ +public class UploadExecutePool extends BaseExecutePool { + @Override protected int getMaxSize() { + return AriaManager.getInstance(AriaManager.APP).getUploadConfig().getMaxTaskNum(); + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/UploadSharePool.java b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/UploadSharePool.java new file mode 100644 index 00000000..f75af152 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/UploadSharePool.java @@ -0,0 +1,43 @@ +/* + * 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.queue.pool; + +import com.arialyy.aria.core.AriaManager; + +/** + * Created by Aria.Lao on 2017/7/17. + * 下载任务池,该池子为简单任务和任务组共用 + */ +public class UploadSharePool { + private static volatile UploadSharePool INSTANCE; + + public UploadExecutePool executePool; + public BaseCachePool cachePool; + + private UploadSharePool() { + executePool = new UploadExecutePool(); + cachePool = new BaseCachePool<>(); + } + + public static UploadSharePool getInstance() { + if (INSTANCE == null) { + synchronized (AriaManager.LOCK) { + INSTANCE = new UploadSharePool(); + } + } + return INSTANCE; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/scheduler/AbsSchedulers.java b/Aria/src/main/java/com/arialyy/aria/core/scheduler/AbsSchedulers.java index f53bf4e7..8e40eacb 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/scheduler/AbsSchedulers.java +++ b/Aria/src/main/java/com/arialyy/aria/core/scheduler/AbsSchedulers.java @@ -21,9 +21,9 @@ import android.util.Log; import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.download.DownloadTask; 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.ITask; import com.arialyy.aria.core.queue.ITaskQueue; import com.arialyy.aria.core.upload.UploadTask; import java.util.Iterator; @@ -33,28 +33,32 @@ import java.util.concurrent.ConcurrentHashMap; /** * Created by lyy on 2017/6/4. + * 事件调度器,用于处理任务状态的调度 */ -public abstract class AbsSchedulers, QUEUE extends ITaskQueue> +abstract class AbsSchedulers, QUEUE extends ITaskQueue> implements ISchedulers { - private static final String TAG = "AbsSchedulers"; + private final String TAG = "AbsSchedulers"; - /** - * 下载的动态生成的代理类后缀 - */ - String DOWNLOAD_PROXY_CLASS_SUFFIX = "$$DownloadListenerProxy"; - - /** - * 上传的动态生成的代理类后缀 - */ - String UPLOAD_PROXY_CLASS_SUFFIX = "$$UploadListenerProxy"; + static final int DOWNLOAD = 0xa1; + static final int UPLOAD = 0xa2; + static final int DOWNLOAD_GROUP = 0xa3; protected QUEUE mQueue; - protected boolean isDownload = true; private Map> mSchedulerListeners = new ConcurrentHashMap<>(); private Map> mObservers = new ConcurrentHashMap<>(); + /** + * 设置调度器类型 + */ + abstract int getSchedulerType(); + + /** + * 设置代理类后缀名 + */ + abstract String getProxySuffix(); + /** * @param targetName 观察者,创建该监听器的对象类名 * @param schedulerListener {@link ISchedulerListener} @@ -109,18 +113,14 @@ public abstract class AbsSchedulers createListener(String targetName) { AbsSchedulerListener listener = null; try { - Class clazz = Class.forName( - targetName + (isDownload ? DOWNLOAD_PROXY_CLASS_SUFFIX : UPLOAD_PROXY_CLASS_SUFFIX)); + Class clazz = Class.forName(targetName + getProxySuffix()); listener = (AbsSchedulerListener) clazz.newInstance(); } catch (ClassNotFoundException e) { Log.e(TAG, targetName + ",没有Aria的Download或Upload注解方法"); - //e.printStackTrace(); } catch (InstantiationException e) { Log.e(TAG, e.getMessage()); - //e.printStackTrace(); } catch (IllegalAccessException e) { Log.e(TAG, e.getMessage()); - //e.printStackTrace(); } return listener; } @@ -131,7 +131,6 @@ public abstract class AbsSchedulers 0; + } + + /** + * 获取正在执行的队列数 + */ + int getExeTaskNum() { + return mQueue.getCurrentExePoolNum(); + } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/scheduler/DQueueMapping.java b/Aria/src/main/java/com/arialyy/aria/core/scheduler/DQueueMapping.java new file mode 100644 index 00000000..623a4a0e --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/scheduler/DQueueMapping.java @@ -0,0 +1,89 @@ +/* + * 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.scheduler; + +import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.queue.DownloadGroupTaskQueue; +import com.arialyy.aria.core.queue.DownloadTaskQueue; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Created by Aria.Lao on 2017/7/13. + * 下载任务和队列的映射表 + */ +public class DQueueMapping { + + public static final int QUEUE_TYPE_DOWNLOAD = 0xa1; + public static final int QUEUE_TYPE_DOWNLOAD_GROUP = 0xa2; + public static final int QUEUE_NONE = 0xab2; + private LinkedHashMap types = new LinkedHashMap<>(); + + private static volatile DQueueMapping instance = null; + + private DQueueMapping() { + + } + + public static DQueueMapping getInstance() { + if (instance == null) { + synchronized (AriaManager.LOCK) { + instance = new DQueueMapping(); + } + } + return instance; + } + + /** + * map中增加类型 + * + * @param key 任务的key + * @param type {@link #QUEUE_TYPE_DOWNLOAD}、{@link #QUEUE_TYPE_DOWNLOAD} + */ + public void addType(String key, int type) { + types.put(key, type); + } + + /** + * @param key 任务的key + */ + public void removeType(String key) { + types.remove(key); + } + + /** + * 获取下一个任务类型 + * + * @return {@link #QUEUE_TYPE_DOWNLOAD}、{@link #QUEUE_TYPE_DOWNLOAD} + */ + public int nextType() { + Iterator> iter = types.entrySet().iterator(); + if (iter.hasNext()) { + Map.Entry next = iter.next(); + int type = next.getValue(); + iter.remove(); + return type; + } + return QUEUE_NONE; + } + + public boolean canStart() { + return DownloadTaskQueue.getInstance().getCurrentExePoolNum() + + DownloadGroupTaskQueue.getInstance().getCurrentExePoolNum() >= AriaManager.getInstance( + AriaManager.APP).getDownloadConfig().getMaxTaskNum(); + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/scheduler/DownloadGroupSchedulers.java b/Aria/src/main/java/com/arialyy/aria/core/scheduler/DownloadGroupSchedulers.java new file mode 100644 index 00000000..dd4dde4a --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/scheduler/DownloadGroupSchedulers.java @@ -0,0 +1,74 @@ +/* + * 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.scheduler; + +import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.download.DownloadGroupEntity; +import com.arialyy.aria.core.download.DownloadGroupTask; +import com.arialyy.aria.core.download.DownloadGroupTaskEntity; +import com.arialyy.aria.core.queue.DownloadGroupTaskQueue; + +/** + * Created by AriaL on 2017/7/2. + * 任务组调度器 + */ +public class DownloadGroupSchedulers extends + AbsSchedulers { + private final String TAG = "DownloadGroupSchedulers"; + private static volatile DownloadGroupSchedulers INSTANCE = null; + + private DownloadGroupSchedulers() { + mQueue = DownloadGroupTaskQueue.getInstance(); + } + + public static DownloadGroupSchedulers getInstance() { + if (INSTANCE == null) { + synchronized (AriaManager.LOCK) { + INSTANCE = new DownloadGroupSchedulers(); + } + } + return INSTANCE; + } + + @Override int getSchedulerType() { + return DOWNLOAD_GROUP; + } + + @Override String getProxySuffix() { + return "$$DownloadGroupListenerProxy"; + } + + @Override protected void startNextTask() { + if (getExeTaskNum() + DownloadSchedulers.getInstance().getExeTaskNum() + >= AriaManager.getInstance(AriaManager.APP).getDownloadConfig().getMaxTaskNum()) { + return; + } + if (!DownloadSchedulers.getInstance().hasNextTask()) { + nextSelf(); + } else { + Integer nextType = DQueueMapping.getInstance().nextType(); + if (nextType == DQueueMapping.QUEUE_TYPE_DOWNLOAD) { + DownloadSchedulers.getInstance().nextSelf(); + } else { + nextSelf(); + } + } + } + + void nextSelf() { + super.startNextTask(); + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/scheduler/DownloadSchedulers.java b/Aria/src/main/java/com/arialyy/aria/core/scheduler/DownloadSchedulers.java index f6137abf..5226c13b 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/scheduler/DownloadSchedulers.java +++ b/Aria/src/main/java/com/arialyy/aria/core/scheduler/DownloadSchedulers.java @@ -16,11 +16,13 @@ package com.arialyy.aria.core.scheduler; +import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.queue.DownloadTaskQueue; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadTask; +import java.nio.MappedByteBuffer; /** * Created by lyy on 2016/8/16. @@ -29,12 +31,11 @@ import com.arialyy.aria.core.download.DownloadTask; public class DownloadSchedulers extends AbsSchedulers { - private static final String TAG = "DownloadSchedulers"; + private final String TAG = "DownloadSchedulers"; private static volatile DownloadSchedulers INSTANCE = null; private DownloadSchedulers() { mQueue = DownloadTaskQueue.getInstance(); - isDownload = true; } public static DownloadSchedulers getInstance() { @@ -45,4 +46,33 @@ public class DownloadSchedulers } return INSTANCE; } + + @Override int getSchedulerType() { + return DOWNLOAD; + } + + @Override String getProxySuffix() { + return "$$DownloadListenerProxy"; + } + + @Override protected void startNextTask() { + if (getExeTaskNum() + DownloadGroupSchedulers.getInstance().getExeTaskNum() + >= AriaManager.getInstance(AriaManager.APP).getDownloadConfig().getMaxTaskNum()) { + return; + } + if (!DownloadGroupSchedulers.getInstance().hasNextTask()) { + nextSelf(); + } else { + Integer nextType = DQueueMapping.getInstance().nextType(); + if (nextType == DQueueMapping.QUEUE_TYPE_DOWNLOAD_GROUP) { + DownloadGroupSchedulers.getInstance().nextSelf(); + } else { + nextSelf(); + } + } + } + + void nextSelf() { + super.startNextTask(); + } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/scheduler/ISchedulers.java b/Aria/src/main/java/com/arialyy/aria/core/scheduler/ISchedulers.java index 7549b55a..a88e1f29 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/scheduler/ISchedulers.java +++ b/Aria/src/main/java/com/arialyy/aria/core/scheduler/ISchedulers.java @@ -17,54 +17,54 @@ package com.arialyy.aria.core.scheduler; import android.os.Handler; -import com.arialyy.aria.core.inf.ITask; +import com.arialyy.aria.core.inf.AbsTask; /** * Created by lyy on 2016/11/2. * 调度器功能接口 */ -public interface ISchedulers extends Handler.Callback { +public interface ISchedulers extends Handler.Callback { /** * 断点支持 */ - public static final int SUPPORT_BREAK_POINT = 9; + int SUPPORT_BREAK_POINT = 9; /** * 任务预加载 */ - public static final int PRE = 0; + int PRE = 0; /** * 任务预加载完成 */ - public static final int POST_PRE = 1; + int POST_PRE = 1; /** * 任务开始 */ - public static final int START = 2; + int START = 2; /** * 任务停止 */ - public static final int STOP = 3; + int STOP = 3; /** * 任务失败 */ - public static final int FAIL = 4; + int FAIL = 4; /** * 任务取消 */ - public static final int CANCEL = 5; + int CANCEL = 5; /** * 任务完成 */ - public static final int COMPLETE = 6; + int COMPLETE = 6; /** * 任务处理中 */ - public static final int RUNNING = 7; + int RUNNING = 7; /** * 恢复任务 */ - public static final int RESUME = 8; + int RESUME = 8; /** * 注册下载器监听,一个观察者只能注册一次监听 @@ -72,26 +72,25 @@ public interface ISchedulers extends Handler.Callback { * @param targetName 观察者,创建该监听器的对象类名 * @param schedulerListener {@link ISchedulerListener} */ - public void addSchedulerListener(String targetName, ISchedulerListener schedulerListener); + void addSchedulerListener(String targetName, ISchedulerListener schedulerListener); /** * @param targetName 观察者,创建该监听器的对象类名 * 取消注册监听器 */ - public void removeSchedulerListener(String targetName, - ISchedulerListener schedulerListener); + void removeSchedulerListener(String targetName, ISchedulerListener schedulerListener); /** * 将当前类注册到Aria * * @param obj 观察者类 */ - public void register(Object obj); + void register(Object obj); /** * 移除注册 * * @param obj 观察者类 */ - public void unRegister(Object obj); + void unRegister(Object obj); } \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/core/scheduler/UploadSchedulers.java b/Aria/src/main/java/com/arialyy/aria/core/scheduler/UploadSchedulers.java index c3dfd361..be260d57 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/scheduler/UploadSchedulers.java +++ b/Aria/src/main/java/com/arialyy/aria/core/scheduler/UploadSchedulers.java @@ -32,7 +32,6 @@ public class UploadSchedulers private UploadSchedulers() { mQueue = UploadTaskQueue.getInstance(); - isDownload = false; } public static UploadSchedulers getInstance() { @@ -44,4 +43,12 @@ public class UploadSchedulers return INSTANCE; } + + @Override int getSchedulerType() { + return UPLOAD; + } + + @Override String getProxySuffix() { + return "$$UploadListenerProxy"; + } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/IUploadListener.java b/Aria/src/main/java/com/arialyy/aria/core/upload/IUploadListener.java index 02a9e28c..2a8cab14 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/IUploadListener.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/IUploadListener.java @@ -15,60 +15,12 @@ */ package com.arialyy.aria.core.upload; +import com.arialyy.aria.core.inf.IEventListener; + /** * Created by lyy on 2017/2/9. * 上传监听 */ -public interface IUploadListener { - - /** - * 预处理 - */ - public void onPre(); - - /** - * 预处理完成 - */ - public void onPostPre(long fileSize); - - /** - * 开始上传 - */ - public void onStart(); - - /** - * 恢复上传 - * - * @param resumeLocation 上次上传停止位置 - */ - public void onResume(long resumeLocation); - - /** - * 停止上传 - * - * @param stopLocation 上传停止位置 - */ - public void onStop(long stopLocation); - - /** - * 上传进度 - * - * @param currentLocation 当前进度 - */ - public void onProgress(long currentLocation); - - /** - * 取消上传 - */ - public void onCancel(); - - /** - * 上传成功 - */ - public void onComplete(); +interface IUploadListener extends IEventListener { - /** - * 上传失败 - */ - public void onFail(); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/ProxyHelper.java b/Aria/src/main/java/com/arialyy/aria/core/upload/ProxyHelper.java new file mode 100644 index 00000000..7162301a --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/ProxyHelper.java @@ -0,0 +1,78 @@ +/* + * 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.upload; + +import com.arialyy.aria.core.AriaManager; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Set; + +import static java.util.Collections.unmodifiableSet; + +/** + * Created by Aria.Lao on 2017/7/10. + * 代理参数获取 + */ +public class ProxyHelper { + public Set downloadCounter, uploadCounter, downloadGroupCounter; + + public static volatile ProxyHelper INSTANCE = null; + + private ProxyHelper() { + init(); + } + + public static ProxyHelper getInstance() { + if (INSTANCE == null) { + synchronized (AriaManager.LOCK) { + INSTANCE = new ProxyHelper(); + } + } + return INSTANCE; + } + + private void init() { + try { + Class clazz = Class.forName("com.arialyy.aria.ProxyClassCounter"); + Method download = clazz.getMethod("getDownloadCounter"); + Method downloadGroup = clazz.getMethod("getDownloadGroupCounter"); + Method upload = clazz.getMethod("getUploadCounter"); + Object object = clazz.newInstance(); + Object dc = download.invoke(object); + if (dc != null) { + downloadCounter = unmodifiableSet((Set) dc); + } + Object dgc = downloadGroup.invoke(object); + if (dgc != null) { + downloadGroupCounter = unmodifiableSet((Set) dgc); + } + Object uc = upload.invoke(object); + if (uc != null) { + uploadCounter = unmodifiableSet((Set) uc); + } + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadEntity.java b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadEntity.java index dafc69d9..05bfc5cf 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadEntity.java @@ -17,25 +17,18 @@ package com.arialyy.aria.core.upload; import android.os.Parcel; import android.os.Parcelable; -import com.arialyy.aria.core.inf.AbsEntity; +import com.arialyy.aria.core.inf.AbsNormalEntity; import com.arialyy.aria.orm.Ignore; +import com.arialyy.aria.orm.Primary; /** * Created by lyy on 2017/2/9. * 上传文件实体 */ -public class UploadEntity extends AbsEntity implements Parcelable { - +public class UploadEntity extends AbsNormalEntity implements Parcelable { + @Primary private String filePath; //文件路径 - private boolean isComplete = false; - - public boolean isComplete() { - return isComplete; - } - public void setComplete(boolean complete) { - isComplete = complete; - } public String getFilePath() { return filePath; @@ -45,6 +38,10 @@ public class UploadEntity extends AbsEntity implements Parcelable { this.filePath = filePath; } + @Override public String getKey() { + return filePath; + } + public UploadEntity() { } @@ -55,13 +52,11 @@ public class UploadEntity extends AbsEntity implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeString(this.filePath); - dest.writeByte(this.isComplete ? (byte) 1 : (byte) 0); } protected UploadEntity(Parcel in) { super(in); this.filePath = in.readString(); - this.isComplete = in.readByte() != 0; } @Ignore public static final Creator CREATOR = new Creator() { diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadListener.java b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadListener.java index b8cfcb6a..a4c432ae 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadListener.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadListener.java @@ -18,8 +18,7 @@ package com.arialyy.aria.core.upload; /** * Created by lyy on 2017/2/23. */ - -public class UploadListener implements IUploadListener { +class UploadListener implements IUploadListener { @Override public void onPre() { } @@ -28,7 +27,7 @@ public class UploadListener implements IUploadListener { } - @Override public void onStart() { + @Override public void onStart(long startLocation) { } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java index 38e71953..eab63b7c 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java @@ -17,30 +17,24 @@ package com.arialyy.aria.core.upload; import android.support.annotation.NonNull; import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.core.command.AbsCmd; -import com.arialyy.aria.core.command.CmdFactory; -import com.arialyy.aria.core.download.DownloadReceiver; -import com.arialyy.aria.core.inf.IEntity; +import com.arialyy.aria.core.command.normal.NormalCmdFactory; +import com.arialyy.aria.core.download.DownloadTaskEntity; +import com.arialyy.aria.core.inf.AbsReceiver; import com.arialyy.aria.core.inf.IReceiver; -import com.arialyy.aria.core.scheduler.DownloadSchedulers; import com.arialyy.aria.core.scheduler.ISchedulerListener; import com.arialyy.aria.core.scheduler.UploadSchedulers; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.CheckUtil; import com.arialyy.aria.util.CommonUtil; -import java.util.ArrayList; import java.util.List; import java.util.Set; -import java.util.regex.Pattern; /** * Created by lyy on 2017/2/6. * 上传功能接收器 */ -public class UploadReceiver implements IReceiver { +public class UploadReceiver extends AbsReceiver { private static final String TAG = "DownloadReceiver"; - public String targetName; - public Object obj; public ISchedulerListener listener; /** @@ -50,17 +44,7 @@ public class UploadReceiver implements IReceiver { */ public UploadTarget load(@NonNull String filePath) { CheckUtil.checkUploadPath(filePath); - UploadEntity entity = UploadEntity.findData(UploadEntity.class, "filePath=?", filePath); - if (entity == null) { - entity = new UploadEntity(); - } - String regex = "[/|\\\\|//]"; - Pattern p = Pattern.compile(regex); - String[] strs = p.split(filePath); - String fileName = strs[strs.length - 1]; - entity.setFileName(fileName); - entity.setFilePath(filePath); - return new UploadTarget(entity, targetName); + return new UploadTarget(filePath, targetName); } /** @@ -68,41 +52,39 @@ public class UploadReceiver implements IReceiver { */ public UploadEntity getUploadEntity(String filePath) { CheckUtil.checkUploadPath(filePath); - return DbEntity.findData(UploadEntity.class, "filePath=?", filePath); + return DbEntity.findFirst(UploadEntity.class, "filePath=?", filePath); } /** * 下载任务是否存在 */ @Override public boolean taskExists(String filePath) { - return DbEntity.findData(UploadEntity.class, "filePath=?", filePath) != null; + return DbEntity.findFirst(UploadEntity.class, "filePath=?", filePath) != null; } - @Override public List getTaskList() { + @Override public List getSimpleTaskList() { return DbEntity.findAllData(UploadEntity.class); } @Override public void stopAllTask() { - List allEntity = DbEntity.findAllData(UploadEntity.class); - List stopCmds = new ArrayList<>(); - for (UploadEntity entity : allEntity) { - if (entity.getState() == IEntity.STATE_RUNNING) { - stopCmds.add( - CommonUtil.createCmd(targetName, new UploadTaskEntity(entity), CmdFactory.TASK_STOP)); - } - } - AriaManager.getInstance(AriaManager.APP).setCmds(stopCmds).exe(); + AriaManager.getInstance(AriaManager.APP) + .setCmd(NormalCmdFactory.getInstance() + .createCmd(targetName, new UploadTaskEntity(), NormalCmdFactory.TASK_STOP_ALL)) + .exe(); } - @Override public void removeAllTask() { + /** + * 删除所有任务 + * + * @param removeFile {@code true} 删除已经上传完成的任务,不仅删除上传记录,还会删除已经上传完成的文件,{@code false} + * 如果文件已经上传完成,只删除上传记录 + */ + @Override public void removeAllTask(boolean removeFile) { final AriaManager am = AriaManager.getInstance(AriaManager.APP); - List allEntity = DbEntity.findAllData(UploadEntity.class); - List cancelCmds = new ArrayList<>(); - for (UploadEntity entity : allEntity) { - cancelCmds.add( - CommonUtil.createCmd(targetName, new UploadTaskEntity(entity), CmdFactory.TASK_CANCEL)); - } - am.setCmds(cancelCmds).exe(); + + am.setCmd(CommonUtil.createCmd(targetName, new DownloadTaskEntity(), + NormalCmdFactory.TASK_CANCEL_ALL)).exe(); + Set keys = am.getReceiver().keySet(); for (String key : keys) { IReceiver receiver = am.getReceiver().get(key); @@ -118,14 +100,19 @@ public class UploadReceiver implements IReceiver { /** * 添加调度器回调 + * + * @see #register() */ - public UploadReceiver addSchedulerListener(ISchedulerListener listener) { + @Deprecated public UploadReceiver addSchedulerListener(ISchedulerListener listener) { this.listener = listener; UploadSchedulers.getInstance().addSchedulerListener(targetName, listener); return this; } - @Override public void removeSchedulerListener() { + /** + * @see #unRegister() + */ + @Deprecated @Override public void removeSchedulerListener() { if (listener != null) { UploadSchedulers.getInstance().removeSchedulerListener(targetName, listener); } @@ -135,11 +122,19 @@ public class UploadReceiver implements IReceiver { * 将当前类注册到Aria */ public UploadReceiver register() { - UploadSchedulers.getInstance().register(obj); + String className = obj.getClass().getName(); + Set cCounter = ProxyHelper.getInstance().uploadCounter; + if (cCounter != null && cCounter.contains(className)) { + UploadSchedulers.getInstance().register(obj); + } return this; } @Override public void unRegister() { - UploadSchedulers.getInstance().unRegister(obj); + String className = obj.getClass().getName(); + Set dCounter = ProxyHelper.getInstance().uploadCounter; + if (dCounter != null && dCounter.contains(className)) { + UploadSchedulers.getInstance().unRegister(obj); + } } } \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadTarget.java index 1868d51c..dd372289 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadTarget.java @@ -16,28 +16,49 @@ package com.arialyy.aria.core.upload; import android.support.annotation.NonNull; -import com.arialyy.aria.core.RequestEnum; -import com.arialyy.aria.core.inf.AbsTarget; +import com.arialyy.aria.core.inf.AbsNormalTarget; import com.arialyy.aria.core.queue.UploadTaskQueue; import com.arialyy.aria.orm.DbEntity; -import java.util.Map; +import java.util.regex.Pattern; /** * Created by lyy on 2017/2/28. */ -public class UploadTarget extends AbsTarget { +public class UploadTarget extends AbsNormalTarget { - UploadTarget(UploadEntity entity, String targetName) { - this.entity = entity; - this.targetName = targetName; - taskEntity = new UploadTaskEntity(entity); + UploadTarget(String filePath, String targetName) { + this.mTargetName = targetName; + mTaskEntity = DbEntity.findFirst(UploadTaskEntity.class, "key=?", filePath); + if (mTaskEntity == null) { + mTaskEntity = new UploadTaskEntity(); + mTaskEntity.entity = getUploadEntity(filePath); + } + if (mTaskEntity.entity == null) { + mTaskEntity.entity = getUploadEntity(filePath); + } + mEntity = mTaskEntity.entity; + } + + private UploadEntity getUploadEntity(String filePath) { + UploadEntity entity = UploadEntity.findFirst(UploadEntity.class, "filePath=?", filePath); + if (entity == null) { + entity = new UploadEntity(); + String regex = "[/|\\\\|//]"; + Pattern p = Pattern.compile(regex); + String[] strs = p.split(filePath); + String fileName = strs[strs.length - 1]; + entity.setFileName(fileName); + entity.setFilePath(filePath); + entity.insert(); + } + return entity; } /** * 设置userAgent */ public UploadTarget setUserAngent(@NonNull String userAgent) { - taskEntity.userAgent = userAgent; + mTaskEntity.userAgent = userAgent; return this; } @@ -47,7 +68,7 @@ public class UploadTarget extends AbsTarget { * @param uploadUrl 上传路径 */ public UploadTarget setUploadUrl(@NonNull String uploadUrl) { - taskEntity.uploadUrl = uploadUrl; + mTaskEntity.uploadUrl = uploadUrl; return this; } @@ -57,7 +78,7 @@ public class UploadTarget extends AbsTarget { * @param attachment 附件key */ public UploadTarget setAttachment(@NonNull String attachment) { - taskEntity.attachment = attachment; + mTaskEntity.attachment = attachment; return this; } @@ -65,7 +86,7 @@ public class UploadTarget extends AbsTarget { * 设置文件名 */ public UploadTarget setFileName(String fileName) { - entity.setFileName(fileName); + mEntity.setFileName(fileName); return this; } @@ -75,28 +96,7 @@ public class UploadTarget extends AbsTarget { * @param contentType tip:multipart/form-data */ public UploadTarget setContentType(String contentType) { - taskEntity.contentType = contentType; - return this; - } - - /** - * 给url请求添加头部 - * - * @param key 头部key - * @param header 头部value - */ - public UploadTarget addHeader(@NonNull String key, @NonNull String header) { - super._addHeader(key, header); - return this; - } - - /** - * 给url请求添加头部 - * - * @param headers key为http头部的key,Value为http头对应的配置 - */ - public UploadTarget addHeaders(Map headers) { - super._addHeaders(headers); + mTaskEntity.contentType = contentType; return this; } @@ -104,28 +104,14 @@ public class UploadTarget extends AbsTarget { * 下载任务是否存在 */ @Override public boolean taskExists() { - return UploadTaskQueue.getInstance().getTask(entity.getFilePath()) != null; - } - - /** - * 设置请求类型 - * - * @param requestEnum {@link RequestEnum} - */ - public UploadTarget setRequestMode(RequestEnum requestEnum) { - super._setRequestMode(requestEnum); - return this; - } - - private UploadEntity getDownloadEntity() { - return entity; + return UploadTaskQueue.getInstance().getTask(mEntity.getFilePath()) != null; } /** * 是否在下载 */ public boolean isUploading() { - UploadTask task = UploadTaskQueue.getInstance().getTask(entity); + UploadTask task = UploadTaskQueue.getInstance().getTask(mEntity); return task != null && task.isRunning(); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadUtil.java b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadUtil.java index 88a87caa..c0ee07d7 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadUtil.java @@ -51,7 +51,7 @@ final class UploadUtil implements Runnable { UploadUtil(UploadTaskEntity taskEntity, IUploadListener listener) { mTaskEntity = taskEntity; CheckUtil.checkTaskEntity(taskEntity); - mUploadEntity = taskEntity.uploadEntity; + mUploadEntity = taskEntity.getEntity(); if (listener == null) { throw new IllegalArgumentException("上传监听不能为空"); } @@ -109,7 +109,7 @@ final class UploadUtil implements Runnable { for (String key : keys) { addFormField(writer, key, mTaskEntity.formFields.get(key)); } - mListener.onStart(); + mListener.onStart(0); uploadFile(writer, mTaskEntity.attachment, uploadFile); Log.d(TAG, finish(writer) + ""); } catch (IOException e) { @@ -162,11 +162,11 @@ final class UploadUtil implements Runnable { writer.append("Content-Disposition: form-data; name=\"") .append(attachment) .append("\"; filename=\"") - .append(mTaskEntity.uploadEntity.getFileName()) + .append(mTaskEntity.getEntity().getFileName()) .append("\"") .append(LINE_END); writer.append("Content-Type: ") - .append(URLConnection.guessContentTypeFromName(mTaskEntity.uploadEntity.getFileName())) + .append(URLConnection.guessContentTypeFromName(mTaskEntity.getEntity().getFileName())) .append(LINE_END); writer.append("Content-Transfer-Encoding: binary").append(LINE_END); writer.append(LINE_END); diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DBConfig.java b/Aria/src/main/java/com/arialyy/aria/orm/DBConfig.java index faf40925..b8178ba5 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/DBConfig.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/DBConfig.java @@ -17,7 +17,11 @@ package com.arialyy.aria.orm; import android.text.TextUtils; import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.download.DownloadGroupEntity; +import com.arialyy.aria.core.download.DownloadGroupTaskEntity; +import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.upload.UploadEntity; +import com.arialyy.aria.core.upload.UploadTaskEntity; import java.util.HashMap; import java.util.Map; @@ -28,7 +32,7 @@ import java.util.Map; public class DBConfig { static Map mapping = new HashMap<>(); static String DB_NAME; - static int VERSION = 3; + static int VERSION = 10; static { if (TextUtils.isEmpty(DB_NAME)) { @@ -41,6 +45,10 @@ public class DBConfig { static { mapping.put("DownloadEntity", DownloadEntity.class); + mapping.put("DownloadGroupEntity", DownloadGroupEntity.class); + mapping.put("DownloadTaskEntity", DownloadTaskEntity.class); + mapping.put("DownloadGroupTaskEntity", DownloadGroupTaskEntity.class); mapping.put("UploadEntity", UploadEntity.class); + mapping.put("UploadTaskEntity", UploadTaskEntity.class); } } diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DbEntity.java b/Aria/src/main/java/com/arialyy/aria/orm/DbEntity.java index fc42e75c..f872e5d2 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/DbEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/DbEntity.java @@ -19,8 +19,10 @@ package com.arialyy.aria.orm; import android.support.annotation.NonNull; import com.arialyy.aria.util.CommonUtil; import java.lang.reflect.Field; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; +import java.util.Map; /** * Created by lyy on 2015/11/2. @@ -34,6 +36,13 @@ public class DbEntity { } + /** + * 直接执行sql语句 + */ + public static void exeSql(String sql) { + DbUtil.getInstance().exeSql(sql); + } + /** * 查询所有数据 * @@ -55,7 +64,7 @@ public class DbEntity { /** * 查询一组数据 * - * DownloadEntity.findData(DownloadEntity.class, "downloadUrl=?", downloadUrl); + * DownloadEntity.findFirst(DownloadEntity.class, "downloadUrl=?", downloadUrl); * * * @return 没有数据返回null @@ -68,12 +77,12 @@ public class DbEntity { /** * 查询一行数据 * - * DownloadEntity.findData(DownloadEntity.class, "downloadUrl=?", downloadUrl); + * DownloadEntity.findFirst(DownloadEntity.class, "downloadUrl=?", downloadUrl); * * * @return 没有数据返回null */ - public static T findData(Class clazz, String... expression) { + public static T findFirst(Class clazz, String... expression) { DbUtil util = DbUtil.getInstance(); List datas = util.findData(clazz, expression); return datas == null ? null : datas.size() > 0 ? datas.get(0) : null; @@ -97,7 +106,6 @@ public class DbEntity { * 删除当前数据 */ public void deleteData() { - //mUtil.delData(getClass(), new Object[] { "rowid" }, new Object[] { rowID }); deleteData(getClass(), "rowid=?", rowID + ""); } @@ -124,7 +132,7 @@ public class DbEntity { */ public void save() { synchronized (LOCK) { - if (DbUtil.getInstance().tableExists(getClass()) && thisIsExist()) { + if (thisIsExist()) { update(); } else { insert(); @@ -136,7 +144,8 @@ public class DbEntity { * 查找数据在表中是否存在 */ private boolean thisIsExist() { - return findData(getClass(), "rowid=?", rowID + "") != null; + DbUtil util = DbUtil.getInstance(); + return util.isExist(getClass(), rowID); } /** @@ -147,7 +156,7 @@ public class DbEntity { updateRowID(); } - private T findData(Class clazz, @NonNull String[] wheres, + private T findFirst(Class clazz, @NonNull String[] wheres, @NonNull String[] values) { DbUtil util = DbUtil.getInstance(); List list = util.findData(clazz, wheres, values); @@ -156,7 +165,7 @@ public class DbEntity { private void updateRowID() { try { - Field[] fields = CommonUtil.getFields(getClass()); + List fields = CommonUtil.getAllFields(getClass()); List where = new ArrayList<>(); List values = new ArrayList<>(); for (Field field : fields) { @@ -165,9 +174,22 @@ public class DbEntity { continue; } where.add(field.getName()); - values.add(field.get(this) + ""); + Type type = field.getType(); + if (SqlHelper.isOneToOne(field)) { + values.add(SqlHelper.getOneToOneParams(field)); + } else if (type == List.class) { + if (SqlHelper.isOneToMany(field)) { + values.add(SqlHelper.getOneToManyElementParams(field)); + } else { + values.add(SqlHelper.list2Str(this, field)); + } + } else if (type == Map.class) { + values.add(SqlHelper.map2Str((Map) field.get(this))); + } else { + values.add(field.get(this) + ""); + } } - DbEntity entity = findData(getClass(), where.toArray(new String[where.size()]), + DbEntity entity = findFirst(getClass(), where.toArray(new String[where.size()]), values.toArray(new String[values.size()])); if (entity != null) { rowID = entity.rowID; diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DbUtil.java b/Aria/src/main/java/com/arialyy/aria/orm/DbUtil.java index e02fd7d4..32cf7b03 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/DbUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/DbUtil.java @@ -64,6 +64,13 @@ public class DbUtil { return INSTANCE; } + /** + * 执行sql语句 + */ + void exeSql(String sql) { + mDb.execSQL(sql); + } + /** * 删除某条数据 */ @@ -97,6 +104,18 @@ public class DbUtil { return SqlHelper.findData(mDb, clazz, expression); } + /** + * 通过rowId判断数据是否存在 + */ + synchronized boolean isExist(Class clazz, int rowId) { + checkDb(); + String sql = "SELECT rowid FROM " + CommonUtil.getClassName(clazz) + " WHERE rowid=" + rowId; + Cursor cursor = mDb.rawQuery(sql, null); + boolean isExist = cursor.getCount() > 0; + cursor.close(); + return isExist; + } + /** * 条件查寻数据 */ @@ -127,7 +146,7 @@ public class DbUtil { SqlHelper.createTable(mDb, clazz, tableName); } - private void checkDb(){ + private void checkDb() { if (mDb == null || !mDb.isOpen()) { mDb = mHelper.getReadableDatabase(); } @@ -175,7 +194,7 @@ public class DbUtil { Log.e(TAG, "请输入删除条件"); return -1; } else if (wheres.length != values.length) { - Log.e(TAG, "key 和 vaule 长度不相等"); + Log.e(TAG, "groupName 和 vaule 长度不相等"); return -1; } StringBuilder sb = new StringBuilder(); diff --git a/Aria/src/main/java/com/arialyy/aria/orm/NormalList.java b/Aria/src/main/java/com/arialyy/aria/orm/NormalList.java new file mode 100644 index 00000000..2d2ba512 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/orm/NormalList.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.orm; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Created by AriaL on 2017/7/4. + * 基本类型的List,只能用于常见的数据类型,如果是一对多的复杂数据结构,需要使用{@link OneToMany} + */ +@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface NormalList { + /** + * 数据类型 + */ + Class clazz(); +} diff --git a/Aria/src/main/java/com/arialyy/aria/orm/OneToMany.java b/Aria/src/main/java/com/arialyy/aria/orm/OneToMany.java new file mode 100644 index 00000000..8f529e1b --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/orm/OneToMany.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.orm; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Created by AriaL on 2017/7/4. + * 一对多 + */ +@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface OneToMany { + /** + * 关联的表 + */ + Class table(); + /** + * 关联的主键 + */ + String key(); +} diff --git a/Aria/src/main/java/com/arialyy/aria/orm/OneToOne.java b/Aria/src/main/java/com/arialyy/aria/orm/OneToOne.java new file mode 100644 index 00000000..7641fd55 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/orm/OneToOne.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.orm; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Created by AriaL on 2017/7/4. + * 一对一 + */ +@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface OneToOne { + + /** + * 关联的表 + */ + Class table(); + + /** + * 关联的主键 + */ + String key(); +} diff --git a/Aria/src/main/java/com/arialyy/aria/orm/Id.java b/Aria/src/main/java/com/arialyy/aria/orm/Primary.java similarity index 97% rename from Aria/src/main/java/com/arialyy/aria/orm/Id.java rename to Aria/src/main/java/com/arialyy/aria/orm/Primary.java index a59b85d9..365f1713 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/Id.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/Primary.java @@ -25,6 +25,6 @@ import java.lang.annotation.Target; * Created by lyy on 2015/11/2. * 表ID字段指定 */ -@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Id { +@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Primary { int value() default -1; } \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/orm/SqlHelper.java b/Aria/src/main/java/com/arialyy/aria/orm/SqlHelper.java index 784900b1..2cdc017d 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/SqlHelper.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/SqlHelper.java @@ -28,9 +28,13 @@ import com.arialyy.aria.util.CheckUtil; import com.arialyy.aria.util.CommonUtil; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -186,11 +190,26 @@ final class SqlHelper extends SQLiteOpenHelper { sql = String.format(sql, params); print(FIND_DATA, sql); Cursor cursor = db.rawQuery(sql, null); - List data = cursor.getCount() > 0 ? newInstanceEntity(clazz, cursor) : null; + List data = cursor.getCount() > 0 ? newInstanceEntity(db, clazz, cursor) : null; + cursor.close(); close(db); return data; } + /** + * 条件查寻数据 + */ + static synchronized List findData(SQLiteDatabase db, String tableName, + String... expression) { + Class clazz = null; + try { + clazz = (Class) Class.forName(tableName); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + return findData(db, clazz, expression); + } + /** * 条件查寻数据 */ @@ -201,7 +220,7 @@ final class SqlHelper extends SQLiteOpenHelper { Log.e(TAG, "请输入查询条件"); return null; } else if (wheres.length != values.length) { - Log.e(TAG, "key 和 vaule 长度不相等"); + Log.e(TAG, "groupName 和 vaule 长度不相等"); return null; } StringBuilder sb = new StringBuilder(); @@ -214,7 +233,8 @@ final class SqlHelper extends SQLiteOpenHelper { } print(FIND_DATA, sb.toString()); Cursor cursor = db.rawQuery(sb.toString(), null); - List data = cursor.getCount() > 0 ? newInstanceEntity(clazz, cursor) : null; + List data = cursor.getCount() > 0 ? newInstanceEntity(db, clazz, cursor) : null; + cursor.close(); close(db); return data; } @@ -228,7 +248,8 @@ final class SqlHelper extends SQLiteOpenHelper { sb.append("SELECT rowid, * FROM ").append(CommonUtil.getClassName(clazz)); print(FIND_ALL_DATA, sb.toString()); Cursor cursor = db.rawQuery(sb.toString(), null); - List data = cursor.getCount() > 0 ? newInstanceEntity(clazz, cursor) : null; + List data = cursor.getCount() > 0 ? newInstanceEntity(db, clazz, cursor) : null; + cursor.close(); close(db); return data; } @@ -270,11 +291,26 @@ final class SqlHelper extends SQLiteOpenHelper { } sb.append(i > 0 ? ", " : ""); try { - Object value = field.get(dbEntity); - sb.append(field.getName()) - .append("='") - .append(value == null ? "" : value.toString()) - .append("'"); + String value; + sb.append(field.getName()).append("='"); + Type type = field.getType(); + if (type == Map.class) { + value = map2Str((Map) field.get(dbEntity)); + } else if (type == List.class) { + if (isOneToMany(field)) { + value = getOneToManyElementParams(field); + } else { + value = list2Str(dbEntity, field); + } + } else if (isOneToOne(field)) { + value = getOneToOneParams(field); + } else { + Object obj = field.get(dbEntity); + value = obj == null ? "" : obj.toString(); + } + + sb.append(value == null ? "" : value); + sb.append("'"); } catch (IllegalAccessException e) { e.printStackTrace(); } @@ -304,24 +340,39 @@ final class SqlHelper extends SQLiteOpenHelper { continue; } sb.append(i > 0 ? ", " : ""); + //sb.append(getFieldName(field.getType(), field)); sb.append(field.getName()); i++; } sb.append(") VALUES ("); i = 0; - for (Field field : fields) { - field.setAccessible(true); - if (ignoreField(field)) { - continue; - } - sb.append(i > 0 ? ", " : ""); - sb.append("'"); - try { - sb.append(field.get(dbEntity)).append("'"); - } catch (IllegalAccessException e) { - e.printStackTrace(); + try { + for (Field field : fields) { + field.setAccessible(true); + if (ignoreField(field)) { + continue; + } + sb.append(i > 0 ? ", " : ""); + sb.append("'"); + Type type = field.getType(); + if (type == Map.class) { + sb.append(map2Str((Map) field.get(dbEntity))); + } else if (type == List.class) { + if (isOneToMany(field)) { + sb.append(getOneToManyElementParams(field)); + } else { + sb.append(list2Str(dbEntity, field)); + } + } else if (isOneToOne(field)) { + sb.append(getOneToOneParams(field)); + } else { + sb.append(field.get(dbEntity)); + } + sb.append("'"); + i++; } - i++; + } catch (IllegalAccessException e) { + e.printStackTrace(); } sb.append(")"); print(INSERT_DATA, sb.toString()); @@ -330,6 +381,104 @@ final class SqlHelper extends SQLiteOpenHelper { close(db); } + /** + * 获取一对一参数 + */ + static String getOneToOneParams(Field field) { + OneToOne oneToOne = field.getAnnotation(OneToOne.class); + if (oneToOne == null) { + throw new IllegalArgumentException("@OneToOne注解的对象必须要有@Primary注解的字段"); + } + return oneToOne.table().getName() + "$$" + oneToOne.key(); + } + + /** + * 获取List一对多参数 + * + * @param field list反射字段 + */ + static String getOneToManyElementParams(Field field) { + OneToMany oneToMany = field.getAnnotation(OneToMany.class); + if (oneToMany == null) { + throw new IllegalArgumentException("一对多元素必须被@OneToMany注解"); + } + //关联的表名 + String tableName = oneToMany.table().getName(); + //关联的字段 + String key = oneToMany.key(); + return tableName + "$$" + key; + } + + /** + * 列表数据转字符串 + * + * @param field list反射字段 + */ + static String list2Str(DbEntity dbEntity, Field field) throws IllegalAccessException { + NormalList normalList = field.getAnnotation(NormalList.class); + if (normalList == null) { + throw new IllegalArgumentException("List中元素必须被@NormalList注解"); + } + List list = (List) field.get(dbEntity); + if (list == null || list.isEmpty()) return ""; + StringBuilder sb = new StringBuilder(); + for (Object aList : list) { + sb.append(aList).append("$$"); + } + return sb.toString(); + } + + /** + * 字符串转列表 + * + * @param str 数据库中的字段 + * @return 如果str为null,则返回null + */ + private static List str2List(String str, Field field) { + NormalList normalList = field.getAnnotation(NormalList.class); + if (normalList == null) { + throw new IllegalArgumentException("List中元素必须被@NormalList注解"); + } + if (TextUtils.isEmpty(str)) return null; + String[] datas = str.split("$$"); + List list = new ArrayList(); + String type = normalList.clazz().getName(); + for (String data : datas) { + list.add(checkData(data, type)); + } + return list; + } + + private static Object checkData(String type, String data) { + switch (type) { + case "String": + return data; + case "int": + case "Integer": + return Integer.parseInt(data); + case "double": + case "Double": + return Double.parseDouble(data); + case "float": + case "Float": + return Float.parseFloat(data); + } + return null; + } + + /** + * 查找class的主键字段 + * + * @return 返回主键字段名 + */ + private static String getPrimaryName(Class clazz) { + List fields = CommonUtil.getAllFields(clazz); + for (Field field : fields) { + if (isPrimary(field)) return field.getName(); + } + return null; + } + /** * 查找表是否存在 * @@ -380,9 +529,13 @@ final class SqlHelper extends SQLiteOpenHelper { if (ignoreField(field)) { continue; } - sb.append(field.getName()); Class type = field.getType(); - if (type == String.class) { + sb.append(field.getName()); + if (type == String.class + || type == Map.class + || type == List.class + || isOneToOne(field) + || type.isEnum()) { sb.append(" varchar"); } else if (type == int.class || type == Integer.class) { sb.append(" interger"); @@ -401,6 +554,10 @@ final class SqlHelper extends SQLiteOpenHelper { } else { continue; } + if (isPrimary(field)) { + //sb.append(" PRIMARY KEY"); + sb.append(" NOT NULL"); + } sb.append(","); } String str = sb.toString(); @@ -447,8 +604,8 @@ final class SqlHelper extends SQLiteOpenHelper { /** * 根据数据游标创建一个具体的对象 */ - static synchronized List newInstanceEntity(Class clazz, - Cursor cursor) { + private static synchronized List newInstanceEntity(SQLiteDatabase db, + Class clazz, Cursor cursor) { List fields = CommonUtil.getAllFields(clazz); List entitys = new ArrayList<>(); if (fields != null && fields.size() > 0) { @@ -479,8 +636,39 @@ final class SqlHelper extends SQLiteOpenHelper { field.set(entity, new Date(cursor.getString(column))); } else if (type == byte[].class) { field.set(entity, cursor.getBlob(column)); - } else { - continue; + } else if (type == Map.class) { + field.set(entity, str2Map(cursor.getString(column))); + } else if (type == List.class) { + String value = cursor.getString(column); + if (isOneToMany(field)) { + //主键字段 + String primaryKey = getPrimaryName(clazz); + if (TextUtils.isEmpty(primaryKey)) { + throw new IllegalArgumentException("List中的元素对象必须需要@Primary注解的字段"); + } + //list字段保存的数据 + int kc = cursor.getColumnIndex(primaryKey); + String primaryData = cursor.getString(kc); + if (TextUtils.isEmpty(primaryData)) continue; + List list = findForeignData(db, primaryData, value); + if (list == null) continue; + field.set(entity, findForeignData(db, primaryData, value)); + } else { + field.set(entity, str2List(value, field)); + } + } else if (isOneToOne(field)) { + String primaryKey = getPrimaryName(clazz); + if (TextUtils.isEmpty(primaryKey)) { + throw new IllegalArgumentException("@OneToOne的注解对象必须需要@Primary注解的字段"); + } + int kc = cursor.getColumnIndex(primaryKey); + String params = cursor.getString(column); + String primaryData = cursor.getString(kc); + if (TextUtils.isEmpty(primaryData) || primaryData.equalsIgnoreCase("null")) continue; + List list = findForeignData(db, primaryData, params); + if (list != null && list.size() > 0) { + field.set(entity, list.get(0)); + } } } entity.rowID = cursor.getInt(cursor.getColumnIndex("rowid")); @@ -496,6 +684,53 @@ final class SqlHelper extends SQLiteOpenHelper { return entitys; } + /** + * 查找一对多、一对一的关联数据 + * + * @param primary 当前表的主键 + * @param childParams 当前表关联数据的类名 $$ 主键名 + */ + private static List findForeignData(SQLiteDatabase db, String primary, + String childParams) { + String[] params = childParams.split("\\$\\$"); + return findData(db, params[0], params[1] + "=?", primary); + } + + /** + * 字符串转Map,只支持 + *
+   *   {@code Map}
+   * 
+ */ + private static Map str2Map(String str) { + Map map = new HashMap<>(); + if (TextUtils.isEmpty(str)) { + return map; + } + String[] element = str.split(","); + for (String data : element) { + String[] s = data.split("\\$"); + map.put(s[0], s[1]); + } + return map; + } + + /** + * Map转字符串,只支持 + *
+   *   {@code Map}
+   * 
+ */ + static String map2Str(Map map) { + StringBuilder sb = new StringBuilder(); + Set keys = map.keySet(); + for (String key : keys) { + sb.append(key).append("$").append(map.get(key)).append(","); + } + String str = sb.toString(); + return TextUtils.isEmpty(str) ? str : str.substring(0, str.length() - 1); + } + private static void close(SQLiteDatabase db) { //if (db != null && db.isOpen()) db.close(); } @@ -511,7 +746,7 @@ final class SqlHelper extends SQLiteOpenHelper { * @return true 忽略该字段 */ static boolean ignoreField(Field field) { - // field.isSynthetic(), 使用as热启动App时,AS会自动给你的clss添加change字段 + // field.isSynthetic(), 使用as热启动App时,AS会自动给你的class添加change字段 Ignore ignore = field.getAnnotation(Ignore.class); int modifiers = field.getModifiers(); return (ignore != null && ignore.value()) @@ -520,4 +755,28 @@ final class SqlHelper extends SQLiteOpenHelper { || Modifier.isStatic(modifiers) || Modifier.isFinal(modifiers); } + + /** + * 判断是否一对多注解 + */ + static boolean isOneToMany(Field field) { + OneToMany oneToMany = field.getAnnotation(OneToMany.class); + return oneToMany != null; + } + + /** + * 判断是否是一对一注解 + */ + static boolean isOneToOne(Field field) { + OneToOne oneToOne = field.getAnnotation(OneToOne.class); + return oneToOne != null; + } + + /** + * 判断是否是主键 + */ + static boolean isPrimary(Field field) { + Primary pk = field.getAnnotation(Primary.class); + return pk != null; + } } \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/util/CheckUtil.java b/Aria/src/main/java/com/arialyy/aria/util/CheckUtil.java index c083c46e..34c54227 100644 --- a/Aria/src/main/java/com/arialyy/aria/util/CheckUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/util/CheckUtil.java @@ -24,6 +24,7 @@ import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.upload.UploadTaskEntity; import com.arialyy.aria.exception.FileException; +import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -76,6 +77,24 @@ public class CheckUtil { if (TextUtils.isEmpty(downloadUrl)) throw new IllegalArgumentException("下载链接不能为null"); } + /** + * 检测下载链接组是否为null + */ + public static void checkDownloadUrls(List urls) { + if (urls == null || urls.isEmpty()) { + throw new IllegalArgumentException("链接组不能为null"); + } + } + + /** + * 检查下载任务组保存路径 + */ + public static void checkDownloadPaths(List paths) { + if (paths == null || paths.isEmpty()) { + throw new IllegalArgumentException("链接保存路径不能为null"); + } + } + /** * 检测上传地址是否为null */ @@ -88,9 +107,9 @@ public class CheckUtil { */ public static void checkTaskEntity(AbsTaskEntity entity) { if (entity instanceof DownloadTaskEntity) { - checkDownloadTaskEntity(((DownloadTaskEntity) entity).downloadEntity); + checkDownloadTaskEntity(((DownloadTaskEntity) entity).getEntity()); } else if (entity instanceof UploadTaskEntity) { - checkUploadTaskEntity(((UploadTaskEntity) entity).uploadEntity); + checkUploadTaskEntity(((UploadTaskEntity) entity).getEntity()); } } @@ -103,7 +122,7 @@ public class CheckUtil { public static boolean checkCmdEntity(AbsTaskEntity entity, boolean checkType) { boolean b = false; if (entity instanceof DownloadTaskEntity) { - DownloadEntity entity1 = ((DownloadTaskEntity) entity).downloadEntity; + DownloadEntity entity1 = ((DownloadTaskEntity) entity).getEntity(); if (entity1 == null) { Log.e(TAG, "下载实体不能为空"); } else if (checkType && TextUtils.isEmpty(entity1.getDownloadUrl())) { @@ -114,7 +133,7 @@ public class CheckUtil { b = true; } } else if (entity instanceof UploadTaskEntity) { - UploadEntity entity1 = ((UploadTaskEntity) entity).uploadEntity; + UploadEntity entity1 = ((UploadTaskEntity) entity).getEntity(); if (entity1 == null) { Log.e(TAG, "上传实体不能为空"); } else if (TextUtils.isEmpty(entity1.getFilePath())) { diff --git a/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java b/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java index 28d447ad..b4d6db90 100644 --- a/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java @@ -22,9 +22,14 @@ import android.content.SharedPreferences; import android.net.Uri; import android.text.TextUtils; import android.util.Log; -import com.arialyy.aria.core.command.CmdFactory; -import com.arialyy.aria.core.command.AbsCmd; +import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.command.normal.NormalCmdFactory; +import com.arialyy.aria.core.command.normal.AbsNormalCmd; +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.inf.AbsTaskEntity; +import com.arialyy.aria.core.upload.UploadEntity; +import com.arialyy.aria.core.upload.UploadTaskEntity; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; @@ -42,6 +47,11 @@ import java.util.Collections; import java.util.List; import java.util.Properties; import java.util.regex.Pattern; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; /** * Created by lyy on 2016/1/22. @@ -49,10 +59,140 @@ import java.util.regex.Pattern; public class CommonUtil { private static final String TAG = "CommonUtil"; + /** + * 实例化泛型的实际类型参数 + * + * @throws Exception + */ + public static void typeCheck(Type type) throws Exception { + System.out.println("该类型是" + type); + // 参数化类型 + if (type instanceof ParameterizedType) { + Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments(); + for (int i = 0; i < typeArguments.length; i++) { + // 类型变量 + if (typeArguments[i] instanceof TypeVariable) { + System.out.println("第" + (i + 1) + "个泛型参数类型是类型变量" + typeArguments[i] + ",无法实例化。"); + } + // 通配符表达式 + else if (typeArguments[i] instanceof WildcardType) { + System.out.println("第" + (i + 1) + "个泛型参数类型是通配符表达式" + typeArguments[i] + ",无法实例化。"); + } + // 泛型的实际类型,即实际存在的类型 + else if (typeArguments[i] instanceof Class) { + System.out.println("第" + (i + 1) + "个泛型参数类型是:" + typeArguments[i] + ",可以直接实例化对象"); + } + } + // 参数化类型数组或类型变量数组 + } else if (type instanceof GenericArrayType) { + System.out.println("该泛型类型是参数化类型数组或类型变量数组,可以获取其原始类型。"); + Type componentType = ((GenericArrayType) type).getGenericComponentType(); + // 类型变量 + if (componentType instanceof TypeVariable) { + System.out.println("该类型变量数组的原始类型是类型变量" + componentType + ",无法实例化。"); + } + // 参数化类型,参数化类型数组或类型变量数组 + // 参数化类型数组或类型变量数组也可以是多维的数组,getGenericComponentType()方法仅仅是去掉最右边的[] + else { + // 递归调用方法自身 + typeCheck(componentType); + } + } else if (type instanceof TypeVariable) { + System.out.println("该类型是类型变量"); + } else if (type instanceof WildcardType) { + System.out.println("该类型是通配符表达式"); + } else if (type instanceof Class) { + System.out.println("该类型不是泛型类型"); + } else { + throw new Exception(); + } + } + + /** + * 根据下载任务组的url创建key + * + * @return urls 为 null 或者 size为0,返回"" + */ + public static String getMd5Code(List urls) { + if (urls == null || urls.size() < 1) return ""; + String md5 = ""; + StringBuilder sb = new StringBuilder(); + for (String url : urls) { + sb.append(url); + } + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + md.update(sb.toString().getBytes()); + md5 = new BigInteger(1, md.digest()).toString(16); + } catch (NoSuchAlgorithmException e) { + Log.e(TAG, e.getMessage()); + } + return md5; + } + + /** + * 删除上传任务的配置,包括 + * + * @param removeFile {@code true} 不仅删除任务数据库记录,还会删除已经下载完成的文件 + * {@code false}如果任务已经完成,只删除任务数据库记录 + */ + public static void delUploadTaskConfig(boolean removeFile, UploadTaskEntity tEntity) { + UploadEntity uEntity = tEntity.getEntity(); + File file = new File(uEntity.getFilePath()); + if (removeFile) { + if (file.exists()) { + file.delete(); + } + } else { + if (!uEntity.isComplete()) { + if (file.exists()) { + file.delete(); + } + } + } + File config = new File( + AriaManager.APP.getFilesDir().getPath() + "/temp/" + uEntity.getFileName() + ".properties"); + if (config.exists()) { + config.delete(); + } + uEntity.deleteData(); + tEntity.deleteData(); + } + + /** + * 删除下载任务的配置,包括 + * + * @param removeFile {@code true} 不仅删除任务数据库记录,还会删除已经下载完成的文件 + * {@code false}如果任务已经完成,只删除任务数据库记录 + */ + public static void delDownloadTaskConfig(boolean removeFile, DownloadTaskEntity tEntity) { + DownloadEntity dEntity = tEntity.getEntity(); + File file = new File(dEntity.getDownloadPath()); + if (removeFile) { + if (file.exists()) { + file.delete(); + } + } else { + if (!dEntity.isComplete()) { + if (file.exists()) { + file.delete(); + } + } + } + + File config = new File( + AriaManager.APP.getFilesDir().getPath() + "/temp/" + dEntity.getFileName() + ".properties"); + if (config.exists()) { + config.delete(); + } + dEntity.deleteData(); + tEntity.deleteData(); + } + /** * 获取CPU核心数 */ - public static int getNumCores() { + public static int getCoresNum() { //Private Class to display only CPU devices in the directory listing class CpuFilter implements FileFilter { @Override public boolean accept(File pathname) { @@ -180,8 +320,8 @@ public class CommonUtil { } } - public static AbsCmd createCmd(String target, T entity, int cmd) { - return CmdFactory.getInstance().createCmd(target, entity, cmd); + public static AbsNormalCmd createCmd(String target, T entity, int cmd) { + return NormalCmdFactory.getInstance().createCmd(target, entity, cmd); } /** @@ -230,6 +370,10 @@ public class CommonUtil { List fields = new ArrayList<>(); Class personClazz = clazz.getSuperclass(); if (personClazz != null) { + Class rootClazz = personClazz.getSuperclass(); + if (rootClazz != null) { + Collections.addAll(fields, rootClazz.getDeclaredFields()); + } Collections.addAll(fields, personClazz.getDeclaredFields()); } Collections.addAll(fields, clazz.getDeclaredFields()); @@ -397,9 +541,11 @@ public class CommonUtil { } /** - * 创建文件 当文件不存在的时候就创建一个文件,否则直接返回文件 + * 创建文件 + * 当文件不存在的时候就创建一个文件。 + * 如果文件存在,先删除原文件,然后重新创建一个新文件 */ - public static File createFile(String path) { + public static void createFile(String path) { File file = new File(path); if (!file.getParentFile().exists()) { Log.d(TAG, "目标文件所在路径不存在,准备创建……"); @@ -408,19 +554,18 @@ public class CommonUtil { } } // 创建目标文件 + if (file.exists()) { + final File to = new File(file.getAbsolutePath() + System.currentTimeMillis()); + file.renameTo(to); + to.delete(); + } try { - if (!file.exists()) { - if (file.createNewFile()) { - Log.d(TAG, "创建文件成功:" + file.getAbsolutePath()); - } - return file; - } else { - return file; + if (file.createNewFile()) { + Log.d(TAG, "创建文件成功:" + file.getAbsolutePath()); } } catch (IOException e) { e.printStackTrace(); } - return null; } /** diff --git a/Aria/src/main/res/layout/activity_aria_file_shange.xml b/Aria/src/main/res/layout/activity_aria_file_shange.xml deleted file mode 100644 index fd652062..00000000 --- a/Aria/src/main/res/layout/activity_aria_file_shange.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Aria/src/main/res/layout/item_file.xml b/Aria/src/main/res/layout/item_file.xml deleted file mode 100644 index 7f4c8044..00000000 --- a/Aria/src/main/res/layout/item_file.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/Aria/src/main/res/values/strings.xml b/Aria/src/main/res/values/strings.xml deleted file mode 100644 index 010e9981..00000000 --- a/Aria/src/main/res/values/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ - - Aria - - 下载实体不能为空 - 下载链接不能为空 - 存储地址不能为空 - 文件名不能为空 - diff --git a/AriaAnnotations/build.gradle b/AriaAnnotations/build.gradle index 0b581ad5..a9f2139b 100644 --- a/AriaAnnotations/build.gradle +++ b/AriaAnnotations/build.gradle @@ -11,4 +11,4 @@ dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) } -//apply from: 'bintray-release.gradle' \ No newline at end of file +apply from: 'bintray-release.gradle' \ No newline at end of file diff --git a/AriaAnnotations/src/main/java/com/arialyy/annotations/AriaConstance.java b/AriaAnnotations/src/main/java/com/arialyy/annotations/AriaConstance.java index 3fd7e89a..0481539a 100644 --- a/AriaAnnotations/src/main/java/com/arialyy/annotations/AriaConstance.java +++ b/AriaAnnotations/src/main/java/com/arialyy/annotations/AriaConstance.java @@ -21,5 +21,25 @@ package com.arialyy.annotations; public interface AriaConstance { String NO_URL = ""; + + /** + * 注解方法为普通任务下载 + */ + int DOWNLOAD = 0xa1; + + /** + * 注解方法为任务组下载 + */ + int DOWNLOAD_GROUP = 0xa2; + + /** + * 注解方法为普通任务上传 + */ + int UPLOAD = 0xb1; + + /** + * 注解方法为任务组上传 + */ + int UPLOAD_GROUP = 0xb2; } diff --git a/AriaAnnotations/src/main/java/com/arialyy/annotations/Download.java b/AriaAnnotations/src/main/java/com/arialyy/annotations/Download.java index 86df2895..41110643 100644 --- a/AriaAnnotations/src/main/java/com/arialyy/annotations/Download.java +++ b/AriaAnnotations/src/main/java/com/arialyy/annotations/Download.java @@ -38,63 +38,63 @@ import java.lang.annotation.Target; /** * 如果你在方法中添加{@code @Download.onPre}注解,在预处理完成时,Aria会调用该方法 */ - @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) public @interface onPre { + @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @interface onPre { String[] value() default { AriaConstance.NO_URL }; } /** * 如果你在方法中添加{@code @Download.onTaskPre}注解,在任务预处理完成时,Aria会调用该方法 */ - @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) public @interface onTaskPre { + @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @interface onTaskPre { String[] value() default { AriaConstance.NO_URL }; } /** * 如果你在方法中添加{@code @Download.onTaskResume}注解,在任务恢复下载时,Aria会调用该方法 */ - @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) public @interface onTaskResume { + @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @interface onTaskResume { String[] value() default { AriaConstance.NO_URL }; } /** * 如果你在方法中添加{@code @Download.onTaskStart}注解,在任务开始下载时,Aria会调用该方法 */ - @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) public @interface onTaskStart { + @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @interface onTaskStart { String[] value() default { AriaConstance.NO_URL }; } /** * 如果你在方法中添加{@code @Download.onTaskStop}注解,在任务停止时,Aria会调用该方法 */ - @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) public @interface onTaskStop { + @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @interface onTaskStop { String[] value() default { AriaConstance.NO_URL }; } /** * 如果你在方法中添加{@code @Download.onTaskCancel}l注解,在任务取消时,Aria会调用该方法 */ - @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) public @interface onTaskCancel { + @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @interface onTaskCancel { String[] value() default { AriaConstance.NO_URL }; } /** * 如果你在方法中添加{@code @Download.onTaskFail)注解,在任务预失败时,Aria会调用该方法 */ - @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) public @interface onTaskFail { + @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @interface onTaskFail { String[] value() default { AriaConstance.NO_URL }; } /** * 如果你在方法中添加{@code @Download.onTaskComplete}注解,在任务完成时,Aria会调用该方法 */ - @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) public @interface onTaskComplete { + @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @interface onTaskComplete { String[] value() default { AriaConstance.NO_URL }; } /** * 如果你在方法中添加{@code @Download.onTaskRunning}注解,在任务正在下载,Aria会调用该方法 */ - @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) public @interface onTaskRunning { + @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @interface onTaskRunning { String[] value() default { AriaConstance.NO_URL }; } @@ -102,7 +102,7 @@ import java.lang.annotation.Target; * 如果你在方法中添加{@code @Download.onNoSupportBreakPoint}注解,如果该任务不支持断点,Aria会调用该方法 */ @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) - public @interface onNoSupportBreakPoint { + @interface onNoSupportBreakPoint { String[] value() default { AriaConstance.NO_URL }; } } diff --git a/AriaAnnotations/src/main/java/com/arialyy/annotations/DownloadGroup.java b/AriaAnnotations/src/main/java/com/arialyy/annotations/DownloadGroup.java new file mode 100644 index 00000000..c0777d82 --- /dev/null +++ b/AriaAnnotations/src/main/java/com/arialyy/annotations/DownloadGroup.java @@ -0,0 +1,99 @@ +/* + * 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.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Created by Aria.Lao on 2017/6/6. + * Aria下载事件被注解的方法中,参数仅能有一个,参数类型为{@link com.arialyy.aria.core.download.DownloadGroupTask} + *
+ *   
+ *      {@literal @}Download.onPre(groupName)
+ *       protected void onPre(DownloadGroupTask task) {
+ *       }
+ *   
+ * 
+ * {@literal @}Download.onPre("myGroupName"),如果你的注解中增加了url描述, + * 则表示,所有下载任务中,只有下载地址为"myGroupName"的任务才能回调该注解的方法。 + */ +@Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) public @interface DownloadGroup { + /** + * 如果你在方法中添加{@code @Download.onPre}注解,在预处理完成时,Aria会调用该方法 + */ + @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @interface onPre { + String[] value() default { AriaConstance.NO_URL }; + } + + /** + * 如果你在方法中添加{@code @Download.onTaskPre}注解,在任务预处理完成时,Aria会调用该方法 + */ + @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @interface onTaskPre { + String[] value() default { AriaConstance.NO_URL }; + } + + /** + * 如果你在方法中添加{@code @Download.onTaskResume}注解,在任务恢复下载时,Aria会调用该方法 + */ + @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @interface onTaskResume { + String[] value() default { AriaConstance.NO_URL }; + } + + /** + * 如果你在方法中添加{@code @Download.onTaskStart}注解,在任务开始下载时,Aria会调用该方法 + */ + @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @interface onTaskStart { + String[] value() default { AriaConstance.NO_URL }; + } + + /** + * 如果你在方法中添加{@code @Download.onTaskStop}注解,在任务停止时,Aria会调用该方法 + */ + @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @interface onTaskStop { + String[] value() default { AriaConstance.NO_URL }; + } + + /** + * 如果你在方法中添加{@code @Download.onTaskCancel}l注解,在任务取消时,Aria会调用该方法 + */ + @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @interface onTaskCancel { + String[] value() default { AriaConstance.NO_URL }; + } + + /** + * 如果你在方法中添加{@code @Download.onTaskFail)注解,在任务预失败时,Aria会调用该方法 + */ + @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @interface onTaskFail { + String[] value() default { AriaConstance.NO_URL }; + } + + /** + * 如果你在方法中添加{@code @Download.onTaskComplete}注解,在任务完成时,Aria会调用该方法 + */ + @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @interface onTaskComplete { + String[] value() default { AriaConstance.NO_URL }; + } + + /** + * 如果你在方法中添加{@code @Download.onTaskRunning}注解,在任务正在下载,Aria会调用该方法 + */ + @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @interface onTaskRunning { + String[] value() default { AriaConstance.NO_URL }; + } +} diff --git a/AriaAnnotations/src/main/java/com/arialyy/annotations/Upload.java b/AriaAnnotations/src/main/java/com/arialyy/annotations/Upload.java index 7906bcc7..609c6663 100644 --- a/AriaAnnotations/src/main/java/com/arialyy/annotations/Upload.java +++ b/AriaAnnotations/src/main/java/com/arialyy/annotations/Upload.java @@ -39,14 +39,14 @@ import java.lang.annotation.Target; /** * 如果你在方法中添加{@code @Upload.onPre}注解,在预处理完成时,Aria会调用该方法 */ - @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) public @interface onPre { + @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @interface onPre { String[] value() default { AriaConstance.NO_URL }; } /** * 如果你在方法中添加{@code @Upload.onTaskPre}注解,在任务预处理完成时,Aria会调用该方法 */ - @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) public @interface onTaskPre { + @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @interface onTaskPre { String[] value() default { AriaConstance.NO_URL }; } @@ -59,42 +59,42 @@ import java.lang.annotation.Target; /** * 如果你在方法中添加{@code @Upload.onTaskStart}注解,在任务开始下载时,Aria会调用该方法 */ - @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) public @interface onTaskStart { + @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @interface onTaskStart { String[] value() default { AriaConstance.NO_URL }; } /** * 如果你在方法中添加{@code @Upload.onTaskStop}注解,在任务停止时,Aria会调用该方法 */ - @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) public @interface onTaskStop { + @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @interface onTaskStop { String[] value() default { AriaConstance.NO_URL }; } /** * 如果你在方法中添加{@code @Upload.onTaskCancel}l注解,在任务取消时,Aria会调用该方法 */ - @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) public @interface onTaskCancel { + @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @interface onTaskCancel { String[] value() default { AriaConstance.NO_URL }; } /** * 如果你在方法中添加{@code @Upload.onTaskFail)注解,在任务预失败时,Aria会调用该方法 */ - @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) public @interface onTaskFail { + @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @interface onTaskFail { String[] value() default { AriaConstance.NO_URL }; } /** * 如果你在方法中添加{@code @Upload.onTaskComplete}注解,在任务完成时,Aria会调用该方法 */ - @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) public @interface onTaskComplete { + @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @interface onTaskComplete { String[] value() default { AriaConstance.NO_URL }; } /** * 如果你在方法中添加{@code @Upload.onTaskRunning}注解,在任务正在下载,Aria会调用该方法 */ - @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) public @interface onTaskRunning { + @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @interface onTaskRunning { String[] value() default { AriaConstance.NO_URL }; } @@ -102,7 +102,7 @@ import java.lang.annotation.Target; * 如果你在方法中添加{@code @Upload.onNoSupportBreakPoint}注解,如果该任务不支持断点,Aria会调用该方法 */ @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) - public @interface onNoSupportBreakPoint { + @interface onNoSupportBreakPoint { String[] value() default { AriaConstance.NO_URL }; } } diff --git a/AriaCompiler/build.gradle b/AriaCompiler/build.gradle index 38de01a1..a76e8659 100644 --- a/AriaCompiler/build.gradle +++ b/AriaCompiler/build.gradle @@ -14,4 +14,4 @@ dependencies { compile project(':AriaAnnotations') } -//apply from: 'bintray-release.gradle' \ No newline at end of file +apply from: 'bintray-release.gradle' \ No newline at end of file diff --git a/AriaCompiler/src/main/java/com/arialyy/compiler/AriaProcessor.java b/AriaCompiler/src/main/java/com/arialyy/compiler/AriaProcessor.java index 5ac4a533..814c6945 100644 --- a/AriaCompiler/src/main/java/com/arialyy/compiler/AriaProcessor.java +++ b/AriaCompiler/src/main/java/com/arialyy/compiler/AriaProcessor.java @@ -32,16 +32,17 @@ import javax.lang.model.element.TypeElement; * 事件注解扫描器 */ @AutoService(Processor.class) public class AriaProcessor extends AbstractProcessor { - ElementHandle mHandler; + ElementHandler mHandler; @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); PrintLog.init(processingEnv.getMessager()); - mHandler = new ElementHandle(processingEnv.getFiler(), processingEnv.getElementUtils()); + mHandler = new ElementHandler(processingEnv.getFiler(), processingEnv.getElementUtils()); } @Override public Set getSupportedAnnotationTypes() { Set annotataions = new LinkedHashSet<>(); + //单任务下载的注解 annotataions.add(Download.onPre.class.getCanonicalName()); annotataions.add(Download.onNoSupportBreakPoint.class.getCanonicalName()); annotataions.add(Download.onTaskCancel.class.getCanonicalName()); @@ -52,6 +53,18 @@ import javax.lang.model.element.TypeElement; annotataions.add(Download.onTaskRunning.class.getCanonicalName()); annotataions.add(Download.onTaskStart.class.getCanonicalName()); annotataions.add(Download.onTaskStop.class.getCanonicalName()); + //下载任务的注解 + annotataions.add(Download.onPre.class.getCanonicalName()); + annotataions.add(Download.onNoSupportBreakPoint.class.getCanonicalName()); + annotataions.add(Download.onTaskCancel.class.getCanonicalName()); + annotataions.add(Download.onTaskComplete.class.getCanonicalName()); + annotataions.add(Download.onTaskFail.class.getCanonicalName()); + annotataions.add(Download.onTaskPre.class.getCanonicalName()); + annotataions.add(Download.onTaskResume.class.getCanonicalName()); + annotataions.add(Download.onTaskRunning.class.getCanonicalName()); + annotataions.add(Download.onTaskStart.class.getCanonicalName()); + annotataions.add(Download.onTaskStop.class.getCanonicalName()); + //上传任务的注解 annotataions.add(Upload.onPre.class.getCanonicalName()); annotataions.add(Upload.onNoSupportBreakPoint.class.getCanonicalName()); annotataions.add(Upload.onTaskCancel.class.getCanonicalName()); @@ -73,6 +86,7 @@ import javax.lang.model.element.TypeElement; public boolean process(Set annotations, RoundEnvironment roundEnv) { mHandler.clean(); mHandler.handleDownload(roundEnv); + mHandler.handleDownloadGroup(roundEnv); mHandler.handleUpload(roundEnv); mHandler.createProxyFile(); return true; diff --git a/AriaCompiler/src/main/java/com/arialyy/compiler/ElementHandle.java b/AriaCompiler/src/main/java/com/arialyy/compiler/ElementHandle.java deleted file mode 100644 index a09f4178..00000000 --- a/AriaCompiler/src/main/java/com/arialyy/compiler/ElementHandle.java +++ /dev/null @@ -1,405 +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.compiler; - -import com.arialyy.annotations.Download; -import com.arialyy.annotations.Upload; -import com.squareup.javapoet.ClassName; -import com.squareup.javapoet.CodeBlock; -import com.squareup.javapoet.FieldSpec; -import com.squareup.javapoet.JavaFile; -import com.squareup.javapoet.MethodSpec; -import com.squareup.javapoet.ParameterSpec; -import com.squareup.javapoet.TypeSpec; -import com.squareup.javapoet.ParameterizedTypeName; -import java.io.IOException; -import java.lang.annotation.Annotation; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import javax.annotation.processing.Filer; -import javax.annotation.processing.RoundEnvironment; -import javax.lang.model.element.Element; -import javax.lang.model.element.ElementKind; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.Modifier; -import javax.lang.model.element.PackageElement; -import javax.lang.model.element.TypeElement; -import javax.lang.model.element.VariableElement; -import javax.lang.model.util.Elements; - -/** - * Created by lyy on 2017/6/6. - * 元素处理 - */ -class ElementHandle { - - private Filer mFiler; - private Elements mElementUtil; - private Map mMethods = new HashMap<>(); - - ElementHandle(Filer filer, Elements elements) { - mFiler = filer; - mElementUtil = elements; - } - - /** - * VariableElement 一般代表成员变量 - * ExecutableElement 一般代表类中的方法 - * TypeElement 一般代表代表类 - * PackageElement 一般代表Package - */ - void handleDownload(RoundEnvironment roundEnv) { - saveMethod(true, roundEnv, Download.onNoSupportBreakPoint.class, - ProxyConstance.DOWNLOAD_TASK_NO_SUPPORT_BREAKPOINT); - saveMethod(true, roundEnv, Download.onPre.class, ProxyConstance.DOWNLOAD_PRE); - saveMethod(true, roundEnv, Download.onTaskCancel.class, ProxyConstance.DOWNLOAD_TASK_CANCEL); - saveMethod(true, roundEnv, Download.onTaskComplete.class, - ProxyConstance.DOWNLOAD_TASK_COMPLETE); - saveMethod(true, roundEnv, Download.onTaskFail.class, ProxyConstance.DOWNLOAD_TASK_FAIL); - saveMethod(true, roundEnv, Download.onTaskPre.class, ProxyConstance.DOWNLOAD_TASK_PRE); - saveMethod(true, roundEnv, Download.onTaskResume.class, ProxyConstance.DOWNLOAD_TASK_RESUME); - saveMethod(true, roundEnv, Download.onTaskRunning.class, ProxyConstance.DOWNLOAD_TASK_RUNNING); - saveMethod(true, roundEnv, Download.onTaskStart.class, ProxyConstance.DOWNLOAD_TASK_START); - saveMethod(true, roundEnv, Download.onTaskStop.class, ProxyConstance.DOWNLOAD_TASK_STOP); - } - - void handleUpload(RoundEnvironment roundEnv) { - saveMethod(false, roundEnv, Upload.onNoSupportBreakPoint.class, - ProxyConstance.UPLOAD_TASK_NO_SUPPORT_BREAKPOINT); - saveMethod(false, roundEnv, Upload.onPre.class, ProxyConstance.UPLOAD_PRE); - saveMethod(false, roundEnv, Upload.onTaskCancel.class, ProxyConstance.UPLOAD_TASK_CANCEL); - saveMethod(false, roundEnv, Upload.onTaskComplete.class, ProxyConstance.UPLOAD_TASK_COMPLETE); - saveMethod(false, roundEnv, Upload.onTaskFail.class, ProxyConstance.UPLOAD_TASK_FAIL); - saveMethod(false, roundEnv, Upload.onTaskPre.class, ProxyConstance.UPLOAD_TASK_PRE); - //saveMethod(false, roundEnv, Upload.onTaskResume.class); - saveMethod(false, roundEnv, Upload.onTaskRunning.class, ProxyConstance.UPLOAD_TASK_RUNNING); - saveMethod(false, roundEnv, Upload.onTaskStart.class, ProxyConstance.UPLOAD_TASK_START); - saveMethod(false, roundEnv, Upload.onTaskStop.class, ProxyConstance.UPLOAD_TASK_STOP); - } - - /** - * 在build文件夹中生成如下代码的文件 - *
-   *   
-   * package com.arialyy.simple.download;
-   *
-   * import com.arialyy.aria.core.download.DownloadTask;
-   * import com.arialyy.aria.core.scheduler.AbsSchedulerListener;
-   *
-   * public final class SingleTaskActivity$$DownloadListenerProxy extends
-   * AbsSchedulerListener {
-   * private SingleTaskActivity obj;
-   *
-   *    public void onPre(final DownloadTask task) {
-   *      obj.onPre((DownloadTask)task);
-   *    }
-   *
-   *    public void onTaskStart(final DownloadTask task) {
-   *      obj.onStart((DownloadTask)task);
-   *    }
-   *
-   *    public void setListener(final Object obj) {
-   *      this.obj = (SingleTaskActivity)obj;
-   *    }
-   * }
-   *   
-   * 
- */ - void createProxyFile() { - Set keys = mMethods.keySet(); - try { - for (String key : keys) { - ProxyEntity entity = mMethods.get(key); - JavaFile jf = JavaFile.builder(entity.packageName, createProxyClass(entity)).build(); - - jf.writeTo(mFiler); - // 如果需要在控制台打印生成的文件,则去掉下面的注释 - //jf.writeTo(System.out); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - - /** - * 创建代理方法 - * - * @param isDownload 是否是下载的注解 - * @param annotation {@link Download}、{@link Upload} - * @param methodName 被代理类注解的方法名 - */ - private MethodSpec createProxyMethod(boolean isDownload, Class annotation, - String methodName) { - ClassName task = ClassName.get( - isDownload ? "com.arialyy.aria.core.download" : "com.arialyy.aria.core.upload", - isDownload ? "DownloadTask" : "UploadTask"); - ParameterSpec parameterSpec = - ParameterSpec.builder(task, "task").addModifiers(Modifier.FINAL).build(); - StringBuilder sb = new StringBuilder(); - sb.append("Set keys = keyMapping.get(\"") - .append(methodName) - .append("\");\n"); - sb.append("if (keys != null) {\n\tif (keys.contains(task.getKey())) {\n") - .append("\t\tobj.") - .append(methodName) - .append("(") - .append(isDownload ? "(DownloadTask)" : "(UploadTask)") - .append("task);\n") - .append("\t}\n} else {\n") - .append("\tobj.") - .append(methodName) - .append("(") - .append(isDownload ? "(DownloadTask)" : "(UploadTask)") - .append("task);\n}\n"); - - return MethodSpec.methodBuilder(annotation.getSimpleName()) - .addModifiers(Modifier.PUBLIC) - .returns(void.class) - .addParameter(parameterSpec) - .addAnnotation(Override.class) - .addCode(sb.toString()) - .build(); - } - - /** - * 创建代理类 - */ - private TypeSpec createProxyClass(ProxyEntity entity) { - TypeSpec.Builder builder = TypeSpec.classBuilder( - entity.className + (entity.isDownlaod ? ProxyConstance.DOWNLOAD_PROXY_CLASS_SUFFIX - : ProxyConstance.UPLOAD_PROXY_CLASS_SUFFIX)) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL); - - //添加被代理的类的字段 - ClassName obj = ClassName.get(entity.packageName, entity.className); - FieldSpec observerField = FieldSpec.builder(obj, "obj").addModifiers(Modifier.PRIVATE).build(); - builder.addField(observerField); - - //添加url映射表 - FieldSpec mappingField = FieldSpec.builder( - ParameterizedTypeName.get(ClassName.get(Map.class), ClassName.get(String.class), - ParameterizedTypeName.get(ClassName.get(Set.class), ClassName.get(String.class))), - "keyMapping").addModifiers(Modifier.PRIVATE).initializer("new $T()", HashMap.class).build(); - builder.addField(mappingField); - - //添加注解方法 - for (Class annotation : entity.methods.keySet()) { - MethodSpec method = - createProxyMethod(entity.isDownlaod, annotation, entity.methods.get(annotation)); - builder.addMethod(method); - } - - //增加构造函数 - CodeBlock.Builder cb = CodeBlock.builder(); - cb.add("Set set = null;\n"); - for (String methodName : entity.keyMappings.keySet()) { - Set keys = entity.keyMappings.get(methodName); - if (keys == null || keys.size() == 0) continue; - StringBuilder sb = new StringBuilder(); - sb.append("set = new $T();\n"); - for (String key : keys) { - if (key.isEmpty()) continue; - sb.append("set.add(\"").append(key).append("\");\n"); - } - - sb.append("keyMapping.put(\"").append(methodName).append("\", ").append("set);\n"); - cb.add(sb.toString(), ClassName.get(HashSet.class)); - } - MethodSpec structure = - MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC).addCode(cb.build()).build(); - builder.addMethod(structure); - - //添加设置代理的类 - ParameterSpec parameterSpec = - ParameterSpec.builder(Object.class, "obj").addModifiers(Modifier.FINAL).build(); - MethodSpec listener = MethodSpec.methodBuilder(ProxyConstance.SET_LISTENER) - .addModifiers(Modifier.PUBLIC) - .returns(void.class) - .addParameter(parameterSpec) - .addAnnotation(Override.class) - .addCode("this.obj = (" + entity.className + ")obj;\n") - .build(); - builder.addJavadoc("该文件为Aria自动生成的代理文件,请不要修改该文件的任何代码!\n"); - - //创建父类参数 - ClassName superClass = ClassName.get("com.arialyy.aria.core.scheduler", "AbsSchedulerListener"); - //创建泛型 - ClassName typeVariableName = ClassName.get( - entity.isDownlaod ? "com.arialyy.aria.core.download" : "com.arialyy.aria.core.upload", - entity.isDownlaod ? "DownloadTask" : "UploadTask"); - builder.superclass(ParameterizedTypeName.get(superClass, typeVariableName)); - builder.addMethod(listener); - return builder.build(); - } - - void clean() { - mMethods.clear(); - } - - /** - * 查找并保存扫描到的方法 - */ - private void saveMethod(boolean isDownload, RoundEnvironment roundEnv, - Class annotationClazz, int annotationType) { - for (Element element : roundEnv.getElementsAnnotatedWith(annotationClazz)) { - ElementKind kind = element.getKind(); - if (kind == ElementKind.METHOD) { - ExecutableElement method = (ExecutableElement) element; - TypeElement classElement = (TypeElement) method.getEnclosingElement(); - PackageElement packageElement = mElementUtil.getPackageOf(classElement); - checkDownloadMethod(isDownload, method); - String methodName = method.getSimpleName().toString(); - String className = method.getEnclosingElement().toString(); //全类名 - ProxyEntity proxyEntity = mMethods.get(className); - if (proxyEntity == null) { - proxyEntity = new ProxyEntity(); - proxyEntity.isDownlaod = isDownload; - proxyEntity.packageName = packageElement.getQualifiedName().toString(); - proxyEntity.className = classElement.getSimpleName().toString(); - mMethods.put(className, proxyEntity); - } - proxyEntity.methods.put(annotationClazz, methodName); - proxyEntity.keyMappings.put(methodName, getValues(method, isDownload, annotationType)); - } - } - } - - /** - * 获取注解的内容 - */ - private Set getValues(ExecutableElement method, boolean isDownload, int annotationType) { - String[] keys = null; - if (isDownload) { - switch (annotationType) { - case ProxyConstance.DOWNLOAD_PRE: - keys = method.getAnnotation(Download.onPre.class).value(); - break; - case ProxyConstance.DOWNLOAD_TASK_PRE: - keys = method.getAnnotation(Download.onTaskPre.class).value(); - break; - case ProxyConstance.DOWNLOAD_TASK_RESUME: - keys = method.getAnnotation(Download.onTaskResume.class).value(); - break; - case ProxyConstance.DOWNLOAD_TASK_START: - keys = method.getAnnotation(Download.onTaskStart.class).value(); - break; - case ProxyConstance.DOWNLOAD_TASK_RUNNING: - keys = method.getAnnotation(Download.onTaskRunning.class).value(); - break; - case ProxyConstance.DOWNLOAD_TASK_STOP: - keys = method.getAnnotation(Download.onTaskStop.class).value(); - break; - case ProxyConstance.DOWNLOAD_TASK_COMPLETE: - keys = method.getAnnotation(Download.onTaskComplete.class).value(); - break; - case ProxyConstance.DOWNLOAD_TASK_CANCEL: - keys = method.getAnnotation(Download.onTaskCancel.class).value(); - break; - case ProxyConstance.DOWNLOAD_TASK_FAIL: - keys = method.getAnnotation(Download.onTaskFail.class).value(); - break; - case ProxyConstance.DOWNLOAD_TASK_NO_SUPPORT_BREAKPOINT: - keys = method.getAnnotation(Download.onNoSupportBreakPoint.class).value(); - break; - } - } else { - switch (annotationType) { - case ProxyConstance.UPLOAD_PRE: - keys = method.getAnnotation(Upload.onPre.class).value(); - break; - case ProxyConstance.UPLOAD_TASK_PRE: - keys = method.getAnnotation(Upload.onTaskPre.class).value(); - break; - case ProxyConstance.UPLOAD_TASK_RESUME: - //keys = method.getAnnotation(Upload.onTaskResume.class).value(); - break; - case ProxyConstance.UPLOAD_TASK_START: - keys = method.getAnnotation(Upload.onTaskStart.class).value(); - break; - case ProxyConstance.UPLOAD_TASK_RUNNING: - keys = method.getAnnotation(Upload.onTaskRunning.class).value(); - break; - case ProxyConstance.UPLOAD_TASK_STOP: - keys = method.getAnnotation(Upload.onTaskStop.class).value(); - break; - case ProxyConstance.UPLOAD_TASK_COMPLETE: - keys = method.getAnnotation(Upload.onTaskComplete.class).value(); - break; - case ProxyConstance.UPLOAD_TASK_CANCEL: - keys = method.getAnnotation(Upload.onTaskCancel.class).value(); - break; - case ProxyConstance.UPLOAD_TASK_FAIL: - keys = method.getAnnotation(Upload.onTaskFail.class).value(); - break; - case ProxyConstance.UPLOAD_TASK_NO_SUPPORT_BREAKPOINT: - keys = method.getAnnotation(Upload.onNoSupportBreakPoint.class).value(); - break; - } - } - - return keys == null ? null : convertSet(keys); - } - - /** - * 检查和下载相关的方法,如果被注解的方法为private或参数不合法,则抛异常 - */ - private void checkDownloadMethod(boolean isDownload, ExecutableElement method) { - String methodName = method.getSimpleName().toString(); - String className = method.getEnclosingElement().toString(); - Set modifiers = method.getModifiers(); - if (modifiers.contains(Modifier.PRIVATE)) { - throw new IllegalAccessError(className + "." + methodName + "不能为private方法"); - } - List params = (List) method.getParameters(); - if (params.size() > 1) { - throw new IllegalArgumentException( - className + "." + methodName + "参数错误, 参数只有一个,且参数必须是" + getCheckParams(isDownload)); - } - if (!params.get(0).asType().toString().equals(getCheckParams(isDownload))) { - throw new IllegalArgumentException(className - + "." - + methodName - + "参数【" - + params.get(0).getSimpleName() - + "】类型错误,参数必须是" - + getCheckParams(isDownload)); - } - } - - /** - * 字符串数组转set - * - * @param keys 注解中查到的key - */ - private Set convertSet(final String[] keys) { - if (keys == null || keys.length == 0) { - return null; - } - if (keys[0].isEmpty()) return null; - Set set = new HashSet<>(); - Collections.addAll(set, keys); - return set; - } - - private String getCheckParams(boolean isDownload) { - return isDownload ? "com.arialyy.aria.core.download.DownloadTask" - : "com.arialyy.aria.core.upload.UploadTask"; - } -} diff --git a/AriaCompiler/src/main/java/com/arialyy/compiler/ElementHandler.java b/AriaCompiler/src/main/java/com/arialyy/compiler/ElementHandler.java new file mode 100644 index 00000000..5cbdd005 --- /dev/null +++ b/AriaCompiler/src/main/java/com/arialyy/compiler/ElementHandler.java @@ -0,0 +1,601 @@ +/* + * 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.compiler; + +import com.arialyy.annotations.Download; +import com.arialyy.annotations.DownloadGroup; +import com.arialyy.annotations.Upload; +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.CodeBlock; +import com.squareup.javapoet.FieldSpec; +import com.squareup.javapoet.JavaFile; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.ParameterSpec; +import com.squareup.javapoet.TypeSpec; +import com.squareup.javapoet.ParameterizedTypeName; +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.annotation.processing.Filer; +import javax.annotation.processing.RoundEnvironment; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.util.Elements; + +/** + * Created by lyy on 2017/6/6. + * 元素处理 + */ +class ElementHandler { + private static final boolean DEBUG = false; + + private Filer mFiler; + private Elements mElementUtil; + private Map mMethods = new HashMap<>(); + private Map> mListenerClass = new HashMap<>(); + + ElementHandler(Filer filer, Elements elements) { + mFiler = filer; + mElementUtil = elements; + } + + /** + * VariableElement 一般代表成员变量 + * ExecutableElement 一般代表类中的方法 + * TypeElement 一般代表代表类 + * PackageElement 一般代表Package + */ + void handleDownload(RoundEnvironment roundEnv) { + saveMethod(TaskEnum.DOWNLOAD, roundEnv, Download.onNoSupportBreakPoint.class, + ProxyConstance.TASK_NO_SUPPORT_BREAKPOINT); + saveMethod(TaskEnum.DOWNLOAD, roundEnv, Download.onPre.class, ProxyConstance.PRE); + saveMethod(TaskEnum.DOWNLOAD, roundEnv, Download.onTaskCancel.class, + ProxyConstance.TASK_CANCEL); + saveMethod(TaskEnum.DOWNLOAD, roundEnv, Download.onTaskComplete.class, + ProxyConstance.TASK_COMPLETE); + saveMethod(TaskEnum.DOWNLOAD, roundEnv, Download.onTaskFail.class, ProxyConstance.TASK_FAIL); + saveMethod(TaskEnum.DOWNLOAD, roundEnv, Download.onTaskPre.class, ProxyConstance.TASK_PRE); + saveMethod(TaskEnum.DOWNLOAD, roundEnv, Download.onTaskResume.class, + ProxyConstance.TASK_RESUME); + saveMethod(TaskEnum.DOWNLOAD, roundEnv, Download.onTaskRunning.class, + ProxyConstance.TASK_RUNNING); + saveMethod(TaskEnum.DOWNLOAD, roundEnv, Download.onTaskStart.class, ProxyConstance.TASK_START); + saveMethod(TaskEnum.DOWNLOAD, roundEnv, Download.onTaskStop.class, ProxyConstance.TASK_STOP); + } + + /** + * 处理搜索到的下载任务组注解 + */ + void handleDownloadGroup(RoundEnvironment roundEnv) { + saveMethod(TaskEnum.DOWNLOAD_GROUP, roundEnv, DownloadGroup.onPre.class, ProxyConstance.PRE); + saveMethod(TaskEnum.DOWNLOAD_GROUP, roundEnv, DownloadGroup.onTaskCancel.class, + ProxyConstance.TASK_CANCEL); + saveMethod(TaskEnum.DOWNLOAD_GROUP, roundEnv, DownloadGroup.onTaskComplete.class, + ProxyConstance.TASK_COMPLETE); + saveMethod(TaskEnum.DOWNLOAD_GROUP, roundEnv, DownloadGroup.onTaskFail.class, + ProxyConstance.TASK_FAIL); + saveMethod(TaskEnum.DOWNLOAD_GROUP, roundEnv, DownloadGroup.onTaskPre.class, + ProxyConstance.TASK_PRE); + saveMethod(TaskEnum.DOWNLOAD_GROUP, roundEnv, DownloadGroup.onTaskResume.class, + ProxyConstance.TASK_RESUME); + saveMethod(TaskEnum.DOWNLOAD_GROUP, roundEnv, DownloadGroup.onTaskRunning.class, + ProxyConstance.TASK_RUNNING); + saveMethod(TaskEnum.DOWNLOAD_GROUP, roundEnv, DownloadGroup.onTaskStart.class, + ProxyConstance.TASK_START); + saveMethod(TaskEnum.DOWNLOAD_GROUP, roundEnv, DownloadGroup.onTaskStop.class, + ProxyConstance.TASK_STOP); + } + + /** + * 处理搜索到的上传注解F + */ + void handleUpload(RoundEnvironment roundEnv) { + saveMethod(TaskEnum.UPLOAD, roundEnv, Upload.onNoSupportBreakPoint.class, + ProxyConstance.TASK_NO_SUPPORT_BREAKPOINT); + saveMethod(TaskEnum.UPLOAD, roundEnv, Upload.onPre.class, ProxyConstance.PRE); + saveMethod(TaskEnum.UPLOAD, roundEnv, Upload.onTaskCancel.class, ProxyConstance.TASK_CANCEL); + saveMethod(TaskEnum.UPLOAD, roundEnv, Upload.onTaskComplete.class, + ProxyConstance.TASK_COMPLETE); + saveMethod(TaskEnum.UPLOAD, roundEnv, Upload.onTaskFail.class, ProxyConstance.TASK_FAIL); + saveMethod(TaskEnum.UPLOAD, roundEnv, Upload.onTaskPre.class, ProxyConstance.TASK_PRE); + //saveMethod(false, roundEnv, Upload.onTaskResume.class); + saveMethod(TaskEnum.UPLOAD, roundEnv, Upload.onTaskRunning.class, ProxyConstance.TASK_RUNNING); + saveMethod(TaskEnum.UPLOAD, roundEnv, Upload.onTaskStart.class, ProxyConstance.TASK_START); + saveMethod(TaskEnum.UPLOAD, roundEnv, Upload.onTaskStop.class, ProxyConstance.TASK_STOP); + } + + /** + * 在build文件夹中生成如下代码的文件 + *
+   *   
+   * package com.arialyy.simple.download;
+   *
+   * import com.arialyy.aria.core.download.DownloadTask;
+   * import com.arialyy.aria.core.scheduler.AbsSchedulerListener;
+   *
+   * public final class SingleTaskActivity$$DownloadListenerProxy extends
+   * AbsSchedulerListener {
+   * private SingleTaskActivity obj;
+   *
+   *    public void onPre(final DownloadTask task) {
+   *      obj.onPre((DownloadTask)task);
+   *    }
+   *
+   *    public void onTaskStart(final DownloadTask task) {
+   *      obj.onStart((DownloadTask)task);
+   *    }
+   *
+   *    public void setListener(final Object obj) {
+   *      this.obj = (SingleTaskActivity)obj;
+   *    }
+   * }
+   *   
+   * 
+ */ + void createProxyFile() { + try { + createProxyListenerFile(); + createProxyClassFile(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * 创建事件代理文件 + */ + private void createProxyListenerFile() throws IOException { + Set keys = mMethods.keySet(); + for (String key : keys) { + ProxyMethodParam entity = mMethods.get(key); + for (TaskEnum taskEnum : entity.taskEnums) { + JavaFile jf = + JavaFile.builder(entity.packageName, createProxyClass(entity, taskEnum)).build(); + createFile(jf); + } + } + } + + private void createFile(JavaFile jf) throws IOException { + if (DEBUG) { + // 如果需要在控制台打印生成的文件,则去掉下面的注释 + jf.writeTo(System.out); + } else { + jf.writeTo(mFiler); + } + } + + /** + * 每一种注解对应的类集合 + */ + private void createProxyClassFile() throws IOException { + Set keys = mListenerClass.keySet(); + TypeSpec.Builder builder = TypeSpec.classBuilder(ProxyConstance.PROXY_COUNTER_NAME) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL); + + FieldSpec mappingField = FieldSpec.builder( + ParameterizedTypeName.get(ClassName.get(Map.class), ClassName.get(String.class), + ParameterizedTypeName.get(ClassName.get(Set.class), ClassName.get(String.class))), + ProxyConstance.PROXY_COUNTER_MAP) + .addModifiers(Modifier.PRIVATE) + .initializer("new $T()", HashMap.class) + .build(); + builder.addField(mappingField); + + //增加构造函数 + CodeBlock.Builder cb = CodeBlock.builder(); + cb.add("Set set = null;\n"); + for (String key : keys) { + addTypeData(key, mListenerClass.get(key), cb); + } + MethodSpec structure = + MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC).addCode(cb.build()).build(); + builder.addMethod(structure); + + builder.addMethod( + creatMethod(ProxyConstance.COUNT_METHOD_DOWNLOAD, ProxyConstance.COUNT_DOWNLOAD)); + builder.addMethod(creatMethod(ProxyConstance.COUNT_METHOD_UPLOAD, ProxyConstance.COUNT_UPLOAD)); + builder.addMethod(creatMethod(ProxyConstance.COUNT_METHOD_DOWNLOAD_GROUP, + ProxyConstance.COUNT_DOWNLOAD_GROUP)); + + JavaFile jf = JavaFile.builder(ProxyConstance.PROXY_COUNTER_PACKAGE, builder.build()).build(); + createFile(jf); + } + + /** + * 创建不同任务类型的代理类集合 + * + * @param key {@link #addListenerMapping(String, String)} + */ + private MethodSpec creatMethod(String methodName, String key) { + MethodSpec.Builder builder = MethodSpec.methodBuilder(methodName); + ParameterizedTypeName returnName = + ParameterizedTypeName.get(ClassName.get(Set.class), ClassName.get(String.class)); + builder.addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .returns(returnName) + .addCode("return " + ProxyConstance.PROXY_COUNTER_MAP + ".get(\"" + key + "\");\n"); + return builder.build(); + } + + /** + * 添加每一种注解对应类 + * + * @param type {@link #addListenerMapping(String, String)} + */ + private void addTypeData(String type, Set clsNames, CodeBlock.Builder cb) { + if (clsNames == null || clsNames.isEmpty()) return; + StringBuilder sb = new StringBuilder(); + sb.append("set = new $T();\n"); + for (String clsName : clsNames) { + sb.append("set.add(\"").append(clsName).append("\");\n"); + } + sb.append("typeMapping.put(\"").append(type).append("\", ").append("set);\n"); + cb.add(sb.toString(), ClassName.get(HashSet.class)); + } + + /** + * 创建代理方法 + * + * @param taskEnum 任务类型枚举{@link TaskEnum} + * @param annotation {@link Download}、{@link Upload} + * @param methodName 被代理类注解的方法名 + */ + private MethodSpec createProxyMethod(TaskEnum taskEnum, Class annotation, + String methodName) { + ClassName task = ClassName.get(taskEnum.getPkg(), taskEnum.getClassName()); + + ParameterSpec parameterSpec = + ParameterSpec.builder(task, "task").addModifiers(Modifier.FINAL).build(); + StringBuilder sb = new StringBuilder(); + sb.append("Set keys = keyMapping.get(\"").append(methodName).append("\");\n"); + sb.append("if (keys != null) {\n\tif (keys.contains(task.getKey())) {\n") + .append("\t\tobj.") + .append(methodName) + .append("((") + .append(taskEnum.getClassName()) + .append(")task);\n") + .append("\t}\n} else {\n") + .append("\tobj.") + .append(methodName) + .append("((") + .append(taskEnum.getClassName()) + .append(")task);\n}\n"); + + return MethodSpec.methodBuilder(annotation.getSimpleName()) + .addModifiers(Modifier.PUBLIC) + .returns(void.class) + .addParameter(parameterSpec) + .addAnnotation(Override.class) + .addCode(sb.toString()) + .build(); + } + + /** + * 创建代理类 + */ + private TypeSpec createProxyClass(ProxyMethodParam entity, TaskEnum taskEnum) { + TypeSpec.Builder builder = TypeSpec.classBuilder(entity.className + taskEnum.getProxySuffix()) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL); + + //添加被代理的类的字段 + ClassName obj = ClassName.get(entity.packageName, entity.className); + FieldSpec observerField = FieldSpec.builder(obj, "obj").addModifiers(Modifier.PRIVATE).build(); + builder.addField(observerField); + + //添加url映射表 + FieldSpec mappingField = FieldSpec.builder( + ParameterizedTypeName.get(ClassName.get(Map.class), ClassName.get(String.class), + ParameterizedTypeName.get(ClassName.get(Set.class), ClassName.get(String.class))), + "keyMapping").addModifiers(Modifier.PRIVATE).initializer("new $T()", HashMap.class).build(); + builder.addField(mappingField); + + //添加注解方法 + Map, String> temp = entity.methods.get(taskEnum); + if (temp != null) { + for (Class annotation : temp.keySet()) { + MethodSpec method = createProxyMethod(taskEnum, annotation, temp.get(annotation)); + builder.addMethod(method); + } + } + + //增加构造函数 + CodeBlock.Builder cb = CodeBlock.builder(); + cb.add("Set set = null;\n"); + for (String methodName : entity.keyMappings.keySet()) { + Set keys = entity.keyMappings.get(methodName); + if (keys == null || keys.size() == 0) continue; + StringBuilder sb = new StringBuilder(); + sb.append("set = new $T();\n"); + for (String key : keys) { + if (key.isEmpty()) continue; + sb.append("set.add(\"").append(key).append("\");\n"); + } + + sb.append("keyMapping.put(\"").append(methodName).append("\", ").append("set);\n"); + cb.add(sb.toString(), ClassName.get(HashSet.class)); + } + MethodSpec structure = + MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC).addCode(cb.build()).build(); + builder.addMethod(structure); + + //添加设置代理的类 + ParameterSpec parameterSpec = + ParameterSpec.builder(Object.class, "obj").addModifiers(Modifier.FINAL).build(); + MethodSpec listener = MethodSpec.methodBuilder(ProxyConstance.SET_LISTENER) + .addModifiers(Modifier.PUBLIC) + .returns(void.class) + .addParameter(parameterSpec) + .addAnnotation(Override.class) + .addCode("this.obj = (" + entity.className + ")obj;\n") + .build(); + builder.addJavadoc("该文件为Aria自动生成的代理文件,请不要修改该文件的任何代码!\n"); + + //创建父类参数 + ClassName superClass = ClassName.get("com.arialyy.aria.core.scheduler", "AbsSchedulerListener"); + //创建泛型 + ClassName typeVariableName = ClassName.get(taskEnum.getPkg(), taskEnum.getClassName()); + builder.superclass(ParameterizedTypeName.get(superClass, typeVariableName)); + builder.addMethod(listener); + return builder.build(); + } + + void clean() { + mMethods.clear(); + } + + /** + * 查找并保存扫描到的方法 + */ + private void saveMethod(TaskEnum taskEnum, RoundEnvironment roundEnv, + Class annotationClazz, int annotationType) { + for (Element element : roundEnv.getElementsAnnotatedWith(annotationClazz)) { + ElementKind kind = element.getKind(); + if (kind == ElementKind.METHOD) { + ExecutableElement method = (ExecutableElement) element; + TypeElement classElement = (TypeElement) method.getEnclosingElement(); + PackageElement packageElement = mElementUtil.getPackageOf(classElement); + checkDownloadMethod(taskEnum, method); + String methodName = method.getSimpleName().toString(); + String className = method.getEnclosingElement().toString(); //全类名 + ProxyMethodParam proxyEntity = mMethods.get(className); + if (proxyEntity == null) { + proxyEntity = new ProxyMethodParam(); + proxyEntity.taskEnums = new HashSet<>(); + proxyEntity.packageName = packageElement.getQualifiedName().toString(); + proxyEntity.className = classElement.getSimpleName().toString(); + mMethods.put(className, proxyEntity); + } + proxyEntity.taskEnums.add(taskEnum); + if (proxyEntity.methods.get(taskEnum) == null) { + proxyEntity.methods.put(taskEnum, new HashMap, String>()); + } + proxyEntity.methods.get(taskEnum).put(annotationClazz, methodName); + proxyEntity.keyMappings.put(methodName, getValues(taskEnum, method, annotationType)); + } + } + } + + /** + * 获取注解的内容 + */ + private Set getValues(TaskEnum taskEnum, ExecutableElement method, int annotationType) { + String clsName = method.getEnclosingElement().toString(); + String[] keys = null; + switch (taskEnum) { + case DOWNLOAD: + keys = getDownloadValues(method, annotationType); + addListenerMapping(clsName, ProxyConstance.COUNT_DOWNLOAD); + break; + case UPLOAD: + keys = getUploadValues(method, annotationType); + addListenerMapping(clsName, ProxyConstance.COUNT_UPLOAD); + break; + case DOWNLOAD_GROUP: + keys = getDownloadGroupValues(method, annotationType); + addListenerMapping(clsName, ProxyConstance.COUNT_DOWNLOAD_GROUP); + break; + } + return keys == null ? null : convertSet(keys); + } + + /** + * 添加方法映射 + * + * @param clsName 注解事件的类 + * @param key {@link ProxyConstance#COUNT_DOWNLOAD}、{@link ProxyConstance#COUNT_UPLOAD}、{@link + * ProxyConstance#COUNT_DOWNLOAD_GROUP} + */ + private void addListenerMapping(String clsName, String key) { + Set cls = mListenerClass.get(key); + if (cls == null) { + cls = new HashSet<>(); + mListenerClass.put(key, cls); + } + cls.add(clsName); + } + + /** + * 获取下载任务组的注解数据 + */ + private String[] getDownloadGroupValues(ExecutableElement method, int annotationType) { + String[] values = null; + switch (annotationType) { + case ProxyConstance.PRE: + values = method.getAnnotation(DownloadGroup.onPre.class).value(); + break; + case ProxyConstance.TASK_PRE: + values = method.getAnnotation(DownloadGroup.onTaskPre.class).value(); + break; + case ProxyConstance.TASK_RESUME: + values = method.getAnnotation(DownloadGroup.onTaskResume.class).value(); + break; + case ProxyConstance.TASK_START: + values = method.getAnnotation(DownloadGroup.onTaskStart.class).value(); + break; + case ProxyConstance.TASK_RUNNING: + values = method.getAnnotation(DownloadGroup.onTaskRunning.class).value(); + break; + case ProxyConstance.TASK_STOP: + values = method.getAnnotation(DownloadGroup.onTaskStop.class).value(); + break; + case ProxyConstance.TASK_COMPLETE: + values = method.getAnnotation(DownloadGroup.onTaskComplete.class).value(); + break; + case ProxyConstance.TASK_CANCEL: + values = method.getAnnotation(DownloadGroup.onTaskCancel.class).value(); + break; + case ProxyConstance.TASK_FAIL: + values = method.getAnnotation(DownloadGroup.onTaskFail.class).value(); + break; + } + return values; + } + + /** + * 获取上传的注解数据 + */ + private String[] getUploadValues(ExecutableElement method, int annotationType) { + String[] values = null; + switch (annotationType) { + case ProxyConstance.PRE: + values = method.getAnnotation(Upload.onPre.class).value(); + break; + case ProxyConstance.TASK_PRE: + values = method.getAnnotation(Upload.onTaskPre.class).value(); + break; + case ProxyConstance.TASK_RESUME: + //values = method.getAnnotation(Upload.onTaskResume.class).value(); + break; + case ProxyConstance.TASK_START: + values = method.getAnnotation(Upload.onTaskStart.class).value(); + break; + case ProxyConstance.TASK_RUNNING: + values = method.getAnnotation(Upload.onTaskRunning.class).value(); + break; + case ProxyConstance.TASK_STOP: + values = method.getAnnotation(Upload.onTaskStop.class).value(); + break; + case ProxyConstance.TASK_COMPLETE: + values = method.getAnnotation(Upload.onTaskComplete.class).value(); + break; + case ProxyConstance.TASK_CANCEL: + values = method.getAnnotation(Upload.onTaskCancel.class).value(); + break; + case ProxyConstance.TASK_FAIL: + values = method.getAnnotation(Upload.onTaskFail.class).value(); + break; + case ProxyConstance.TASK_NO_SUPPORT_BREAKPOINT: + //values = method.getAnnotation(Upload.onNoSupportBreakPoint.class).value(); + break; + } + return values; + } + + /** + * 获取下载的注解数据 + */ + private String[] getDownloadValues(ExecutableElement method, int annotationType) { + String[] values = null; + switch (annotationType) { + case ProxyConstance.PRE: + values = method.getAnnotation(Download.onPre.class).value(); + break; + case ProxyConstance.TASK_PRE: + values = method.getAnnotation(Download.onTaskPre.class).value(); + break; + case ProxyConstance.TASK_RESUME: + values = method.getAnnotation(Download.onTaskResume.class).value(); + break; + case ProxyConstance.TASK_START: + values = method.getAnnotation(Download.onTaskStart.class).value(); + break; + case ProxyConstance.TASK_RUNNING: + values = method.getAnnotation(Download.onTaskRunning.class).value(); + break; + case ProxyConstance.TASK_STOP: + values = method.getAnnotation(Download.onTaskStop.class).value(); + break; + case ProxyConstance.TASK_COMPLETE: + values = method.getAnnotation(Download.onTaskComplete.class).value(); + break; + case ProxyConstance.TASK_CANCEL: + values = method.getAnnotation(Download.onTaskCancel.class).value(); + break; + case ProxyConstance.TASK_FAIL: + values = method.getAnnotation(Download.onTaskFail.class).value(); + break; + case ProxyConstance.TASK_NO_SUPPORT_BREAKPOINT: + values = method.getAnnotation(Download.onNoSupportBreakPoint.class).value(); + break; + } + return values; + } + + /** + * 检查和下载相关的方法,如果被注解的方法为private或参数不合法,则抛异常 + */ + private void checkDownloadMethod(TaskEnum taskEnum, ExecutableElement method) { + String methodName = method.getSimpleName().toString(); + String className = method.getEnclosingElement().toString(); + Set modifiers = method.getModifiers(); + if (modifiers.contains(Modifier.PRIVATE)) { + throw new IllegalAccessError(className + "." + methodName + "不能为private方法"); + } + List params = (List) method.getParameters(); + if (params.size() > 1) { + throw new IllegalArgumentException( + className + "." + methodName + "参数错误, 参数只有一个,且参数必须是" + getCheckParams(taskEnum)); + } + if (!params.get(0).asType().toString().equals(getCheckParams(taskEnum))) { + throw new IllegalArgumentException(className + + "." + + methodName + + "参数【" + + params.get(0).getSimpleName() + + "】类型错误,参数必须是" + + getCheckParams(taskEnum)); + } + } + + /** + * 字符串数组转set + * + * @param keys 注解中查到的key + */ + private Set convertSet(final String[] keys) { + if (keys == null || keys.length == 0) { + return null; + } + if (keys[0].isEmpty()) return null; + Set set = new HashSet<>(); + Collections.addAll(set, keys); + return set; + } + + private String getCheckParams(TaskEnum taskEnum) { + return taskEnum.pkg + "." + taskEnum.getClassName(); + } +} diff --git a/AriaCompiler/src/main/java/com/arialyy/compiler/PrintLog.java b/AriaCompiler/src/main/java/com/arialyy/compiler/PrintLog.java index 1350de4b..161a2214 100644 --- a/AriaCompiler/src/main/java/com/arialyy/compiler/PrintLog.java +++ b/AriaCompiler/src/main/java/com/arialyy/compiler/PrintLog.java @@ -22,7 +22,6 @@ import javax.tools.Diagnostic; /** * Created by Aria.Lao on 2017/6/6. */ - class PrintLog { private volatile static PrintLog INSTANCE = null; diff --git a/AriaCompiler/src/main/java/com/arialyy/compiler/ProxyConstance.java b/AriaCompiler/src/main/java/com/arialyy/compiler/ProxyConstance.java index 6ea65c33..98fcb980 100644 --- a/AriaCompiler/src/main/java/com/arialyy/compiler/ProxyConstance.java +++ b/AriaCompiler/src/main/java/com/arialyy/compiler/ProxyConstance.java @@ -17,43 +17,44 @@ package com.arialyy.compiler; /** * Created by lyy on 2017/6/7. + * 扫描器常量 */ - -public interface ProxyConstance { +interface ProxyConstance { /** * 设置观察者的方法 */ String SET_LISTENER = "setListener"; /** - * 下载的动态生成的代理类后缀 + * 代理配置类 + */ + String PROXY_COUNTER_PACKAGE = "com.arialyy.aria"; + /** + * 代理分类统计 */ - String DOWNLOAD_PROXY_CLASS_SUFFIX = "$$DownloadListenerProxy"; + String PROXY_COUNTER_NAME = "ProxyClassCounter"; /** - * 上传的动态生成的代理类后缀 + * 代理分类统计映射表 */ - String UPLOAD_PROXY_CLASS_SUFFIX = "$$UploadListenerProxy"; + String PROXY_COUNTER_MAP = "typeMapping"; + + String COUNT_DOWNLOAD = "download"; + String COUNT_DOWNLOAD_GROUP = "downloadGroup"; + String COUNT_UPLOAD = "upload"; - int DOWNLOAD_PRE = 0X11; - int DOWNLOAD_TASK_PRE = 0X12; - int DOWNLOAD_TASK_RESUME = 0X13; - int DOWNLOAD_TASK_START = 0X14; - int DOWNLOAD_TASK_STOP = 0X15; - int DOWNLOAD_TASK_CANCEL = 0X16; - int DOWNLOAD_TASK_FAIL = 0X17; - int DOWNLOAD_TASK_COMPLETE = 0X18; - int DOWNLOAD_TASK_RUNNING = 0X19; - int DOWNLOAD_TASK_NO_SUPPORT_BREAKPOINT = 0X1A; + String COUNT_METHOD_DOWNLOAD = "getDownloadCounter"; + String COUNT_METHOD_DOWNLOAD_GROUP = "getDownloadGroupCounter"; + String COUNT_METHOD_UPLOAD = "getUploadCounter"; - int UPLOAD_PRE = 0X11; - int UPLOAD_TASK_PRE = 0X12; - int UPLOAD_TASK_RESUME = 0X13; - int UPLOAD_TASK_START = 0X14; - int UPLOAD_TASK_STOP = 0X15; - int UPLOAD_TASK_CANCEL = 0X16; - int UPLOAD_TASK_FAIL = 0X17; - int UPLOAD_TASK_COMPLETE = 0X18; - int UPLOAD_TASK_RUNNING = 0X19; - int UPLOAD_TASK_NO_SUPPORT_BREAKPOINT = 0X1A; + int PRE = 0X11; + int TASK_PRE = 0X12; + int TASK_RESUME = 0X13; + int TASK_START = 0X14; + int TASK_STOP = 0X15; + int TASK_CANCEL = 0X16; + int TASK_FAIL = 0X17; + int TASK_COMPLETE = 0X18; + int TASK_RUNNING = 0X19; + int TASK_NO_SUPPORT_BREAKPOINT = 0X1A; } diff --git a/AriaCompiler/src/main/java/com/arialyy/compiler/ProxyEntity.java b/AriaCompiler/src/main/java/com/arialyy/compiler/ProxyMethodParam.java similarity index 75% rename from AriaCompiler/src/main/java/com/arialyy/compiler/ProxyEntity.java rename to AriaCompiler/src/main/java/com/arialyy/compiler/ProxyMethodParam.java index 41573cfc..3cc813aa 100644 --- a/AriaCompiler/src/main/java/com/arialyy/compiler/ProxyEntity.java +++ b/AriaCompiler/src/main/java/com/arialyy/compiler/ProxyMethodParam.java @@ -22,12 +22,12 @@ import java.util.Set; /** * Created by Aria.Lao on 2017/6/7. + * 创建代理方法的参数 */ - -class ProxyEntity { - public String packageName; - public String className; - public boolean isDownlaod = true; - public Map> keyMappings = new HashMap<>(); - public Map, String> methods = new HashMap<>(); +class ProxyMethodParam { + String packageName; + String className; + Set taskEnums; + Map> keyMappings = new HashMap<>(); + Map, String>> methods = new HashMap<>(); } diff --git a/AriaCompiler/src/main/java/com/arialyy/compiler/TaskEnum.java b/AriaCompiler/src/main/java/com/arialyy/compiler/TaskEnum.java new file mode 100644 index 00000000..7620f1df --- /dev/null +++ b/AriaCompiler/src/main/java/com/arialyy/compiler/TaskEnum.java @@ -0,0 +1,53 @@ +/* + * 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.compiler; + +/** + * Created by Aria.Lao on 2017/7/10. + * 任务类型枚举 + */ +enum TaskEnum { + DOWNLOAD("com.arialyy.aria.core.download", "DownloadTask", + "$$DownloadListenerProxy"), DOWNLOAD_GROUP("com.arialyy.aria.core.download", + "DownloadGroupTask", "$$DownloadGroupListenerProxy"), UPLOAD("com.arialyy.aria.core.upload", + "UploadTask", "$$UploadListenerProxy"), UPLOAD_GROUP("com.arialyy.aria.core.upload", + "UploadGroupTask", "$$UploadGroupListenerProxy"); + + String pkg, className, proxySuffix; + + public String getClassName() { + return className; + } + + public String getProxySuffix() { + return proxySuffix; + } + + public String getPkg() { + return pkg; + } + + /** + * @param pkg 包名 + * @param className 任务完整类名 + * @param proxySuffix 事件代理后缀 + */ + TaskEnum(String pkg, String className, String proxySuffix) { + this.pkg = pkg; + this.className = className; + this.proxySuffix = proxySuffix; + } +} diff --git a/README.md b/README.md index e2776f14..1e8af13e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Aria ![图标](https://github.com/AriaLyy/DownloadUtil/blob/v_2.0/app/src/main/res/mipmap-hdpi/ic_launcher.png)
## [ENGLISH DOC](https://github.com/AriaLyy/Aria/blob/master/ENGLISH_README.md)
-Aria项目源于15年工作中遇到的一个文件下载管理的需求,当时被下载折磨的痛不欲生,从那时起便萌生了编写一个简单易用,稳当高效的下载框架,aria经历了1.0到3.0的开发,算是越来越接近当初所制定的目标了。 +Aria项目源于工作中遇到的一个文件下载管理的需求,当时被下载折磨的痛不欲生,从那时起便萌生了编写一个简单易用,稳当高效的下载框架,aria经历了1.0到3.0的开发,算是越来越接近当初所制定的目标了。 Aria有以下特点: + 简单、方便 @@ -13,7 +13,7 @@ Aria有以下特点: - [一句代码实现速度限制](#常用接口) - [通过修改配置文件很容易就能修改下载线程数](#配置文件设置参数) - [优先下载某一个任务](#常用接口) - + - [支持任务组下载,多个任务可共享一个progress](#一组任务下载) + 支持https地址下载 - 在配置文件中很容易就可以设置CA证书的信息 + 支持300、301、302重定向下载链接下载 @@ -30,13 +30,14 @@ Aria怎样使用? [![Download](https://api.bintray.com/packages/arialyy/maven/AriaApi/images/download.svg)](https://bintray.com/arialyy/maven/AriaApi/_latestVersion) [![Download](https://api.bintray.com/packages/arialyy/maven/AriaCompiler/images/download.svg)](https://bintray.com/arialyy/maven/AriaCompiler/_latestVersion) ```java -compile 'com.arialyy.aria:aria-core:3.2.0' -annotationProcessor 'com.arialyy.aria:aria-compiler:3.2.0' +compile 'com.arialyy.aria:aria-core:3.2.6' +annotationProcessor 'com.arialyy.aria:aria-compiler:3.2.6' ``` ## 示例 ![多任务下载](https://github.com/AriaLyy/DownloadUtil/blob/master/img/download_img.gif) ![网速下载限制](https://github.com/AriaLyy/DownloadUtil/blob/master/img/max_speed.gif) +![下载任务组](https://github.com/AriaLyy/DownloadUtil/blob/master/img/download_group.gif) ## 性能 ![性能展示](https://github.com/AriaLyy/DownloadUtil/blob/master/img/performance.png) @@ -51,15 +52,8 @@ annotationProcessor 'com.arialyy.aria:aria-compiler:3.2.0' ``` ## 使用Aria进行下载 -* 添加任务(不进行下载),当其他下载任务完成时,将自动下载等待中的任务 - ```java - Aria.download(this) - .load(DOWNLOAD_URL) - .setDownloadPath(DOWNLOAD_PATH) //文件保存路径 - .add(); - ``` - -* 下载 +### 普通任务下载 +* 下载\恢复下载 ```java Aria.download(this) @@ -72,15 +66,33 @@ annotationProcessor 'com.arialyy.aria:aria-compiler:3.2.0' ```java Aria.download(this).load(DOWNLOAD_URL).pause(); ``` -* 恢复下载 + +* 取消下载 ```java - Aria.download(this).load(DOWNLOAD_URL).resume(); + Aria.download(this).load(DOWNLOAD_URL).cancel(); ``` +### 一组任务下载 +任务组的下载和普通任务的下载基本上差不多,区别在于,任务组下载不需要对每一个子任务设置保存路径,**但是需要设置任务组保存文件夹路径,所有子任务都保存在该文件夹下** + +* 下载\恢复下载 + + ```java + Aria.download(this) + .load(urls) //设置一主任务,参数为List + .setDownloadDirPath(groupDirPath) //设置任务组的文件夹路径 + .start(); //启动下载 + ``` +* 暂停 + + ```java + Aria.download(this).load(urls).pause(); + ``` + * 取消下载 ```java - Aria.download(this).load(DOWNLOAD_URL).cancel(); + Aria.download(this).load(urls).cancel(); ``` ### 下载状态获取 @@ -97,12 +109,13 @@ annotationProcessor 'com.arialyy.aria:aria-compiler:3.2.0' } ``` -2. 使用`@Download`或`@Upload`注解你的函数
- **注意:** - - 注解回掉采用Apt的方式实现,所以,你不需要担心这会影响你机器的性能 - - 被注解的方法**不能被private修饰** - - 被注解的方法**只能有一个参数,并且参数类型必须是`DownloadTask`或`UploadTask`** - - 方法名可以为任意字符串 +2. 使用`@Download`或`@Upload`或`@DownloadGroup`注解你的函数 + + **注意:** + - 注解回掉采用Apt的方式实现,所以,你不需要担心这会影响你机器的性能 + - 被注解的方法**不能被private修饰** + - 被注解的方法**只能有一个参数,并且参数类型必须是`DownloadTask`或`UploadTask`或`DownloadGroupTask`** + - 方法名可以为任意字符串 3. 除了在widget(Activity、Fragment、Dialog、Popupwindow)中使用注解方法外,你还可以在Service、Notification等组件中使用注解函数。 @@ -336,6 +349,7 @@ Aria.download(this).load(DOWNLOAD_URL).setExtendField(str) ## 开发日志 + + v_3.2.6 移除广播事件,增加任务组下载功能 + v_3.1.9 修复stopAll队列没有任务时崩溃的问题,增加针对单个任务监听的功能 + v_3.1.7 修复某些文件下载不了的bug,增加apt注解方法,事件获取更加简单了 + v_3.1.6 取消任务时onTaskCancel回调两次的bug @@ -345,16 +359,6 @@ Aria.download(this).load(DOWNLOAD_URL).setExtendField(str) + v_3.0.3 修复暂停后删除任务,闪退问题,添加删除记录的api + v_3.0.2 支持30x重定向链接下载 + v_3.0.0 添加上传任务支持,修复一些已发现的bug - + v_2.4.4 修复不支持断点的下载链接拿不到文件大小的问题 - + v_2.4.3 修复404链接卡顿的问题 - + v_2.4.2 修复失败重试无效的bug - + v_2.4.1 修复下载慢的问题,修复application、service 不能使用的问题 - + v_2.4.0 支持https链接下载 - + v_2.3.8 修复数据错乱的bug、添加fragment支持 - + v_2.3.6 添加dialog、popupWindow支持 - + v_2.3.3 添加断点支持、修改下载逻辑,让使用更加简单、修复一个内存泄露的bug - + v_2.3.1 重命名为Aria,下载流程简化 - + v_2.1.1 增加,选择最大下载任务数接口 License ------- diff --git a/app/build.gradle b/app/build.gradle index 247814e2..c0683d76 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion 23 - buildToolsVersion '25.0.2' + buildToolsVersion '25.0.3' defaultConfig { applicationId "com.arialyy.simple" @@ -42,7 +42,6 @@ dependencies { compile 'com.squareup.okhttp3:okhttp:3.2.0' compile 'com.arialyy.frame:MVVM2:2.2.0' compile project(':Aria') -// compile 'com.arialyy.aria:aria-core:3.1.8' -// annotationProcessor 'com.arialyy.aria:aria-compiler:3.1.8' + compile project(':AriaCompiler') } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b91acce7..4443482d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -31,6 +31,7 @@ + diff --git a/app/src/main/assets/aria_config.xml b/app/src/main/assets/aria_config.xml index f9ed36f3..474752d0 100644 --- a/app/src/main/assets/aria_config.xml +++ b/app/src/main/assets/aria_config.xml @@ -7,9 +7,6 @@ - - - @@ -34,11 +31,12 @@ + + + - - diff --git a/app/src/main/java/com/arialyy/simple/MainActivity.java b/app/src/main/java/com/arialyy/simple/MainActivity.java index 7b315dab..5867a402 100644 --- a/app/src/main/java/com/arialyy/simple/MainActivity.java +++ b/app/src/main/java/com/arialyy/simple/MainActivity.java @@ -18,14 +18,12 @@ package com.arialyy.simple; import android.content.Intent; import android.os.Bundle; -import android.support.v7.widget.Toolbar; import android.view.View; -import butterknife.Bind; import butterknife.OnClick; -import com.arialyy.aria.core.Aria; import com.arialyy.simple.base.BaseActivity; import com.arialyy.simple.databinding.ActivityMainBinding; import com.arialyy.simple.download.DownloadActivity; +import com.arialyy.simple.download.group.DownloadGroupActivity; import com.arialyy.simple.test.TestMutilTaskSysDownload; import com.arialyy.simple.upload.UploadActivity; @@ -45,15 +43,20 @@ public class MainActivity extends BaseActivity { return R.layout.activity_main; } - @OnClick(R.id.download) public void downloadDemo() { - startActivity(new Intent(this, DownloadActivity.class)); - } - @OnClick(R.id.upload) public void uploadDemo() { - startActivity(new Intent(this, UploadActivity.class)); - } + @OnClick({R.id.download, R.id.upload, R.id.download_task_group}) + public void funcation(View view){ + switch (view.getId()){ + case R.id.download: + startActivity(new Intent(this, DownloadActivity.class)); + break; + case R.id.upload: + startActivity(new Intent(this, UploadActivity.class)); + break; + case R.id.download_task_group: + startActivity(new Intent(this, DownloadGroupActivity.class)); + break; + } - @OnClick(R.id.multi_test) public void mutliTest() { - startActivity(new Intent(this, TestMutilTaskSysDownload.class)); } } diff --git a/app/src/main/java/com/arialyy/simple/base/BaseApplication.java b/app/src/main/java/com/arialyy/simple/base/BaseApplication.java index 117a34dd..29cda1b9 100644 --- a/app/src/main/java/com/arialyy/simple/base/BaseApplication.java +++ b/app/src/main/java/com/arialyy/simple/base/BaseApplication.java @@ -17,7 +17,10 @@ package com.arialyy.simple.base; import android.app.Application; +import android.os.Build; +import android.os.StrictMode; import com.arialyy.frame.core.AbsFrame; +import com.arialyy.simple.BuildConfig; /** * Created by Lyy on 2016/9/27. @@ -26,5 +29,11 @@ public class BaseApplication extends Application { @Override public void onCreate() { super.onCreate(); AbsFrame.init(this); + + if (BuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { + StrictMode.setThreadPolicy( + new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build()); + StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build()); + } } } \ No newline at end of file diff --git a/app/src/main/java/com/arialyy/simple/base/adapter/AbsRVAdapter.java b/app/src/main/java/com/arialyy/simple/base/adapter/AbsRVAdapter.java index 744ccd94..e7eecd4a 100644 --- a/app/src/main/java/com/arialyy/simple/base/adapter/AbsRVAdapter.java +++ b/app/src/main/java/com/arialyy/simple/base/adapter/AbsRVAdapter.java @@ -16,6 +16,8 @@ package com.arialyy.simple.base.adapter; import android.content.Context; +import android.databinding.DataBindingUtil; +import android.databinding.ViewDataBinding; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; @@ -45,6 +47,9 @@ public abstract class AbsRVAdapter @Override public Holder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(setLayoutId(viewType), parent, false); + //LayoutInflater inflater = LayoutInflater.from(parent.getContext()); + //VD binding = DataBindingUtil.inflate(inflater, setLayoutId(viewType), parent, false); + //; holder = getViewHolder(view, viewType); return holder; } @@ -55,6 +60,14 @@ public abstract class AbsRVAdapter bindData(holder, position, mData.get(position)); } + @Override public void onBindViewHolder(Holder holder, int position, List payloads) { + if (payloads == null || payloads.isEmpty()) { + bindData(holder, position, mData.get(position)); + } else { + bindData(holder, position, mData.get(position), payloads); + } + } + public Context getContext() { return mContext; } @@ -69,4 +82,8 @@ public abstract class AbsRVAdapter protected abstract int setLayoutId(int type); protected abstract void bindData(Holder holder, int position, T item); + + protected void bindData(Holder holder, int position, T item, List payloads) { + + } } \ No newline at end of file diff --git a/app/src/main/java/com/arialyy/simple/download/DownloadModule.java b/app/src/main/java/com/arialyy/simple/download/DownloadModule.java index f4c2e831..faef1cf3 100644 --- a/app/src/main/java/com/arialyy/simple/download/DownloadModule.java +++ b/app/src/main/java/com/arialyy/simple/download/DownloadModule.java @@ -16,27 +16,16 @@ package com.arialyy.simple.download; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.content.res.Resources; import android.os.Environment; -import android.os.Handler; -import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.util.CommonUtil; -import com.arialyy.frame.util.AndroidUtils; -import com.arialyy.frame.util.StringUtil; -import com.arialyy.frame.util.show.L; import com.arialyy.simple.R; import com.arialyy.simple.download.multi_download.FileListEntity; import com.arialyy.simple.base.BaseModule; -import java.io.File; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; -import java.util.Random; /** * Created by Lyy on 2016/9/27. @@ -92,69 +81,4 @@ public class DownloadModule extends BaseModule { return entity; } - /** - * 下载广播过滤器 - */ - public IntentFilter getDownloadFilter() { - IntentFilter filter = new IntentFilter(); - filter.addDataScheme(getContext().getPackageName()); - filter.addAction(Aria.ACTION_PRE); - filter.addAction(Aria.ACTION_POST_PRE); - filter.addAction(Aria.ACTION_RESUME); - filter.addAction(Aria.ACTION_START); - filter.addAction(Aria.ACTION_RUNNING); - filter.addAction(Aria.ACTION_STOP); - filter.addAction(Aria.ACTION_CANCEL); - filter.addAction(Aria.ACTION_COMPLETE); - filter.addAction(Aria.ACTION_FAIL); - return filter; - } - - /** - * 创建Receiver - */ - public BroadcastReceiver createReceiver(final Handler handler) { - - return new BroadcastReceiver() { - long len = 0; - - @Override public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - switch (action) { - case Aria.ACTION_POST_PRE: - DownloadEntity entity = intent.getParcelableExtra(Aria.DOWNLOAD_ENTITY); - len = entity.getFileSize(); - L.d(TAG, "download onPre"); - handler.obtainMessage(SingleTaskActivity.DOWNLOAD_PRE, len).sendToTarget(); - break; - case Aria.ACTION_START: - L.d(TAG, "download start"); - break; - case Aria.ACTION_RESUME: - L.d(TAG, "download resume"); - long location = intent.getLongExtra(Aria.CURRENT_LOCATION, 1); - handler.obtainMessage(SingleTaskActivity.DOWNLOAD_RESUME, location).sendToTarget(); - break; - case Aria.ACTION_RUNNING: - long current = intent.getLongExtra(Aria.CURRENT_LOCATION, 0); - int progress = len == 0 ? 0 : (int) ((current * 100) / len); - handler.obtainMessage(SingleTaskActivity.DOWNLOAD_RUNNING, progress).sendToTarget(); - break; - case Aria.ACTION_STOP: - L.d(TAG, "download stop"); - handler.sendEmptyMessage(SingleTaskActivity.DOWNLOAD_STOP); - break; - case Aria.ACTION_COMPLETE: - handler.sendEmptyMessage(SingleTaskActivity.DOWNLOAD_COMPLETE); - break; - case Aria.ACTION_CANCEL: - handler.sendEmptyMessage(SingleTaskActivity.DOWNLOAD_CANCEL); - break; - case Aria.ACTION_FAIL: - handler.sendEmptyMessage(SingleTaskActivity.DOWNLOAD_FAILE); - break; - } - } - }; - } } \ No newline at end of file diff --git a/app/src/main/java/com/arialyy/simple/download/HighestPriorityActivity.java b/app/src/main/java/com/arialyy/simple/download/HighestPriorityActivity.java index aee06f79..611861dd 100644 --- a/app/src/main/java/com/arialyy/simple/download/HighestPriorityActivity.java +++ b/app/src/main/java/com/arialyy/simple/download/HighestPriorityActivity.java @@ -29,6 +29,7 @@ import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadTarget; import com.arialyy.aria.core.download.DownloadTask; +import com.arialyy.aria.core.inf.AbsEntity; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.frame.util.show.L; import com.arialyy.simple.R; @@ -58,7 +59,7 @@ public class HighestPriorityActivity extends BaseActivity mData = new ArrayList<>(); + private List mData = new ArrayList<>(); private Set mRecord = new HashSet<>(); @Override protected int setLayoutId() { @@ -83,7 +84,7 @@ public class HighestPriorityActivity extends BaseActivity temp = Aria.download(this).getTaskList(); + List temp = Aria.download(this).getSimpleTaskList(); if (temp != null && !temp.isEmpty()) { for (DownloadEntity entity : temp) { if (entity.getDownloadUrl().equals(DOWNLOAD_URL)) continue; diff --git a/app/src/main/java/com/arialyy/simple/download/SingleTaskActivity.java b/app/src/main/java/com/arialyy/simple/download/SingleTaskActivity.java index 6e3b468e..5e4612fa 100644 --- a/app/src/main/java/com/arialyy/simple/download/SingleTaskActivity.java +++ b/app/src/main/java/com/arialyy/simple/download/SingleTaskActivity.java @@ -16,15 +16,8 @@ package com.arialyy.simple.download; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; import android.os.Bundle; import android.os.Environment; -import android.os.Handler; -import android.os.Message; -import android.support.v7.widget.Toolbar; -import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -36,33 +29,20 @@ import butterknife.Bind; import com.arialyy.annotations.Download; import com.arialyy.aria.core.download.DownloadTarget; import com.arialyy.aria.core.Aria; -import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadTask; import com.arialyy.aria.core.inf.IEntity; -import com.arialyy.aria.util.CommonUtil; -import com.arialyy.frame.util.FileUtil; -import com.arialyy.frame.util.show.L; import com.arialyy.frame.util.show.T; import com.arialyy.simple.R; import com.arialyy.simple.base.BaseActivity; import com.arialyy.simple.databinding.ActivitySingleBinding; -import com.arialyy.simple.widget.HorizontalProgressBarWithNumber; -import java.io.File; public class SingleTaskActivity extends BaseActivity { - public static final int DOWNLOAD_PRE = 0x01; - public static final int DOWNLOAD_STOP = 0x02; - public static final int DOWNLOAD_FAILE = 0x03; - public static final int DOWNLOAD_CANCEL = 0x04; - public static final int DOWNLOAD_RESUME = 0x05; - public static final int DOWNLOAD_COMPLETE = 0x06; - public static final int DOWNLOAD_RUNNING = 0x07; - public static final int DOWNLOAD_START = 0x08; private static final String DOWNLOAD_URL = //"http://kotlinlang.org/docs/kotlin-docs.pdf"; //"https://atom-installer.github.com/v1.13.0/AtomSetup.exe?s=1484074138&ext=.exe"; "http://static.gaoshouyou.com/d/22/94/822260b849944492caadd2983f9bb624.apk"; + //"http://down2.xiaoshuofuwuqi.com/d/file/filetxt/20170608/14/%BA%DA%CE%D7%CA%A6%E1%C8%C6%F0.txt"; //"http://tinghuaapp.oss-cn-shanghai.aliyuncs.com/20170612201739607815"; //"http://static.gaoshouyou.com/d/36/69/2d3699acfa69e9632262442c46516ad8.apk"; //"http://oqcpqqvuf.bkt.clouddn.com/ceshi.txt"; @@ -70,65 +50,11 @@ public class SingleTaskActivity extends BaseActivity { //不支持断点的链接 //"http://ox.konsung.net:5555/ksdc-web/download/downloadFile/?fileName=ksdc_1.0.2.apk&rRange=0-"; //"http://172.18.104.50:8080/download/_302turn"; - @Bind(R.id.progressBar) HorizontalProgressBarWithNumber mPb; @Bind(R.id.start) Button mStart; @Bind(R.id.stop) Button mStop; @Bind(R.id.cancel) Button mCancel; - @Bind(R.id.size) TextView mSize; - @Bind(R.id.speed) TextView mSpeed; @Bind(R.id.speeds) RadioGroup mRg; - private Handler mUpdateHandler = new Handler() { - @Override public void handleMessage(Message msg) { - super.handleMessage(msg); - switch (msg.what) { - case DOWNLOAD_RUNNING: - DownloadTask task = (DownloadTask) msg.obj; - long current = task.getCurrentProgress(); - long len = task.getFileSize(); - if (len == 0) { - mPb.setProgress(0); - } else { - mPb.setProgress((int) ((current * 100) / len)); - } - mSpeed.setText(task.getConvertSpeed()); - break; - case DOWNLOAD_PRE: - setBtState(false); - break; - case DOWNLOAD_START: - mSize.setText(CommonUtil.formatFileSize((Long) msg.obj)); - break; - case DOWNLOAD_FAILE: - Toast.makeText(SingleTaskActivity.this, "下载失败", Toast.LENGTH_SHORT).show(); - setBtState(true); - break; - case DOWNLOAD_STOP: - Toast.makeText(SingleTaskActivity.this, "暂停下载", Toast.LENGTH_SHORT).show(); - mStart.setText("恢复"); - setBtState(true); - break; - case DOWNLOAD_CANCEL: - mPb.setProgress(0); - Toast.makeText(SingleTaskActivity.this, "取消下载", Toast.LENGTH_SHORT).show(); - mStart.setText("开始"); - setBtState(true); - break; - case DOWNLOAD_RESUME: - mStart.setText("暂停"); - setBtState(false); - break; - case DOWNLOAD_COMPLETE: - mPb.setProgress(100); - Toast.makeText(SingleTaskActivity.this, "下载完成", Toast.LENGTH_SHORT).show(); - mStart.setText("重新开始?"); - mCancel.setEnabled(false); - setBtState(true); - break; - } - } - }; - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Aria.download(this).register(); @@ -153,7 +79,7 @@ public class SingleTaskActivity extends BaseActivity { switch (item.getItemId()) { case R.id.help: msg = "一些小知识点:\n" - + "1、你可以通过task.getKey().equals(DOWNLOAD_URL)判断是否是当前页面的下载,以防止progress乱跳\n" + + "1、你可以在注解中增加链接,用于指定被注解的方法只能被特定的下载任务回调,以防止progress乱跳\n" + "2、当遇到网络慢的情况时,你可以先使用onPre()更新UI界面,待连接成功时,再在onTaskPre()获取完整的task数据,然后给UI界面设置正确的数据\n" + "3、你可以在界面初始化时通过Aria.download(this).load(DOWNLOAD_URL).getPercent()等方法快速获取相关任务的一些数据"; showMsgDialog("tip", msg); @@ -183,40 +109,54 @@ public class SingleTaskActivity extends BaseActivity { } @Download.onPre(DOWNLOAD_URL) protected void onPre(DownloadTask task) { - mUpdateHandler.obtainMessage(DOWNLOAD_PRE, task.getDownloadEntity().getFileSize()) - .sendToTarget(); + setBtState(false); } @Download.onTaskStart(DOWNLOAD_URL) void taskStart(DownloadTask task) { - mUpdateHandler.obtainMessage(DOWNLOAD_START, task.getDownloadEntity().getFileSize()) - .sendToTarget(); + getBinding().setFileSize(task.getConvertFileSize()); } @Download.onTaskRunning(DOWNLOAD_URL) protected void running(DownloadTask task) { - mUpdateHandler.obtainMessage(DOWNLOAD_RUNNING, task).sendToTarget(); + long len = task.getFileSize(); + if (len == 0) { + getBinding().setProgress(0); + } else { + getBinding().setProgress(task.getPercent()); + } + getBinding().setSpeed(task.getConvertSpeed()); } @Download.onTaskResume(DOWNLOAD_URL) void taskResume(DownloadTask task) { - mUpdateHandler.obtainMessage(DOWNLOAD_START, task.getFileSize()).sendToTarget(); + mStart.setText("暂停"); + setBtState(false); } @Download.onTaskStop(DOWNLOAD_URL) void taskStop(DownloadTask task) { - mUpdateHandler.sendEmptyMessage(DOWNLOAD_STOP); - L.d(TAG, "task__stop"); + mStart.setText("恢复"); + setBtState(true); + getBinding().setSpeed(""); } @Download.onTaskCancel(DOWNLOAD_URL) void taskCancel(DownloadTask task) { - mUpdateHandler.sendEmptyMessage(DOWNLOAD_CANCEL); - L.d(TAG, "task__cancel"); + getBinding().setProgress(0); + Toast.makeText(SingleTaskActivity.this, "取消下载", Toast.LENGTH_SHORT).show(); + mStart.setText("开始"); + setBtState(true); + getBinding().setSpeed(""); } @Download.onTaskFail(DOWNLOAD_URL) void taskFail(DownloadTask task) { - L.d(TAG, "task__fail"); - mUpdateHandler.sendEmptyMessage(DOWNLOAD_FAILE); + Toast.makeText(SingleTaskActivity.this, "下载失败", Toast.LENGTH_SHORT).show(); + setBtState(true); } @Download.onTaskComplete(DOWNLOAD_URL) void taskComplete(DownloadTask task) { - mUpdateHandler.sendEmptyMessage(DOWNLOAD_COMPLETE); + getBinding().setProgress(100); + Toast.makeText(SingleTaskActivity.this, "下载完成", Toast.LENGTH_SHORT).show(); + mStart.setText("重新开始?"); + mCancel.setEnabled(false); + setBtState(true); + getBinding().setSpeed(""); } @Download.onNoSupportBreakPoint(DOWNLOAD_URL) @@ -232,7 +172,7 @@ public class SingleTaskActivity extends BaseActivity { super.init(savedInstanceState); setTitle("单任务下载"); DownloadTarget target = Aria.download(this).load(DOWNLOAD_URL); - mPb.setProgress(target.getPercent()); + getBinding().setProgress(target.getPercent()); if (target.getTaskState() == IEntity.STATE_STOP) { mStart.setText("恢复"); mStart.setTextColor(getResources().getColor(android.R.color.holo_blue_light)); @@ -240,8 +180,7 @@ public class SingleTaskActivity extends BaseActivity { } else if (target.isDownloading()) { setBtState(false); } - mSize.setText(target.getConvertFileSize()); - Aria.get(this).getDownloadConfig().setOpenBreadCast(true); + getBinding().setFileSize(target.getConvertFileSize()); } public void onClick(View view) { @@ -251,6 +190,7 @@ public class SingleTaskActivity extends BaseActivity { if (text.equals("重新开始?") || text.equals("开始")) { Aria.download(this) .load(DOWNLOAD_URL) + .addHeader("groupName", "value") .setDownloadPath(Environment.getExternalStorageDirectory().getPath() + "/test.apk") .start(); } else if (text.equals("恢复")) { diff --git a/app/src/main/java/com/arialyy/simple/download/fragment_download/DownloadFragment.java b/app/src/main/java/com/arialyy/simple/download/fragment_download/DownloadFragment.java index d78f82fe..8e34589b 100644 --- a/app/src/main/java/com/arialyy/simple/download/fragment_download/DownloadFragment.java +++ b/app/src/main/java/com/arialyy/simple/download/fragment_download/DownloadFragment.java @@ -38,25 +38,20 @@ import com.arialyy.simple.widget.HorizontalProgressBarWithNumber; * Created by Aria.Lao on 2017/1/4. */ public class DownloadFragment extends AbsFragment { - @Bind(R.id.progressBar) HorizontalProgressBarWithNumber mPb; @Bind(R.id.start) Button mStart; @Bind(R.id.stop) Button mStop; @Bind(R.id.cancel) Button mCancel; - @Bind(R.id.size) TextView mSize; - @Bind(R.id.speed) TextView mSpeed; - private static final String DOWNLOAD_URL = - "http://static.ilongyuan.cn/rayark/RayarkFZ_2.0.7.apk"; + private static final String DOWNLOAD_URL = "http://static.ilongyuan.cn/rayark/RayarkFZ_2.0.7.apk"; @Override protected void init(Bundle savedInstanceState) { if (Aria.download(this).taskExists(DOWNLOAD_URL)) { DownloadTarget target = Aria.download(this).load(DOWNLOAD_URL); - int p = (int) (target.getCurrentProgress() * 100 / target.getFileSize()); - mPb.setProgress(p); + getBinding().setProgress(target.getPercent()); } DownloadEntity entity = Aria.download(this).getDownloadEntity(DOWNLOAD_URL); if (entity != null) { - mSize.setText(CommonUtil.formatFileSize(entity.getFileSize())); + getBinding().setFileSize(CommonUtil.formatFileSize(entity.getFileSize())); int state = entity.getState(); setBtState(state != DownloadEntity.STATE_RUNNING); } else { @@ -83,30 +78,29 @@ public class DownloadFragment extends AbsFragment { } @Download.onTaskPre public void onTaskPre(DownloadTask task) { - mSize.setText(CommonUtil.formatFileSize(task.getFileSize())); + getBinding().setFileSize(task.getConvertFileSize()); setBtState(false); } @Download.onTaskStop public void onTaskStop(DownloadTask task) { setBtState(true); - mSpeed.setText("0.0kb/s"); + getBinding().setSpeed("0.0kb/s"); } @Download.onTaskCancel public void onTaskCancel(DownloadTask task) { setBtState(true); - mPb.setProgress(0); - mSpeed.setText("0.0kb/s"); + getBinding().setProgress(0); + getBinding().setSpeed("0.0kb/s"); } @Download.onTaskRunning public void onTaskRunning(DownloadTask task) { - long current = task.getCurrentProgress(); long len = task.getFileSize(); if (len == 0) { - mPb.setProgress(0); + getBinding().setProgress(0); } else { - mPb.setProgress((int) ((current * 100) / len)); + getBinding().setProgress(task.getPercent()); } - mSpeed.setText(task.getConvertSpeed()); + getBinding().setSpeed(task.getConvertSpeed()); } @Override protected void onDelayLoad() { diff --git a/app/src/main/java/com/arialyy/simple/download/group/DownloadGroupActivity.java b/app/src/main/java/com/arialyy/simple/download/group/DownloadGroupActivity.java new file mode 100644 index 00000000..b5471182 --- /dev/null +++ b/app/src/main/java/com/arialyy/simple/download/group/DownloadGroupActivity.java @@ -0,0 +1,131 @@ +/* + * 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.simple.download.group; + +import android.os.Bundle; +import android.os.Environment; +import android.view.View; +import android.widget.Button; +import butterknife.Bind; +import com.arialyy.annotations.DownloadGroup; +import com.arialyy.aria.core.Aria; +import com.arialyy.aria.core.download.DownloadGroupEntity; +import com.arialyy.aria.core.download.DownloadGroupTask; +import com.arialyy.aria.core.download.DownloadGroupTaskEntity; +import com.arialyy.frame.util.show.L; +import com.arialyy.frame.util.show.T; +import com.arialyy.simple.R; +import com.arialyy.simple.base.BaseActivity; +import com.arialyy.simple.databinding.ActivityDownloadGroupBinding; +import com.arialyy.simple.widget.SubStateLinearLayout; +import java.util.List; + +/** + * Created by Aria.Lao on 2017/7/6. + */ +public class DownloadGroupActivity extends BaseActivity { + + @Bind(R.id.start) Button mStart; + @Bind(R.id.stop) Button mStop; + @Bind(R.id.cancel) Button mCancel; + @Bind(R.id.child_list) SubStateLinearLayout mChildList; + List mUrls; + + @Override protected void init(Bundle savedInstanceState) { + super.init(savedInstanceState); + Aria.download(this).register(); + setTitle("任务组"); + mUrls = getModule(GroupModule.class).getUrls(); + DownloadGroupTaskEntity entity = Aria.download(this).getDownloadGroupTask(mUrls); + if (entity != null && entity.getEntity() != null) { + DownloadGroupEntity groupEntity = entity.getEntity(); + mChildList.addData(groupEntity.getSubTask()); + getBinding().setFileSize(groupEntity.getConvertFileSize()); + if (groupEntity.getFileSize() == 0) { + getBinding().setProgress(0); + } else { + getBinding().setProgress(groupEntity.isComplete() ? 100 + : (int) (groupEntity.getCurrentProgress() * 100 / groupEntity.getFileSize())); + } + } + } + + @Override protected int setLayoutId() { + return R.layout.activity_download_group; + } + + public void onClick(View view) { + switch (view.getId()) { + case R.id.start: + Aria.download(this) + .load(mUrls) + .setDownloadDirPath( + Environment.getExternalStorageDirectory().getPath() + "/Download/group_test_3") + .setGroupAlias("任务组测试") + .setSubTaskFileName(getModule(GroupModule.class).getSubName()) + .start(); + break; + case R.id.stop: + Aria.download(this).load(mUrls).stop(); + break; + case R.id.cancel: + Aria.download(this).load(mUrls).cancel(); + break; + } + } + + @DownloadGroup.onPre() protected void onPre(DownloadGroupTask task) { + L.d(TAG, "group pre"); + } + + @DownloadGroup.onTaskPre() protected void onTaskPre(DownloadGroupTask task) { + L.d(TAG, "group task pre"); + getBinding().setFileSize(task.getConvertFileSize()); + } + + @DownloadGroup.onTaskStart() void taskStart(DownloadGroupTask task) { + L.d(TAG, "group task start"); + } + + @DownloadGroup.onTaskRunning() protected void running(DownloadGroupTask task) { + getBinding().setProgress(task.getPercent()); + getBinding().setSpeed(task.getConvertSpeed()); + mChildList.updateChildProgress(task.getEntity().getSubTask()); + } + + @DownloadGroup.onTaskResume() void taskResume(DownloadGroupTask task) { + L.d(TAG, "group task resume"); + } + + @DownloadGroup.onTaskStop() void taskStop(DownloadGroupTask task) { + L.d(TAG, "group task stop"); + getBinding().setSpeed(""); + } + + @DownloadGroup.onTaskCancel() void taskCancel(DownloadGroupTask task) { + getBinding().setSpeed(""); + getBinding().setProgress(0); + } + + @DownloadGroup.onTaskFail() void taskFail(DownloadGroupTask task) { + L.d(TAG, "group task fail"); + } + + @DownloadGroup.onTaskComplete() void taskComplete(DownloadGroupTask task) { + getBinding().setProgress(100); + T.showShort(this, "任务组下载完成"); + } +} diff --git a/app/src/main/java/com/arialyy/simple/download/group/GroupModule.java b/app/src/main/java/com/arialyy/simple/download/group/GroupModule.java new file mode 100644 index 00000000..221212e4 --- /dev/null +++ b/app/src/main/java/com/arialyy/simple/download/group/GroupModule.java @@ -0,0 +1,54 @@ +/* + * 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.simple.download.group; + +import android.content.Context; +import com.arialyy.simple.R; +import com.arialyy.simple.base.BaseModule; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Created by Aria.Lao on 2017/7/6. + */ +public class GroupModule extends BaseModule { + public GroupModule(Context context) { + super(context); + } + + List getUrls() { + List urls = new ArrayList<>(); + String[] str = getContext().getResources().getStringArray(R.array.group_urls); + Collections.addAll(urls, str); + return urls; + } + + List getSubName(){ + List names = new ArrayList<>(); + String[] str = getContext().getResources().getStringArray(R.array.group_names); + Collections.addAll(names, str); + return names; + } + + //NormalList convertPath(NormalList urls){ + // NormalList paths = new ArrayList<>(); + // + // for (String url : urls){ + // + // } + //} +} diff --git a/app/src/main/java/com/arialyy/simple/download/multi_download/DownloadAdapter.java b/app/src/main/java/com/arialyy/simple/download/multi_download/DownloadAdapter.java index 16ccf516..676d2bf8 100644 --- a/app/src/main/java/com/arialyy/simple/download/multi_download/DownloadAdapter.java +++ b/app/src/main/java/com/arialyy/simple/download/multi_download/DownloadAdapter.java @@ -16,6 +16,7 @@ package com.arialyy.simple.download.multi_download; +import android.annotation.SuppressLint; import android.content.Context; import android.content.res.Resources; import android.view.View; @@ -24,12 +25,15 @@ import android.widget.TextView; import butterknife.Bind; import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.download.DownloadGroupEntity; +import com.arialyy.aria.core.inf.AbsEntity; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.util.CommonUtil; import com.arialyy.simple.R; import com.arialyy.simple.base.adapter.AbsHolder; import com.arialyy.simple.base.adapter.AbsRVAdapter; import com.arialyy.simple.widget.HorizontalProgressBarWithNumber; +import com.arialyy.simple.widget.SubStateLinearLayout; import java.util.List; import java.util.Map; import java.util.Set; @@ -39,21 +43,32 @@ import java.util.concurrent.ConcurrentHashMap; * Created by Lyy on 2016/9/27. * 下载列表适配器 */ -public class DownloadAdapter extends AbsRVAdapter { +public class DownloadAdapter extends AbsRVAdapter { private static final String TAG = "DownloadAdapter"; private Map mPositions = new ConcurrentHashMap<>(); - public DownloadAdapter(Context context, List data) { + public DownloadAdapter(Context context, List data) { super(context, data); int i = 0; - for (DownloadEntity entity : data) { - mPositions.put(entity.getDownloadUrl(), i); + for (AbsEntity entity : data) { + mPositions.put(getKey(entity), i); i++; } } - @Override protected MyHolder getViewHolder(View convertView, int viewType) { - return new MyHolder(convertView); + private String getKey(AbsEntity entity) { + if (entity instanceof DownloadEntity) { + return ((DownloadEntity) entity).getDownloadUrl(); + } else if (entity instanceof DownloadGroupEntity) { + return ((DownloadGroupEntity) entity).getGroupName(); + } + return ""; + } + + @Override protected SimpleHolder getViewHolder(View convertView, int viewType) { + if (viewType == 1) return new SimpleHolder(convertView); + if (viewType == 2) return new GroupHolder(convertView); + return null; } public void addDownloadEntity(DownloadEntity entity) { @@ -61,21 +76,33 @@ public class DownloadAdapter extends AbsRVAdapter= mData.size()) { return; } @@ -84,15 +111,18 @@ public class DownloadAdapter extends AbsRVAdapter= mData.size()) { return; } mData.set(position, entity); - notifyItemChanged(position); + notifyItemChanged(position, entity); } private synchronized int indexItem(String url) { @@ -105,51 +135,97 @@ public class DownloadAdapter extends AbsRVAdapter payloads) { + AbsEntity entity = (AbsEntity) payloads.get(0); + updateSpeed(holder, entity); + } + + /** + * 只更新速度 + */ + private void updateSpeed(SimpleHolder holder, final AbsEntity entity) { + long size = entity.getFileSize(); + long progress = entity.getCurrentProgress(); + int current = size == 0 ? 0 : (int) (progress * 100 / size); + holder.speed.setText(entity.getConvertSpeed()); + holder.fileSize.setText(covertCurrentSize(progress) + "/" + CommonUtil.formatFileSize(size)); holder.progress.setProgress(current); - BtClickListener listener = new BtClickListener(item); - holder.bt.setOnClickListener(listener); - holder.name.setText("文件名:" + item.getFileName()); - holder.url.setText("下载地址:" + item.getDownloadUrl()); - holder.path.setText("保持路径:" + item.getDownloadPath()); + //if (holder instanceof GroupHolder){ + // handleSubChild((GroupHolder) holder, entity); + //} + } + + @SuppressLint("SetTextI18n") + private void handleProgress(SimpleHolder holder, final AbsEntity entity) { String str = ""; int color = android.R.color.holo_green_light; - switch (item.getState()) { - case DownloadEntity.STATE_WAIT: - case DownloadEntity.STATE_OTHER: - case DownloadEntity.STATE_FAIL: + switch (entity.getState()) { + case IEntity.STATE_WAIT: + case IEntity.STATE_OTHER: + case IEntity.STATE_FAIL: str = "开始"; break; - case DownloadEntity.STATE_STOP: + case IEntity.STATE_STOP: str = "恢复"; color = android.R.color.holo_blue_light; break; - case DownloadEntity.STATE_PRE: - case DownloadEntity.STATE_POST_PRE: - case DownloadEntity.STATE_RUNNING: + case IEntity.STATE_PRE: + case IEntity.STATE_POST_PRE: + case IEntity.STATE_RUNNING: str = "暂停"; color = android.R.color.holo_red_light; break; - case DownloadEntity.STATE_COMPLETE: + case IEntity.STATE_COMPLETE: str = "重新开始?"; holder.progress.setProgress(100); break; } + long size = entity.getFileSize(); + long progress = entity.getCurrentProgress(); + int current = size == 0 ? 0 : (int) (progress * 100 / size); holder.bt.setText(str); holder.bt.setTextColor(getColor(color)); - holder.speed.setText(item.getConvertSpeed()); + holder.progress.setProgress(current); + + BtClickListener listener = new BtClickListener(entity); + holder.bt.setOnClickListener(listener); + String name = (isSimpleDownload(entity) ? ((DownloadEntity) entity).getFileName() + : ((DownloadGroupEntity) entity).getAlias()); + holder.name.setText("文件名:" + name); + holder.speed.setText(entity.getConvertSpeed()); holder.fileSize.setText(covertCurrentSize(progress) + "/" + CommonUtil.formatFileSize(size)); + //删除按钮事件 holder.cancel.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - mData.remove(item); + mData.remove(entity); notifyDataSetChanged(); - Aria.download(getContext()).load(item).cancel(); + if (isSimpleDownload(entity)) { + Aria.download(getContext()).load((DownloadEntity) entity).cancel(); + } else { + Aria.download(getContext()).load((DownloadGroupEntity) entity).cancel(); + } } }); + //if (holder instanceof GroupHolder){ + // handleSubChild((GroupHolder) holder, entity); + //} + } + + private void handleSubChild(GroupHolder holder, final AbsEntity entity) { + if (holder.childList.getSubData().size() > 0){ + holder.childList.updateChildProgress(((DownloadGroupEntity)entity).getSubTask()); + }else { + holder.childList.addData(((DownloadGroupEntity)entity).getSubTask()); + } + } + + private boolean isSimpleDownload(AbsEntity entity) { + return entity instanceof DownloadEntity; } private String covertCurrentSize(long currentSize) { @@ -161,49 +237,67 @@ public class DownloadAdapter extends AbsRVAdapter { @Bind(R.id.list) RecyclerView mList; private DownloadAdapter mAdapter; - private List mData = new ArrayList<>(); - - String[] mFilterStr = new String[] { - "https://g37.gdl.netease.com/onmyoji_netease_10_1.0.20.apk", - "http://static.gaoshouyou.com/d/eb/f2/dfeba30541f209ab8a50d847fc1661ce.apk" - }; + private List mData = new ArrayList<>(); @Override protected int setLayoutId() { return R.layout.activity_multi_download; @@ -56,7 +52,7 @@ public class MultiDownloadActivity extends BaseActivity temps = Aria.download(this).getTaskList(); + List temps = Aria.download(this).getTotleTaskList(); if (temps != null && !temps.isEmpty()) { mData.addAll(temps); } @@ -76,40 +72,68 @@ public class MultiDownloadActivity extends BaseActivity { @OnClick(R.id.upload) void upload() { Aria.upload(this) .load(FILE_PATH) - .setUploadUrl("http://172.18.104.228:8080/upload/sign_file") + .setUploadUrl("http://172.18.104.129:8080/upload/sign_file") .setAttachment("file") .start(); } diff --git a/app/src/main/java/com/arialyy/simple/widget/NoScrollListView.java b/app/src/main/java/com/arialyy/simple/widget/NoScrollListView.java new file mode 100644 index 00000000..b481ca64 --- /dev/null +++ b/app/src/main/java/com/arialyy/simple/widget/NoScrollListView.java @@ -0,0 +1,43 @@ +/* + * 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.simple.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.ListView; + +/** + * Created by lyy on 2015/4/22. + * 不带有滑动功能的ListView + */ +public class NoScrollListView extends ListView { + public NoScrollListView(Context context) { + super(context); + } + + public NoScrollListView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public NoScrollListView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); + super.onMeasure(widthMeasureSpec, expandSpec); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/arialyy/simple/widget/SubStateLinearLayout.java b/app/src/main/java/com/arialyy/simple/widget/SubStateLinearLayout.java new file mode 100644 index 00000000..6c6b4660 --- /dev/null +++ b/app/src/main/java/com/arialyy/simple/widget/SubStateLinearLayout.java @@ -0,0 +1,121 @@ +package com.arialyy.simple.widget; + +import android.content.Context; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.simple.R; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + +/** + * Created by Aria.Lao on 2017/7/17. + */ +public class SubStateLinearLayout extends LinearLayout { + + interface OnShowCallback { + void onShow(boolean visibility); + } + + OnShowCallback callback; + + List mSubData = new LinkedList<>(); + Map mPosition = new WeakHashMap<>(); + + public SubStateLinearLayout(Context context) { + super(context); + setOrientation(VERTICAL); + } + + public SubStateLinearLayout(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + setOrientation(VERTICAL); + } + + public SubStateLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setOrientation(VERTICAL); + } + + public void addData(List datas) { + removeAllViews(); + mSubData.clear(); + mSubData.addAll(datas); + createShowView(); + int i = 1; + for (DownloadEntity entity : datas) { + TextView view = createView(entity); + mPosition.put(entity.getDownloadPath(), i); + addView(view, i); + i++; + } + } + + public void setOnShowCallback(OnShowCallback callback) { + this.callback = callback; + } + + public List getSubData() { + return mSubData; + } + + public void updateChildProgress(List entities) { + for (DownloadEntity entity : entities) { + Integer i = mPosition.get(entity.getDownloadPath()); + if (i == null) return; + int position = i; + if (position != -1) { + TextView child = ((TextView) getChildAt(position)); + int p = getPercent(entity); + child.setText(entity.getFileName() + ": " + p + "%" + " | " + entity.getConvertSpeed()); + child.invalidate(); + } + } + } + + private TextView createView(DownloadEntity entity) { + TextView view = + (TextView) LayoutInflater.from(getContext()).inflate(R.layout.layout_child_state, null); + view.setText(entity.getFileName() + ": " + getPercent(entity) + "%"); + return view; + } + + private void createShowView() { + + TextView view = + (TextView) LayoutInflater.from(getContext()).inflate(R.layout.layout_child_state, null); + view.setText("点击显示子任务"); + view.setOnClickListener(new OnClickListener() { + @Override public void onClick(View v) { + int visibility = getChildAt(1).getVisibility(); + if (visibility == GONE) { + showChild(true); + ((TextView) v).setText("点击隐藏子任务"); + } else { + showChild(false); + ((TextView) v).setText("点击显示子任务"); + } + } + }); + addView(view, 0); + } + + private void showChild(boolean show) { + for (int i = 1, count = getChildCount(); i < count; i++) { + getChildAt(i).setVisibility(show ? VISIBLE : GONE); + invalidate(); + } + } + + private int getPercent(DownloadEntity entity) { + long size = entity.getFileSize(); + long progress = entity.getCurrentProgress(); + return size == 0 ? 0 : (int) (progress * 100 / size); + } +} diff --git a/app/src/main/res/layout/activity_download_group.xml b/app/src/main/res/layout/activity_download_group.xml new file mode 100644 index 00000000..98d73bdc --- /dev/null +++ b/app/src/main/res/layout/activity_download_group.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 84e60c71..17407578 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -13,7 +13,7 @@ android:id="@+id/download" android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="下载 demo" + android:text="下载" style="?buttonBarButtonStyle" /> @@ -21,15 +21,15 @@ android:id="@+id/upload" android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="上传 demo" + android:text="上传" style="?buttonBarButtonStyle" />