From 0d3c157f381232a50038f0b5481175d4b6bd4f08 Mon Sep 17 00:00:00 2001
From: AriaLyy <511455842@qq.com>
Date: Sat, 31 Dec 2016 14:40:42 +0800
Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=AD=E7=82=B9=E6=94=AF?=
=?UTF-8?q?=E6=8C=81=E5=9B=9E=E8=B0=83=EF=BC=8C=E4=BF=AE=E5=A4=8D=E6=B2=A1?=
=?UTF-8?q?=E6=9C=89=E6=96=AD=E7=82=B9=E6=97=B6=E7=9A=84bug=EF=BC=8C?=
=?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=8B=E8=BD=BD=E4=BA=8B=E4=BB=B6=E5=AF=BC?=
=?UTF-8?q?=E8=87=B4=E7=9A=84=E5=86=85=E5=AD=98=E6=B3=84=E6=BC=8F=E7=9A=84?=
=?UTF-8?q?bug?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.idea/misc.xml | 2 +-
.../com/arialyy/aria/core/AMReceiver.java | 11 +-
.../java/com/arialyy/aria/core/AMTarget.java | 22 +-
.../main/java/com/arialyy/aria/core/Aria.java | 48 ++
.../com/arialyy/aria/core/AriaManager.java | 67 ++-
.../arialyy/aria/core/DownloadManager.java | 48 --
.../aria/core/queue/DownloadTaskQueue.java | 42 +-
.../arialyy/aria/core/queue/ITaskQueue.java | 20 +-
.../core/scheduler/DownloadSchedulers.java | 91 +--
.../aria/core/task/DownloadListener.java | 4 +
.../arialyy/aria/core/task/DownloadUtil.java | 541 ++++++++++--------
.../aria/core/task/IDownloadListener.java | 9 +-
.../java/com/arialyy/aria/core/task/Task.java | 116 ++--
.../com/arialyy/aria/util/CommonUtil.java | 14 +
.../com/arialyy/aria/util/WeakHandler.java | 516 +++++++++++++++++
.../simple/activity/SingleTaskActivity.java | 2 +-
.../arialyy/simple/module/DownloadModule.java | 44 +-
17 files changed, 1106 insertions(+), 491 deletions(-)
create mode 100644 Aria/src/main/java/com/arialyy/aria/util/WeakHandler.java
diff --git a/.idea/misc.xml b/.idea/misc.xml
index fbb68289..5d199810 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -37,7 +37,7 @@
-
+
diff --git a/Aria/src/main/java/com/arialyy/aria/core/AMReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/AMReceiver.java
index f1714319..f73d3b42 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/AMReceiver.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/AMReceiver.java
@@ -15,6 +15,7 @@
*/
package com.arialyy.aria.core;
+import com.arialyy.aria.core.scheduler.DownloadSchedulers;
import com.arialyy.aria.core.scheduler.OnSchedulerListener;
/**
@@ -25,7 +26,6 @@ public class AMReceiver {
Object obj;
OnSchedulerListener listener;
DownloadEntity entity;
- DownloadManager manager = DownloadManager.getInstance();
public AMTarget load(DownloadEntity entity) {
this.entity = entity;
@@ -37,7 +37,7 @@ public class AMReceiver {
*/
public AMReceiver addSchedulerListener(OnSchedulerListener listener) {
this.listener = listener;
- manager.getTaskQueue().getDownloadSchedulers().addSchedulerListener(obj, listener);
+ DownloadSchedulers.getInstance().addSchedulerListener(obj, listener);
return this;
}
@@ -46,8 +46,13 @@ public class AMReceiver {
*/
public AMReceiver removeSchedulerListener() {
if (listener != null) {
- manager.getTaskQueue().getDownloadSchedulers().removeSchedulerListener(obj, listener);
+ DownloadSchedulers.getInstance().removeSchedulerListener(obj, listener);
}
return this;
}
+
+ void destroy() {
+ obj = null;
+ listener = null;
+ }
}
\ No newline at end of file
diff --git a/Aria/src/main/java/com/arialyy/aria/core/AMTarget.java b/Aria/src/main/java/com/arialyy/aria/core/AMTarget.java
index 46cafed4..6c9b23a4 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/AMTarget.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/AMTarget.java
@@ -38,8 +38,9 @@ public class AMTarget {
* 添加任务
*/
public void add() {
- receiver.manager.setCmd(
- CommonUtil.createCmd(receiver.obj, receiver.entity, CmdFactory.TASK_CREATE)).exe();
+ DownloadManager.getInstance()
+ .setCmd(CommonUtil.createCmd(receiver.obj, receiver.entity, CmdFactory.TASK_CREATE))
+ .exe();
}
/**
@@ -49,7 +50,7 @@ public class AMTarget {
List cmds = new ArrayList<>();
cmds.add(CommonUtil.createCmd(receiver.obj, receiver.entity, CmdFactory.TASK_CREATE));
cmds.add(CommonUtil.createCmd(receiver.obj, receiver.entity, CmdFactory.TASK_START));
- receiver.manager.setCmds(cmds).exe();
+ DownloadManager.getInstance().setCmds(cmds).exe();
cmds.clear();
}
@@ -57,24 +58,27 @@ public class AMTarget {
* 停止下载
*/
public void stop() {
- receiver.manager.setCmd(
- CommonUtil.createCmd(receiver.obj, receiver.entity, CmdFactory.TASK_STOP)).exe();
+ DownloadManager.getInstance()
+ .setCmd(CommonUtil.createCmd(receiver.obj, receiver.entity, CmdFactory.TASK_STOP))
+ .exe();
}
/**
* 恢复下载
*/
public void resume() {
- receiver.manager.setCmd(
- CommonUtil.createCmd(receiver.obj, receiver.entity, CmdFactory.TASK_START)).exe();
+ DownloadManager.getInstance()
+ .setCmd(CommonUtil.createCmd(receiver.obj, receiver.entity, CmdFactory.TASK_START))
+ .exe();
}
/**
* 取消下载
*/
public void cancel() {
- receiver.manager.setCmd(
- CommonUtil.createCmd(receiver.obj, receiver.entity, CmdFactory.TASK_CANCEL)).exe();
+ DownloadManager.getInstance()
+ .setCmd(CommonUtil.createCmd(receiver.obj, receiver.entity, CmdFactory.TASK_CANCEL))
+ .exe();
}
/**
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 2d2b3aaf..3fce3d74 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/Aria.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/Aria.java
@@ -43,6 +43,54 @@ import android.os.Build;
*
*/
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) public class Aria {
+ /**
+ * 预处理完成
+ */
+ 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 ENTITY = "DOWNLOAD_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 a7c7e095..eb72a04c 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java
@@ -45,12 +45,12 @@ import java.util.Set;
* Aria管理器,任务操作在这里执行
*/
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) public class AriaManager {
- private static final String TAG = "AriaManager";
- private static final Object LOCK = new Object();
- private static volatile AriaManager INSTANCE = null;
- private Map mTargets = new HashMap<>();
+ private static final String TAG = "AriaManager";
+ private static final Object LOCK = new Object();
+ private static volatile AriaManager INSTANCE = null;
+ private Map mTargets = new HashMap<>();
private DownloadManager mManager;
- private LifeCallback mLifeCallback;
+ private LifeCallback mLifeCallback;
private AriaManager(Context context) {
regAppLifeCallback(context);
@@ -93,7 +93,7 @@ import java.util.Set;
*/
public void stopAllTask() {
List allEntity = mManager.getAllDownloadEntity();
- List stopCmds = new ArrayList<>();
+ List stopCmds = new ArrayList<>();
for (DownloadEntity entity : allEntity) {
if (entity.getState() == DownloadEntity.STATE_DOWNLOAD_ING) {
stopCmds.add(CommonUtil.createCmd(entity, CmdFactory.TASK_STOP));
@@ -152,8 +152,8 @@ import java.util.Set;
* 删除所有任务
*/
public void cancelAllTask() {
- List allEntity = mManager.getAllDownloadEntity();
- List cancelCmds = new ArrayList<>();
+ List allEntity = mManager.getAllDownloadEntity();
+ List cancelCmds = new ArrayList<>();
for (DownloadEntity entity : allEntity) {
cancelCmds.add(CommonUtil.createCmd(entity, CmdFactory.TASK_CANCEL));
}
@@ -167,26 +167,32 @@ import java.util.Set;
}
private AMReceiver putTarget(Object obj) {
- String clsName = obj.getClass().getName();
- AMReceiver target = mTargets.get(clsName);
- if (target == null) {
- target = new AMReceiver();
- target.obj = obj;
- String key = "";
+ String clsName = obj.getClass().getName();
+ AMReceiver target = null;
+ String key = "";
+ if (!(obj instanceof Activity)) {
if (obj instanceof android.support.v4.app.Fragment) {
- key = "_" + ((Fragment) obj).getActivity().getClass().getName();
+ key = clsName + "_" + ((Fragment) obj).getActivity().getClass().getName();
} else if (obj instanceof android.app.Fragment) {
- key = "_" + ((android.app.Fragment) obj).getActivity().getClass().getName();
+ key = clsName + "_" + ((android.app.Fragment) obj).getActivity().getClass().getName();
} else if (obj instanceof Dialog) {
Activity activity = ((Dialog) obj).getOwnerActivity();
if (activity != null) {
- key = "_" + activity.getClass().getName();
+ key = clsName + "_" + activity.getClass().getName();
}
handleDialogDialogLift((Dialog) obj);
}
-
- if (!TextUtils.isEmpty(key)) {
- mTargets.put(clsName, target);
+ } else {
+ key = clsName;
+ }
+ if (TextUtils.isEmpty(key)) {
+ throw new IllegalArgumentException("未知类型");
+ } else {
+ target = mTargets.get(key);
+ if (target == null) {
+ target = new AMReceiver();
+ target.obj = obj;
+ mTargets.put(key, target);
}
}
return target;
@@ -197,12 +203,12 @@ import java.util.Set;
*/
private void handleDialogDialogLift(Dialog dialog) {
try {
- Field dismissField = CommonUtil.getField(dialog.getClass(), "mDismissMessage");
- Message dismissMsg = (Message) dismissField.get(dialog);
+ Field dismissField = CommonUtil.getField(dialog.getClass(), "mDismissMessage");
+ Message dismissMsg = (Message) dismissField.get(dialog);
//如果Dialog已经设置Dismiss事件,则查找cancel事件
if (dismissMsg != null) {
- Field cancelField = CommonUtil.getField(dialog.getClass(), "mCancelMessage");
- Message cancelMsg = (Message) cancelField.get(dialog);
+ Field cancelField = CommonUtil.getField(dialog.getClass(), "mCancelMessage");
+ Message cancelMsg = (Message) cancelField.get(dialog);
if (cancelMsg != null) {
Log.e(TAG, "你已经对Dialog设置了Dismiss和cancel事件。为了防止内存泄露,"
+ "请在dismiss方法中调用Aria.whit(this).removeSchedulerListener();来注销事件");
@@ -270,14 +276,15 @@ import java.util.Set;
* onDestroy
*/
private void destroySchedulerListener(Object obj) {
- Set keys = mTargets.keySet();
- String clsName = obj.getClass().getName();
+ Set keys = mTargets.keySet();
+ String clsName = obj.getClass().getName();
for (String key : keys) {
if (key.equals(clsName) || key.contains(clsName)) {
- AMReceiver target = mTargets.get(key);
- if (target.obj != null) {
- if (target.obj instanceof Application || target.obj instanceof Service) break;
- target.removeSchedulerListener();
+ AMReceiver receiver = mTargets.get(key);
+ if (receiver.obj != null) {
+ if (receiver.obj instanceof Application || receiver.obj instanceof Service) break;
+ receiver.removeSchedulerListener();
+ receiver.destroy();
mTargets.remove(key);
}
break;
diff --git a/Aria/src/main/java/com/arialyy/aria/core/DownloadManager.java b/Aria/src/main/java/com/arialyy/aria/core/DownloadManager.java
index 19afbf70..12640284 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/DownloadManager.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/DownloadManager.java
@@ -31,54 +31,6 @@ import java.util.List;
* 下载管理器,通过命令的方式控制下载
*/
public class DownloadManager {
- /**
- * 预处理完成
- */
- 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 ENTITY = "DOWNLOAD_ENTITY";
- /**
- * 位置
- */
- public static final String CURRENT_LOCATION = "CURRENT_LOCATION";
- /**
- * 速度
- */
- public static final String CURRENT_SPEED = "CURRENT_SPEED";
private static final String TAG = "DownloadManager";
private static final Object LOCK = new Object();
private static volatile DownloadManager INSTANCE = null;
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 59bd9de3..e479b9cc 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
@@ -35,8 +35,8 @@ public class DownloadTaskQueue implements ITaskQueue {
private static final String TAG = "DownloadTaskQueue";
private CachePool mCachePool = CachePool.getInstance();
private ExecutePool mExecutePool = ExecutePool.getInstance();
- private Context mContext;
- private IDownloadSchedulers mSchedulers;
+ private Context mContext;
+ //private IDownloadSchedulers mSchedulers;
private DownloadTaskQueue() {
}
@@ -118,9 +118,9 @@ public class DownloadTaskQueue implements ITaskQueue {
}
}
- @Override public IDownloadSchedulers getDownloadSchedulers() {
- return mSchedulers;
- }
+ //@Override public IDownloadSchedulers getDownloadSchedulers() {
+ // return mSchedulers;
+ //}
@Override public int size() {
return mExecutePool.size();
@@ -130,7 +130,7 @@ public class DownloadTaskQueue implements ITaskQueue {
//原始长度
int size = Configuration.getInstance().getDownloadNum();
int diff = downloadNum - size;
- if (size == downloadNum){
+ if (size == downloadNum) {
Log.d(TAG, "设置的下载任务数和配置文件的下载任务数一直,跳过");
return;
}
@@ -158,10 +158,14 @@ public class DownloadTaskQueue implements ITaskQueue {
@Override public Task createTask(Object target, DownloadEntity entity) {
Task task;
if (target == null) {
- task = TaskFactory.getInstance().createTask(mContext, entity, mSchedulers);
+ //task = TaskFactory.getInstance().createTask(mContext, entity, mSchedulers);
+ task = TaskFactory.getInstance()
+ .createTask(mContext, entity, DownloadSchedulers.getInstance());
} else {
task = TaskFactory.getInstance()
- .createTask(target.getClass().getName(), mContext, entity, mSchedulers);
+ //.createTask(target.getClass().getName(), mContext, entity, mSchedulers);
+ .createTask(target.getClass().getName(), mContext, entity,
+ DownloadSchedulers.getInstance());
}
mCachePool.putTask(task);
return task;
@@ -193,9 +197,9 @@ public class DownloadTaskQueue implements ITaskQueue {
return mCachePool.pollTask();
}
- @Override public void setScheduler(IDownloadSchedulers schedulers) {
- mSchedulers = schedulers;
- }
+ //@Override public void setScheduler(IDownloadSchedulers schedulers) {
+ // mSchedulers = schedulers;
+ //}
public static class Builder {
Context context;
@@ -205,17 +209,17 @@ public class DownloadTaskQueue implements ITaskQueue {
this.context = context.getApplicationContext();
}
- public Builder setDownloadSchedulers(IDownloadSchedulers schedulers) {
- this.schedulers = schedulers;
- return this;
- }
+ //public Builder setDownloadSchedulers(IDownloadSchedulers schedulers) {
+ // this.schedulers = schedulers;
+ // return this;
+ //}
public DownloadTaskQueue build() {
DownloadTaskQueue queue = new DownloadTaskQueue(context);
- if (schedulers == null) {
- schedulers = DownloadSchedulers.getInstance(queue);
- }
- queue.setScheduler(schedulers);
+ //if (schedulers == null) {
+ // schedulers = DownloadSchedulers.getInstance();
+ //}
+ //queue.setScheduler(schedulers);
return queue;
}
}
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 d558c668..485eba63 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
@@ -26,10 +26,10 @@ import com.arialyy.aria.core.task.Task;
*/
public interface ITaskQueue extends IDownloader {
- /**
- * 获取调度器
- */
- public IDownloadSchedulers getDownloadSchedulers();
+ ///**
+ // * 获取调度器
+ // */
+ //public IDownloadSchedulers getDownloadSchedulers();
/**
* 任务池队列大小
@@ -74,10 +74,10 @@ public interface ITaskQueue extends IDownloader {
*/
public Task getNextTask();
- /**
- * 设置下载调度器
- *
- * @param schedulers 下载调度器{@link IDownloadSchedulers}
- */
- public void setScheduler(IDownloadSchedulers schedulers);
+ ///**
+ // * 设置下载调度器
+ // *
+ // * @param schedulers 下载调度器{@link IDownloadSchedulers}
+ // */
+ //public void setScheduler(IDownloadSchedulers schedulers);
}
\ No newline at end of file
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 19ca81fa..33416706 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
@@ -19,12 +19,12 @@ package com.arialyy.aria.core.scheduler;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
+import com.arialyy.aria.core.DownloadManager;
import com.arialyy.aria.core.queue.ITaskQueue;
import com.arialyy.aria.core.DownloadEntity;
import com.arialyy.aria.core.task.Task;
-import com.arialyy.aria.core.queue.pool.ExecutePool;
-import com.arialyy.aria.core.queue.DownloadTaskQueue;
import com.arialyy.aria.util.Configuration;
+import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -36,63 +36,87 @@ public class DownloadSchedulers implements IDownloadSchedulers {
/**
* 任务预加载
*/
- public static final int PRE = 0;
+ public static final int PRE = 0;
/**
* 任务开始
*/
- public static final int START = 1;
+ public static final int START = 1;
/**
* 任务停止
*/
- public static final int STOP = 2;
+ public static final int STOP = 2;
/**
* 任务失败
*/
- public static final int FAIL = 3;
+ public static final int FAIL = 3;
/**
* 任务取消
*/
- public static final int CANCEL = 4;
+ public static final int CANCEL = 4;
/**
* 任务完成
*/
- public static final int COMPLETE = 5;
+ public static final int COMPLETE = 5;
/**
* 下载中
*/
- public static final int RUNNING = 6;
+ public static final int RUNNING = 6;
/**
* 恢复下载
*/
- public static final int RESUME = 7;
- private static final String TAG = "DownloadSchedulers";
- private static final Object LOCK = new Object();
+ public static final int RESUME = 7;
+ private static final String TAG = "DownloadSchedulers";
+ private static final Object LOCK = new Object();
private static volatile DownloadSchedulers INSTANCE = null;
- /**
- * 超时时间
- */
- long mTimeOut = 10000;
-
/**
* 下载器任务监听
*/
Map mSchedulerListeners = new ConcurrentHashMap<>();
+ DownloadManager mManager = DownloadManager.getInstance();
ITaskQueue mQueue;
- public DownloadSchedulers(ITaskQueue downloadTaskQueue) {
- mQueue = downloadTaskQueue;
+ private DownloadSchedulers() {
+ mQueue = mManager.getTaskQueue();
}
- public static DownloadSchedulers getInstance(DownloadTaskQueue queue) {
+ public static DownloadSchedulers getInstance() {
if (INSTANCE == null) {
synchronized (LOCK) {
- INSTANCE = new DownloadSchedulers(queue);
+ //INSTANCE = new DownloadSchedulers(queue);
+ INSTANCE = new DownloadSchedulers();
}
}
return INSTANCE;
}
+ @Override public void addSchedulerListener(Object target, OnSchedulerListener schedulerListener) {
+ if (target == null) {
+ throw new IllegalArgumentException("target 不能为null");
+ }
+ String name = target.getClass().getName();
+ if (mSchedulerListeners.get(name) != null) {
+ Log.w(TAG, "监听器已存在");
+ return;
+ }
+ mSchedulerListeners.put(name, schedulerListener);
+ }
+
+ @Override
+ public void removeSchedulerListener(Object target, OnSchedulerListener schedulerListener) {
+ if (target == null) {
+ throw new IllegalArgumentException("target 不能为null");
+ }
+ //OnSchedulerListener listener = mSchedulerListeners.get(target.getClass().getName());
+ //mSchedulerListeners.remove(listener);
+ //该内存溢出解决方案:http://stackoverflow.com/questions/14585829/how-safe-is-to-delete-already-removed-concurrenthashmap-element
+ for (Iterator> iter =
+ mSchedulerListeners.entrySet().iterator(); iter.hasNext(); ) {
+ Map.Entry entry = iter.next();
+ if (entry.getKey().equals(target.getClass().getName())) iter.remove();
+ }
+ }
+
@Override public boolean handleMessage(Message msg) {
Task task = (Task) msg.obj;
if (task == null) {
@@ -104,6 +128,7 @@ public class DownloadSchedulers implements IDownloadSchedulers {
switch (msg.what) {
case STOP:
case CANCEL:
+ mQueue.removeTask(entity);
mQueue.removeTask(entity);
if (mQueue.size() != Configuration.getInstance().getDownloadNum()) {
startNextTask(entity);
@@ -205,26 +230,4 @@ public class DownloadSchedulers implements IDownloadSchedulers {
mQueue.startTask(newTask);
}
}
-
- @Override public void addSchedulerListener(Object target, OnSchedulerListener schedulerListener) {
- if (target == null) {
- throw new IllegalArgumentException("target 不能为null");
- }
- String name = target.getClass().getName();
- if (mSchedulerListeners.get(name) != null) {
- Log.w(TAG, "监听器已存在");
- return;
- }
- mSchedulerListeners.put(name, schedulerListener);
- }
-
- @Override
- public void removeSchedulerListener(Object target, OnSchedulerListener schedulerListener) {
- if (target == null) {
- throw new IllegalArgumentException("target 不能为null");
- }
- OnSchedulerListener listener = mSchedulerListeners.get(target.getClass().getName());
- mSchedulerListeners.remove(listener);
- listener = null;
- }
-}
\ No newline at end of file
+}
diff --git a/Aria/src/main/java/com/arialyy/aria/core/task/DownloadListener.java b/Aria/src/main/java/com/arialyy/aria/core/task/DownloadListener.java
index a0e9b8b8..bab38eae 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/task/DownloadListener.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/task/DownloadListener.java
@@ -23,6 +23,10 @@ class DownloadListener implements IDownloadListener {
}
+ @Override public void supportBreakpoint(boolean support) {
+
+ }
+
@Override public void onCancel() {
}
diff --git a/Aria/src/main/java/com/arialyy/aria/core/task/DownloadUtil.java b/Aria/src/main/java/com/arialyy/aria/core/task/DownloadUtil.java
index 987d2b1f..481d9b09 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/task/DownloadUtil.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/task/DownloadUtil.java
@@ -29,10 +29,7 @@ import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
-import java.util.List;
-import java.util.Map;
import java.util.Properties;
-import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -40,7 +37,7 @@ import java.util.concurrent.Executors;
* Created by lyy on 2015/8/25.
* 下载工具类
*/
-final class DownloadUtil implements IDownloadUtil {
+final class DownloadUtil implements IDownloadUtil, Runnable {
private static final String TAG = "DownloadUtil";
private static final Object LOCK = new Object();
/**
@@ -49,22 +46,26 @@ final class DownloadUtil implements IDownloadUtil {
private final int THREAD_NUM;
//下载监听
private IDownloadListener mListener;
- private int mConnectTimeOut = 5000 * 4; //连接超时时间
- private int mReadTimeOut = 5000 * 20; //流读取的超时时间
+ private int mConnectTimeOut = 5000 * 4; //连接超时时间
+ private int mReadTimeOut = 5000 * 20; //流读取的超时时间
/**
* 已经完成下载任务的线程数量
*/
- private int mCompleteThreadNum = 0;
- private boolean isDownloading = false;
- private boolean isStop = false;
- private boolean isCancel = false;
- private boolean isNewTask = true;
- private int mCancelNum = 0;
- private long mCurrentLocation = 0;
- private int mStopNum = 0;
+ private boolean isDownloading = false;
+ private boolean isStop = false;
+ private boolean isCancel = false;
+ private boolean isNewTask = true;
+ private boolean isSupportBreakpoint = true;
+ private int mCompleteThreadNum = 0;
+ private int mCancelNum = 0;
+ private long mCurrentLocation = 0;
+ private int mStopNum = 0;
+ private int mFailNum = 0;
private Context mContext;
private DownloadEntity mDownloadEntity;
private ExecutorService mFixedThreadPool;
+ private File mDownloadFile; //下载的文件
+ private File mConfigFile;//下载信息配置文件
private SparseArray mTask = new SparseArray<>();
DownloadUtil(Context context, DownloadEntity entity, IDownloadListener downloadListener) {
@@ -78,6 +79,25 @@ final class DownloadUtil implements IDownloadUtil {
mListener = downloadListener;
THREAD_NUM = threadNum;
mFixedThreadPool = Executors.newFixedThreadPool(Integer.MAX_VALUE);
+ init();
+ }
+
+ private void init() {
+ mDownloadFile = new File(mDownloadEntity.getDownloadPath());
+ //读取已完成的线程数
+ mConfigFile = new File(
+ mContext.getFilesDir().getPath() + "/temp/" + 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() {
@@ -117,7 +137,7 @@ final class DownloadUtil implements IDownloadUtil {
isDownloading = false;
mFixedThreadPool.shutdown();
for (int i = 0; i < THREAD_NUM; i++) {
- DownLoadTask task = (DownLoadTask) mTask.get(i);
+ SingleThreadTask task = (SingleThreadTask) mTask.get(i);
if (task != null) {
task.cancel();
}
@@ -132,7 +152,7 @@ final class DownloadUtil implements IDownloadUtil {
isDownloading = false;
mFixedThreadPool.shutdown();
for (int i = 0; i < THREAD_NUM; i++) {
- DownLoadTask task = (DownLoadTask) mTask.get(i);
+ SingleThreadTask task = (SingleThreadTask) mTask.get(i);
if (task != null) {
task.stop();
}
@@ -175,148 +195,9 @@ final class DownloadUtil implements IDownloadUtil {
isCancel = false;
mCancelNum = 0;
mStopNum = 0;
- final String filePath = mDownloadEntity.getDownloadPath();
- final String downloadUrl = mDownloadEntity.getDownloadUrl();
- final File dFile = new File(filePath);
- //读取已完成的线程数
- final File configFile =
- new File(mContext.getFilesDir().getPath() + "/temp/" + dFile.getName() + ".properties");
- try {
- if (!configFile.exists()) { //记录文件被删除,则重新下载
- isNewTask = true;
- CommonUtil.createFile(configFile.getPath());
- } else {
- isNewTask = !dFile.exists();
- }
- } catch (Exception e) {
- e.printStackTrace();
- failDownload("下载失败,记录文件被删除");
- return;
- }
+ mFailNum = 0;
mListener.onPre();
- new Thread(new Runnable() {
- @Override public void run() {
- try {
- URL url = new URL(downloadUrl);
- HttpURLConnection conn = (HttpURLConnection) url.openConnection();
- setConnectParam(conn);
- conn.setRequestProperty("Range", "bytes=" + 0 + "-");
- conn.setConnectTimeout(mConnectTimeOut * 4);
- conn.connect();
- 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) {
- int fileLength = conn.getContentLength();
- //必须建一个文件
- CommonUtil.createFile(filePath);
- RandomAccessFile file = new RandomAccessFile(filePath, "rwd");
- //设置文件长度
- file.setLength(fileLength);
- mListener.onPostPre(fileLength);
- //分配每条线程的下载区间
- Properties pro = null;
- pro = CommonUtil.loadConfig(configFile);
- if (pro.isEmpty()) {
- isNewTask = true;
- } else {
- for (int i = 0; i < THREAD_NUM; i++) {
- if (pro.getProperty(dFile.getName() + "_record_" + i) == null) {
- Object state = pro.getProperty(dFile.getName() + "_state_" + i);
- if (state != null && Integer.parseInt(state + "") == 1) {
- continue;
- }
- isNewTask = true;
- break;
- }
- }
- }
- 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(dFile.getName() + "_state_" + i);
- if (state != null && Integer.parseInt(state + "") == 1) { //该线程已经完成
- mCurrentLocation += endL - startL;
- Log.d(TAG, "++++++++++ 线程_" + i + "_已经下载完成 ++++++++++");
- mCompleteThreadNum++;
- mStopNum++;
- mCancelNum++;
- if (mCompleteThreadNum == THREAD_NUM) {
- if (configFile.exists()) {
- configFile.delete();
- }
- mListener.onComplete();
- isDownloading = false;
- return;
- }
- continue;
- }
- //分配下载位置
- Object record = pro.getProperty(dFile.getName() + "_record_" + i);
- if (!isNewTask
- && record != null
- && Long.parseLong(record + "") > 0) { //如果有记录,则恢复下载
- Long r = Long.parseLong(record + "");
- mCurrentLocation += r - startL;
- Log.d(TAG, "++++++++++ 线程_" + i + "_恢复下载 ++++++++++");
- mListener.onChildResume(r);
- startL = r;
- recordL[rl] = i;
- rl++;
- } else {
- isNewTask = true;
- }
- if (isNewTask) {
- recordL[rl] = i;
- rl++;
- }
- if (i == (THREAD_NUM - 1)) {
- //如果整个文件的大小不为线程个数的整数倍,则最后一个线程的结束位置即为文件的总长度
- endL = fileLength;
- }
- ConfigEntity entity =
- new ConfigEntity(mContext, fileLength, downloadUrl, dFile, i, startL, endL);
- DownLoadTask task = new DownLoadTask(entity);
- mTask.put(i, task);
- }
- if (mCurrentLocation > 0) {
- mListener.onResume(mCurrentLocation);
- } else {
- mListener.onStart(mCurrentLocation);
- }
- for (int l : recordL) {
- if (l == -1) continue;
- Runnable task = mTask.get(l);
- if (task != null && !mFixedThreadPool.isShutdown()) {
- mFixedThreadPool.execute(task);
- }
- }
- } else if (code == HttpURLConnection.HTTP_OK) {
- //在conn.setRequestProperty("Range", "bytes=" + 0 + "-");下,200为不支持断点状态
- Log.w(TAG, "该下载链接不支持断点下载");
- } else {
- failDownload("下载失败,返回码:" + code);
- }
- } catch (IOException e) {
- failDownload("下载失败【downloadUrl:"
- + downloadUrl
- + "】\n【filePath:"
- + filePath
- + "】"
- + CommonUtil.getPrintException(e));
- }
- }
- }).start();
+ new Thread(this).start();
}
@Override public void resumeDownload() {
@@ -339,6 +220,157 @@ final class DownloadUtil implements IDownloadUtil {
"image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
}
+ @Override public void run() {
+ try {
+ URL url = new URL(mDownloadEntity.getDownloadUrl());
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ setConnectParam(conn);
+ conn.setRequestProperty("Range", "bytes=" + 0 + "-");
+ conn.setConnectTimeout(mConnectTimeOut * 4);
+ conn.connect();
+ 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) {
+ isSupportBreakpoint = true;
+ mListener.supportBreakpoint(true);
+ handleBreakpoint(conn);
+ } else if (code == HttpURLConnection.HTTP_OK) {
+ //在conn.setRequestProperty("Range", "bytes=" + 0 + "-");下,200为不支持断点状态
+ mListener.supportBreakpoint(false);
+ Log.w(TAG, "该下载链接不支持断点下载");
+ handleBreakpoint(conn);
+ } else {
+ failDownload("下载失败,返回码:" + code);
+ }
+ } catch (IOException e) {
+ failDownload("下载失败【downloadUrl:"
+ + mDownloadEntity.getDownloadUrl()
+ + "】\n【filePath:"
+ + mDownloadFile.getPath()
+ + "】"
+ + CommonUtil.getPrintException(e));
+ }
+ }
+
+ /**
+ * 处理断点
+ */
+ private void handleBreakpoint(HttpURLConnection conn) throws IOException {
+ //不支持断点只能单线程下载
+ if (!isSupportBreakpoint) {
+ ConfigEntity entity = new ConfigEntity();
+ entity.fileSize = conn.getContentLength();
+ entity.downloadUrl = mDownloadEntity.getDownloadUrl();
+ entity.tempFile = mDownloadFile;
+ entity.threadId = 0;
+ entity.startLocation = 0;
+ entity.endLocation = entity.fileSize;
+ SingleThreadTask task = new SingleThreadTask(entity);
+ mFixedThreadPool.execute(task);
+ mListener.onStart(0);
+ return;
+ }
+ int fileLength = conn.getContentLength();
+ //必须建一个文件
+ CommonUtil.createFile(mDownloadFile.getPath());
+ RandomAccessFile file = new RandomAccessFile(mDownloadFile.getPath(), "rwd");
+ //设置文件长度
+ file.setLength(fileLength);
+ mListener.onPostPre(fileLength);
+ //分配每条线程的下载区间
+ Properties pro = null;
+ pro = CommonUtil.loadConfig(mConfigFile);
+ if (pro.isEmpty()) {
+ isNewTask = true;
+ } else {
+ 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;
+ }
+ isNewTask = true;
+ break;
+ }
+ }
+ }
+ 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) { //该线程已经完成
+ mCurrentLocation += endL - startL;
+ Log.d(TAG, "++++++++++ 线程_" + i + "_已经下载完成 ++++++++++");
+ mCompleteThreadNum++;
+ mStopNum++;
+ mCancelNum++;
+ if (mCompleteThreadNum == THREAD_NUM) {
+ if (mConfigFile.exists()) {
+ mConfigFile.delete();
+ }
+ mListener.onComplete();
+ isDownloading = false;
+ return;
+ }
+ continue;
+ }
+ //分配下载位置
+ Object record = pro.getProperty(mDownloadFile.getName() + "_record_" + i);
+ //如果有记录,则恢复下载
+ if (!isNewTask && record != null && Long.parseLong(record + "") > 0) {
+ Long r = Long.parseLong(record + "");
+ mCurrentLocation += r - startL;
+ Log.d(TAG, "++++++++++ 线程_" + i + "_恢复下载 ++++++++++");
+ mListener.onChildResume(r);
+ startL = r;
+ recordL[rl] = i;
+ rl++;
+ } else {
+ isNewTask = true;
+ }
+ if (isNewTask) {
+ recordL[rl] = i;
+ rl++;
+ }
+ if (i == (THREAD_NUM - 1)) {
+ //如果整个文件的大小不为线程个数的整数倍,则最后一个线程的结束位置即为文件的总长度
+ endL = fileLength;
+ }
+ ConfigEntity entity = new ConfigEntity();
+ entity.fileSize = fileLength;
+ entity.downloadUrl = mDownloadEntity.getDownloadUrl();
+ entity.tempFile = mDownloadFile;
+ entity.threadId = i;
+ entity.startLocation = startL;
+ entity.endLocation = endL;
+ SingleThreadTask task = new SingleThreadTask(entity);
+ mTask.put(i, task);
+ }
+ if (mCurrentLocation > 0) {
+ mListener.onResume(mCurrentLocation);
+ } else {
+ mListener.onStart(mCurrentLocation);
+ }
+ for (int l : recordL) {
+ if (l == -1) continue;
+ Runnable task = mTask.get(l);
+ if (task != null && !mFixedThreadPool.isShutdown()) {
+ mFixedThreadPool.execute(task);
+ }
+ }
+ }
+
/**
* 子线程下载信息类
*/
@@ -350,68 +382,62 @@ final class DownloadUtil implements IDownloadUtil {
long startLocation;
long endLocation;
File tempFile;
- Context context;
-
- private ConfigEntity(Context context, long fileSize, String downloadUrl, File file,
- int threadId, long startLocation, long endLocation) {
- this.fileSize = fileSize;
- this.downloadUrl = downloadUrl;
- this.tempFile = file;
- this.threadId = threadId;
- this.startLocation = startLocation;
- this.endLocation = endLocation;
- this.context = context;
- }
}
/**
* 单个线程的下载任务
*/
- private class DownLoadTask implements Runnable {
- private static final String TAG = "DownLoadTask";
- private ConfigEntity dEntity;
+ private class SingleThreadTask implements Runnable {
+ private static final String TAG = "SingleThreadTask";
+ private ConfigEntity configEntity;
private String configFPath;
private long currentLocation = 0;
- private DownLoadTask(ConfigEntity downloadInfo) {
- this.dEntity = downloadInfo;
- configFPath = dEntity.context.getFilesDir().getPath()
- + "/temp/"
- + dEntity.tempFile.getName()
- + ".properties";
+ private SingleThreadTask(ConfigEntity downloadInfo) {
+ this.configEntity = downloadInfo;
+ if (isSupportBreakpoint) {
+ configFPath = mContext.getFilesDir().getPath()
+ + "/temp/"
+ + configEntity.tempFile.getName()
+ + ".properties";
+ }
}
@Override public void run() {
HttpURLConnection conn = null;
InputStream is = null;
try {
- Log.d(TAG, "线程_"
- + dEntity.threadId
- + "_正在下载【开始位置 : "
- + dEntity.startLocation
- + ",结束位置:"
- + dEntity.endLocation
- + "】");
- URL url = new URL(dEntity.downloadUrl);
+ URL url = new URL(configEntity.downloadUrl);
conn = (HttpURLConnection) url.openConnection();
- //在头里面请求下载开始位置和结束位置
- conn.setRequestProperty("Range",
- "bytes=" + dEntity.startLocation + "-" + dEntity.endLocation);
+ if (isSupportBreakpoint) {
+ Log.d(TAG, "线程_"
+ + configEntity.threadId
+ + "_正在下载【开始位置 : "
+ + configEntity.startLocation
+ + ",结束位置:"
+ + configEntity.endLocation
+ + "】");
+ //在头里面请求下载开始位置和结束位置
+ conn.setRequestProperty("Range",
+ "bytes=" + configEntity.startLocation + "-" + configEntity.endLocation);
+ } else {
+ Log.w(TAG, "该下载不支持断点,即将重新下载");
+ }
setConnectParam(conn);
conn.setConnectTimeout(mConnectTimeOut);
conn.setReadTimeout(mReadTimeOut); //设置读取流的等待时间,必须设置该参数
is = conn.getInputStream();
//创建可设置位置的文件
- RandomAccessFile file = new RandomAccessFile(dEntity.tempFile, "rwd");
+ RandomAccessFile file = new RandomAccessFile(configEntity.tempFile, "rwd");
//设置每条线程写入文件的位置
- file.seek(dEntity.startLocation);
+ file.seek(configEntity.startLocation);
byte[] buffer = new byte[1024];
int len;
//当前子线程的下载位置
- currentLocation = dEntity.startLocation;
+ currentLocation = configEntity.startLocation;
while ((len = is.read(buffer)) != -1) {
if (isCancel) {
- Log.d(TAG, "++++++++++ thread_" + dEntity.threadId + "_cancel ++++++++++");
+ Log.d(TAG, "++++++++++ thread_" + configEntity.threadId + "_cancel ++++++++++");
break;
}
if (isStop) {
@@ -420,7 +446,6 @@ final class DownloadUtil implements IDownloadUtil {
//把下载数据数据写入文件
file.write(buffer, 0, len);
progress(len);
- currentLocation += len;
}
file.close();
//close 为阻塞的,需要使用线程池来处理
@@ -428,32 +453,40 @@ final class DownloadUtil implements IDownloadUtil {
conn.disconnect();
if (isCancel) {
- cancel();
return;
}
//停止状态不需要删除记录文件
if (isStop) {
- //stop();
return;
}
- Log.i(TAG, "线程【" + dEntity.threadId + "】下载完毕");
- writeConfig(dEntity.tempFile.getName() + "_state_" + dEntity.threadId, 1 + "");
- mListener.onChildComplete(dEntity.endLocation);
- mCompleteThreadNum++;
- if (mCompleteThreadNum == THREAD_NUM) {
- File configFile = new File(configFPath);
- if (configFile.exists()) {
- configFile.delete();
+ //支持断点的处理
+ if (isSupportBreakpoint) {
+ Log.i(TAG, "线程【" + configEntity.threadId + "】下载完毕");
+ writeConfig(configEntity.tempFile.getName() + "_state_" + configEntity.threadId, 1 + "");
+ mListener.onChildComplete(configEntity.endLocation);
+ mCompleteThreadNum++;
+ if (mCompleteThreadNum == THREAD_NUM) {
+ File configFile = new File(configFPath);
+ if (configFile.exists()) {
+ configFile.delete();
+ }
+ isDownloading = false;
+ mListener.onComplete();
}
+ } else {
+ Log.i(TAG, "下载任务完成");
isDownloading = false;
mListener.onComplete();
}
} catch (MalformedURLException e) {
- failDownload(dEntity, currentLocation, "下载链接异常", e);
+ mFailNum++;
+ failDownload(configEntity, currentLocation, "下载链接异常", e);
} catch (IOException e) {
- failDownload(dEntity, currentLocation, "下载失败【" + dEntity.downloadUrl + "】", e);
+ mFailNum++;
+ failDownload(configEntity, currentLocation, "下载失败【" + configEntity.downloadUrl + "】", e);
} catch (Exception e) {
- failDownload(dEntity, currentLocation, "获取流失败", e);
+ mFailNum++;
+ failDownload(configEntity, currentLocation, "获取流失败", e);
}
}
@@ -463,11 +496,19 @@ final class DownloadUtil implements IDownloadUtil {
protected void stop() {
synchronized (LOCK) {
try {
- mStopNum++;
- String location = String.valueOf(currentLocation);
- Log.i(TAG, "thread_" + dEntity.threadId + "_stop, stop location ==> " + currentLocation);
- writeConfig(dEntity.tempFile.getName() + "_record_" + dEntity.threadId, location);
- if (mStopNum == THREAD_NUM) {
+ if (isSupportBreakpoint) {
+ mStopNum++;
+ String location = String.valueOf(currentLocation);
+ Log.i(TAG,
+ "thread_" + configEntity.threadId + "_stop, stop location ==> " + currentLocation);
+ writeConfig(configEntity.tempFile.getName() + "_record_" + configEntity.threadId,
+ location);
+ if (mStopNum == THREAD_NUM) {
+ Log.d(TAG, "++++++++++++++++ onStop +++++++++++++++++");
+ isDownloading = false;
+ mListener.onStop(mCurrentLocation);
+ }
+ } else {
Log.d(TAG, "++++++++++++++++ onStop +++++++++++++++++");
isDownloading = false;
mListener.onStop(mCurrentLocation);
@@ -479,26 +520,8 @@ final class DownloadUtil implements IDownloadUtil {
}
/**
- * 取消下载
+ * 下载中
*/
- private void cancel() {
- synchronized (LOCK) {
- mCancelNum++;
- if (mCancelNum == THREAD_NUM) {
- File configFile = new File(configFPath);
- if (configFile.exists()) {
- configFile.delete();
- }
- if (dEntity.tempFile.exists()) {
- dEntity.tempFile.delete();
- }
- Log.d(TAG, "++++++++++++++++ onCancel +++++++++++++++++");
- isDownloading = false;
- mListener.onCancel();
- }
- }
- }
-
private void progress(long len) {
synchronized (LOCK) {
mCurrentLocation += len;
@@ -507,13 +530,30 @@ final class DownloadUtil implements IDownloadUtil {
}
/**
- * 将记录写入到配置文件
+ * 取消下载
*/
- private void writeConfig(String key, String record) throws IOException {
- File configFile = new File(configFPath);
- Properties pro = CommonUtil.loadConfig(configFile);
- pro.setProperty(key, record);
- CommonUtil.saveConfig(configFile, pro);
+ private void cancel() {
+ synchronized (LOCK) {
+ if (isSupportBreakpoint) {
+ mCancelNum++;
+ if (mCancelNum == THREAD_NUM) {
+ File configFile = new File(configFPath);
+ if (configFile.exists()) {
+ configFile.delete();
+ }
+ if (configEntity.tempFile.exists()) {
+ configEntity.tempFile.delete();
+ }
+ Log.d(TAG, "++++++++++++++++ onCancel +++++++++++++++++");
+ isDownloading = false;
+ mListener.onCancel();
+ }
+ } else {
+ Log.d(TAG, "++++++++++++++++ onCancel +++++++++++++++++");
+ isDownloading = false;
+ mListener.onCancel();
+ }
+ }
}
/**
@@ -525,19 +565,36 @@ final class DownloadUtil implements IDownloadUtil {
try {
isDownloading = false;
isStop = true;
- Log.e(TAG, msg);
if (ex != null) {
Log.e(TAG, CommonUtil.getPrintException(ex));
}
- if (currentLocation != -1) {
- String location = String.valueOf(currentLocation);
- writeConfig(dEntity.tempFile.getName() + "_record_" + dEntity.threadId, location);
+ if (isSupportBreakpoint) {
+ if (currentLocation != -1) {
+ String location = String.valueOf(currentLocation);
+ writeConfig(dEntity.tempFile.getName() + "_record_" + dEntity.threadId, location);
+ }
+ if (mFailNum == THREAD_NUM) {
+ Log.d(TAG, "++++++++++++++++ onFail +++++++++++++++++");
+ mListener.onFail();
+ }
+ } else {
+ Log.d(TAG, "++++++++++++++++ onFail +++++++++++++++++");
+ mListener.onFail();
}
- mListener.onFail();
} catch (IOException e) {
e.printStackTrace();
}
}
}
+
+ /**
+ * 将记录写入到配置文件
+ */
+ private void writeConfig(String key, String record) throws IOException {
+ File configFile = new File(configFPath);
+ Properties pro = CommonUtil.loadConfig(configFile);
+ pro.setProperty(key, record);
+ CommonUtil.saveConfig(configFile, pro);
+ }
}
}
\ No newline at end of file
diff --git a/Aria/src/main/java/com/arialyy/aria/core/task/IDownloadListener.java b/Aria/src/main/java/com/arialyy/aria/core/task/IDownloadListener.java
index 78a5d604..037bd808 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/task/IDownloadListener.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/task/IDownloadListener.java
@@ -14,13 +14,20 @@
* limitations under the License.
*/
-
package com.arialyy.aria.core.task;
/**
* 下载监听
*/
public interface IDownloadListener {
+
+ /**
+ * 支持断点回调
+ *
+ * @param support true,支持;false 不支持
+ */
+ public void supportBreakpoint(boolean support);
+
/**
* 取消下载
*/
diff --git a/Aria/src/main/java/com/arialyy/aria/core/task/Task.java b/Aria/src/main/java/com/arialyy/aria/core/task/Task.java
index 8b29994e..141fd8d0 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/task/Task.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/task/Task.java
@@ -18,14 +18,16 @@ package com.arialyy.aria.core.task;
import android.content.Context;
import android.content.Intent;
-import android.net.Uri;
import android.os.Handler;
import android.util.Log;
+import com.arialyy.aria.core.Aria;
import com.arialyy.aria.core.DownloadManager;
import com.arialyy.aria.core.scheduler.DownloadSchedulers;
import com.arialyy.aria.core.scheduler.IDownloadSchedulers;
import com.arialyy.aria.core.DownloadEntity;
+import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.Configuration;
+import java.lang.ref.WeakReference;
/**
* Created by lyy on 2016/8/11.
@@ -43,14 +45,15 @@ public class Task {
private Context mContext;
private IDownloadUtil mUtil;
- private Task(Context context, DownloadEntity entity) {
+ private Task(Context context, DownloadEntity entity, Handler outHandler) {
mContext = context.getApplicationContext();
mEntity = entity;
+ mOutHandler = outHandler;
init();
}
private void init() {
- mListener = new DListener(mContext, mEntity, mOutHandler);
+ mListener = new DListener(mContext, this, mOutHandler);
mUtil = new DownloadUtil(mContext, mEntity, mListener);
}
@@ -62,7 +65,7 @@ public class Task {
Log.d(TAG, "任务正在下载");
} else {
if (mListener == null) {
- mListener = new DListener(mContext, mEntity, mOutHandler);
+ mListener = new DListener(mContext, this, mOutHandler);
}
mUtil.startDownload();
}
@@ -89,12 +92,13 @@ public class Task {
} else {
mEntity.setState(DownloadEntity.STATE_STOP);
mEntity.save();
- sendInState2Target(DownloadSchedulers.STOP);
-
+ if (mOutHandler != null) {
+ mOutHandler.obtainMessage(DownloadSchedulers.STOP, this).sendToTarget();
+ }
// 发送停止下载的广播
- Intent intent = createIntent(DownloadManager.ACTION_STOP);
- intent.putExtra(DownloadManager.CURRENT_LOCATION, mEntity.getCurrentProgress());
- intent.putExtra(DownloadManager.ENTITY, mEntity);
+ Intent intent = CommonUtil.createIntent(mContext.getPackageName(), Aria.ACTION_STOP);
+ intent.putExtra(Aria.CURRENT_LOCATION, mEntity.getCurrentProgress());
+ intent.putExtra(Aria.ENTITY, mEntity);
mContext.sendBroadcast(intent);
}
}
@@ -125,38 +129,16 @@ public class Task {
mUtil.delConfigFile();
mUtil.delTempFile();
mEntity.deleteData();
- sendInState2Target(DownloadSchedulers.CANCEL);
-
+ if (mOutHandler != null) {
+ mOutHandler.obtainMessage(DownloadSchedulers.CANCEL, this).sendToTarget();
+ }
//发送取消下载的广播
- Intent intent = createIntent(DownloadManager.ACTION_CANCEL);
- intent.putExtra(DownloadManager.ENTITY, mEntity);
+ Intent intent = CommonUtil.createIntent(mContext.getPackageName(), Aria.ACTION_CANCEL);
+ intent.putExtra(Aria.ENTITY, mEntity);
mContext.sendBroadcast(intent);
}
}
- /**
- * 创建特定的Intent
- */
- private Intent createIntent(String action) {
- Uri.Builder builder = new Uri.Builder();
- builder.scheme(mContext.getPackageName());
- Uri uri = builder.build();
- Intent intent = new Intent(action);
- intent.setData(uri);
- return intent;
- }
-
- /**
- * 将任务状态发送给下载器
- *
- * @param state {@link DownloadSchedulers#START}
- */
- private void sendInState2Target(int state) {
- if (mOutHandler != null) {
- mOutHandler.obtainMessage(state, this).sendToTarget();
- }
- }
-
static class Builder {
DownloadEntity downloadEntity;
Handler outHandler;
@@ -204,8 +186,7 @@ public class Task {
//}
public Task build() {
- Task task = new Task(context, downloadEntity);
- task.mOutHandler = outHandler;
+ Task task = new Task(context, downloadEntity, outHandler);
task.setTargetName(targetName);
downloadEntity.save();
return task;
@@ -215,10 +196,11 @@ public class Task {
/**
* 下载监听类
*/
- private class DListener extends DownloadListener {
- Handler outHandler;
- Context context;
- Intent sendIntent;
+ private static class DListener extends DownloadListener {
+ WeakReference outHandler;
+ WeakReference task;
+ Context context;
+ Intent sendIntent;
long INTERVAL = 1024 * 10; //10k大小的间隔
long lastLen = 0; //上一次发送长度
long lastTime = 0;
@@ -226,18 +208,19 @@ public class Task {
boolean isFirst = true;
DownloadEntity downloadEntity;
- DListener(Context context, DownloadEntity downloadEntity, Handler outHandler) {
+ DListener(Context context, Task task, Handler outHandler) {
this.context = context;
- this.outHandler = outHandler;
- this.downloadEntity = downloadEntity;
- sendIntent = createIntent(DownloadManager.ACTION_RUNNING);
- sendIntent.putExtra(DownloadManager.ENTITY, downloadEntity);
+ this.outHandler = new WeakReference<>(outHandler);
+ this.task = new WeakReference<>(task);
+ this.downloadEntity = this.task.get().getDownloadEntity();
+ sendIntent = CommonUtil.createIntent(context.getPackageName(), Aria.ACTION_RUNNING);
+ sendIntent.putExtra(Aria.ENTITY, downloadEntity);
}
@Override public void onPre() {
super.onPre();
downloadEntity.setState(DownloadEntity.STATE_PRE);
- sendIntent(DownloadManager.ACTION_PRE, -1);
+ sendIntent(Aria.ACTION_PRE, -1);
}
@Override public void onPostPre(long fileSize) {
@@ -245,14 +228,14 @@ public class Task {
downloadEntity.setFileSize(fileSize);
downloadEntity.setState(DownloadEntity.STATE_POST_PRE);
sendInState2Target(DownloadSchedulers.PRE);
- sendIntent(DownloadManager.ACTION_POST_PRE, -1);
+ sendIntent(Aria.ACTION_POST_PRE, -1);
}
@Override public void onResume(long resumeLocation) {
super.onResume(resumeLocation);
downloadEntity.setState(DownloadEntity.STATE_DOWNLOAD_ING);
sendInState2Target(DownloadSchedulers.RESUME);
- sendIntent(DownloadManager.ACTION_RESUME, resumeLocation);
+ sendIntent(Aria.ACTION_RESUME, resumeLocation);
}
@Override public void onStart(long startLocation) {
@@ -260,15 +243,15 @@ public class Task {
downloadEntity.setState(DownloadEntity.STATE_DOWNLOAD_ING);
downloadEntity.setFailNum(0);
sendInState2Target(DownloadSchedulers.START);
- sendIntent(DownloadManager.ACTION_START, startLocation);
+ 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(DownloadManager.CURRENT_LOCATION, currentLocation);
- sendIntent.putExtra(DownloadManager.CURRENT_SPEED, speed);
+ sendIntent.putExtra(Aria.CURRENT_LOCATION, currentLocation);
+ sendIntent.putExtra(Aria.CURRENT_SPEED, speed);
lastTime = System.currentTimeMillis();
if (isFirst) {
downloadEntity.setSpeed(0);
@@ -288,14 +271,14 @@ public class Task {
downloadEntity.setState(DownloadEntity.STATE_STOP);
downloadEntity.setSpeed(0);
sendInState2Target(DownloadSchedulers.STOP);
- sendIntent(DownloadManager.ACTION_STOP, stopLocation);
+ sendIntent(Aria.ACTION_STOP, stopLocation);
}
@Override public void onCancel() {
super.onCancel();
downloadEntity.setState(DownloadEntity.STATE_CANCEL);
sendInState2Target(DownloadSchedulers.CANCEL);
- sendIntent(DownloadManager.ACTION_CANCEL, -1);
+ sendIntent(Aria.ACTION_CANCEL, -1);
downloadEntity.deleteData();
}
@@ -305,7 +288,7 @@ public class Task {
downloadEntity.setDownloadComplete(true);
downloadEntity.setSpeed(0);
sendInState2Target(DownloadSchedulers.COMPLETE);
- sendIntent(DownloadManager.ACTION_COMPLETE, downloadEntity.getFileSize());
+ sendIntent(Aria.ACTION_COMPLETE, downloadEntity.getFileSize());
}
@Override public void onFail() {
@@ -314,17 +297,28 @@ public class Task {
downloadEntity.setState(DownloadEntity.STATE_FAIL);
downloadEntity.setSpeed(0);
sendInState2Target(DownloadSchedulers.FAIL);
- sendIntent(DownloadManager.ACTION_FAIL, -1);
+ sendIntent(Aria.ACTION_FAIL, -1);
+ }
+
+ /**
+ * 将任务状态发送给下载器
+ *
+ * @param state {@link DownloadSchedulers#START}
+ */
+ private void sendInState2Target(int state) {
+ if (outHandler.get() != null) {
+ outHandler.get().obtainMessage(state, task.get()).sendToTarget();
+ }
}
private void sendIntent(String action, long location) {
- downloadEntity.setDownloadComplete(action.equals(DownloadManager.ACTION_COMPLETE));
+ downloadEntity.setDownloadComplete(action.equals(Aria.ACTION_COMPLETE));
downloadEntity.setCurrentProgress(location);
downloadEntity.update();
- Intent intent = createIntent(action);
- intent.putExtra(DownloadManager.ENTITY, downloadEntity);
+ Intent intent = CommonUtil.createIntent(context.getPackageName(), action);
+ intent.putExtra(Aria.ENTITY, downloadEntity);
if (location != -1) {
- intent.putExtra(DownloadManager.CURRENT_LOCATION, location);
+ intent.putExtra(Aria.CURRENT_LOCATION, location);
}
if (Configuration.isOpenBreadCast) {
context.sendBroadcast(intent);
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 bdcf94c2..612078b3 100644
--- a/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java
+++ b/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java
@@ -17,7 +17,9 @@
package com.arialyy.aria.util;
import android.content.Context;
+import android.content.Intent;
import android.content.SharedPreferences;
+import android.net.Uri;
import android.util.Log;
import com.arialyy.aria.core.command.CmdFactory;
import com.arialyy.aria.core.DownloadEntity;
@@ -46,6 +48,18 @@ public class CommonUtil {
return CmdFactory.getInstance().createCmd(entity, cmd);
}
+ /**
+ * 创建隐性的Intent
+ */
+ public static Intent createIntent(String packageName, String action) {
+ Uri.Builder builder = new Uri.Builder();
+ builder.scheme(packageName);
+ Uri uri = builder.build();
+ Intent intent = new Intent(action);
+ intent.setData(uri);
+ return intent;
+ }
+
/**
* 存储字符串到配置文件
*
diff --git a/Aria/src/main/java/com/arialyy/aria/util/WeakHandler.java b/Aria/src/main/java/com/arialyy/aria/util/WeakHandler.java
new file mode 100644
index 00000000..060c5647
--- /dev/null
+++ b/Aria/src/main/java/com/arialyy/aria/util/WeakHandler.java
@@ -0,0 +1,516 @@
+/*
+ * Copyright (c) 2014 Badoo Trading Limited
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * Portions of documentation in this code are modifications based on work created and
+ * shared by Android Open Source Project and used according to terms described in the
+ * Apache License, Version 2.0
+ */
+package com.arialyy.aria.util;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+
+import java.lang.ref.WeakReference;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Memory safer implementation of android.os.Handler
+ *
+ * Original implementation of Handlers always keeps hard reference to handler in queue of execution.
+ * If you create anonymous handler and post delayed message into it, it will keep all parent class
+ * for that time in memory even if it could be cleaned.
+ *
+ * This implementation is trickier, it will keep WeakReferences to runnables and messages,
+ * and GC could collect them once WeakHandler instance is not referenced any more
+ *
+ *
+ * @see android.os.Handler
+ *
+ * Created by Dmytro Voronkevych on 17/06/2014.
+ */
+@SuppressWarnings("unused")
+public class WeakHandler {
+ private final Handler.Callback mCallback; // hard reference to Callback. We need to keep callback in memory
+ private final ExecHandler mExec;
+ private Lock mLock = new ReentrantLock();
+ @SuppressWarnings("ConstantConditions")
+ @VisibleForTesting
+ final ChainedRef mRunnables = new ChainedRef(mLock, null);
+
+ /**
+ * Default constructor associates this handler with the {@link Looper} for the
+ * current thread.
+ *
+ * If this thread does not have a looper, this handler won't be able to receive messages
+ * so an exception is thrown.
+ */
+ public WeakHandler() {
+ mCallback = null;
+ mExec = new ExecHandler();
+ }
+
+ /**
+ * Constructor associates this handler with the {@link Looper} for the
+ * current thread and takes a callback interface in which you can handle
+ * messages.
+ *
+ * If this thread does not have a looper, this handler won't be able to receive messages
+ * so an exception is thrown.
+ *
+ * @param callback The callback interface in which to handle messages, or null.
+ */
+ public WeakHandler(@Nullable Handler.Callback callback) {
+ mCallback = callback; // Hard referencing body
+ mExec = new ExecHandler(new WeakReference<>(callback)); // Weak referencing inside ExecHandler
+ }
+
+ /**
+ * Use the provided {@link Looper} instead of the default one.
+ *
+ * @param looper The looper, must not be null.
+ */
+ public WeakHandler(@NonNull Looper looper) {
+ mCallback = null;
+ mExec = new ExecHandler(looper);
+ }
+
+ /**
+ * Use the provided {@link Looper} instead of the default one and take a callback
+ * interface in which to handle messages.
+ *
+ * @param looper The looper, must not be null.
+ * @param callback The callback interface in which to handle messages, or null.
+ */
+ public WeakHandler(@NonNull Looper looper, @NonNull Handler.Callback callback) {
+ mCallback = callback;
+ mExec = new ExecHandler(looper, new WeakReference<>(callback));
+ }
+
+ /**
+ * Causes the Runnable r to be added to the message queue.
+ * The runnable will be run on the thread to which this handler is
+ * attached.
+ *
+ * @param r The Runnable that will be executed.
+ *
+ * @return Returns true if the Runnable was successfully placed in to the
+ * message queue. Returns false on failure, usually because the
+ * looper processing the message queue is exiting.
+ */
+ public final boolean post(@NonNull Runnable r) {
+ return mExec.post(wrapRunnable(r));
+ }
+
+ /**
+ * Causes the Runnable r to be added to the message queue, to be run
+ * at a specific time given by uptimeMillis.
+ * The time-base is {@link android.os.SystemClock#uptimeMillis}.
+ * The runnable will be run on the thread to which this handler is attached.
+ *
+ * @param r The Runnable that will be executed.
+ * @param uptimeMillis The absolute time at which the callback should run,
+ * using the {@link android.os.SystemClock#uptimeMillis} time-base.
+ *
+ * @return Returns true if the Runnable was successfully placed in to the
+ * message queue. Returns false on failure, usually because the
+ * looper processing the message queue is exiting. Note that a
+ * result of true does not mean the Runnable will be processed -- if
+ * the looper is quit before the delivery time of the message
+ * occurs then the message will be dropped.
+ */
+ public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
+ return mExec.postAtTime(wrapRunnable(r), uptimeMillis);
+ }
+
+ /**
+ * Causes the Runnable r to be added to the message queue, to be run
+ * at a specific time given by uptimeMillis.
+ * The time-base is {@link android.os.SystemClock#uptimeMillis}.
+ * The runnable will be run on the thread to which this handler is attached.
+ *
+ * @param r The Runnable that will be executed.
+ * @param uptimeMillis The absolute time at which the callback should run,
+ * using the {@link android.os.SystemClock#uptimeMillis} time-base.
+ *
+ * @return Returns true if the Runnable was successfully placed in to the
+ * message queue. Returns false on failure, usually because the
+ * looper processing the message queue is exiting. Note that a
+ * result of true does not mean the Runnable will be processed -- if
+ * the looper is quit before the delivery time of the message
+ * occurs then the message will be dropped.
+ *
+ * @see android.os.SystemClock#uptimeMillis
+ */
+ public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) {
+ return mExec.postAtTime(wrapRunnable(r), token, uptimeMillis);
+ }
+
+ /**
+ * Causes the Runnable r to be added to the message queue, to be run
+ * after the specified amount of time elapses.
+ * The runnable will be run on the thread to which this handler
+ * is attached.
+ *
+ * @param r The Runnable that will be executed.
+ * @param delayMillis The delay (in milliseconds) until the Runnable
+ * will be executed.
+ *
+ * @return Returns true if the Runnable was successfully placed in to the
+ * message queue. Returns false on failure, usually because the
+ * looper processing the message queue is exiting. Note that a
+ * result of true does not mean the Runnable will be processed --
+ * if the looper is quit before the delivery time of the message
+ * occurs then the message will be dropped.
+ */
+ public final boolean postDelayed(Runnable r, long delayMillis) {
+ return mExec.postDelayed(wrapRunnable(r), delayMillis);
+ }
+
+ /**
+ * Posts a message to an object that implements Runnable.
+ * Causes the Runnable r to executed on the next iteration through the
+ * message queue. The runnable will be run on the thread to which this
+ * handler is attached.
+ * This method is only for use in very special circumstances -- it
+ * can easily starve the message queue, cause ordering problems, or have
+ * other unexpected side-effects.
+ *
+ * @param r The Runnable that will be executed.
+ *
+ * @return Returns true if the message was successfully placed in to the
+ * message queue. Returns false on failure, usually because the
+ * looper processing the message queue is exiting.
+ */
+ public final boolean postAtFrontOfQueue(Runnable r) {
+ return mExec.postAtFrontOfQueue(wrapRunnable(r));
+ }
+
+ /**
+ * Remove any pending posts of Runnable r that are in the message queue.
+ */
+ public final void removeCallbacks(Runnable r) {
+ final WeakRunnable runnable = mRunnables.remove(r);
+ if (runnable != null) {
+ mExec.removeCallbacks(runnable);
+ }
+ }
+
+ /**
+ * Remove any pending posts of Runnable r with Object
+ * token that are in the message queue. If token is null,
+ * all callbacks will be removed.
+ */
+ public final void removeCallbacks(Runnable r, Object token) {
+ final WeakRunnable runnable = mRunnables.remove(r);
+ if (runnable != null) {
+ mExec.removeCallbacks(runnable, token);
+ }
+ }
+
+ /**
+ * Pushes a message onto the end of the message queue after all pending messages
+ * before the current time. It will be received in callback,
+ * in the thread attached to this handler.
+ *
+ * @return Returns true if the message was successfully placed in to the
+ * message queue. Returns false on failure, usually because the
+ * looper processing the message queue is exiting.
+ */
+ public final boolean sendMessage(Message msg) {
+ return mExec.sendMessage(msg);
+ }
+
+ /**
+ * Sends a Message containing only the what value.
+ *
+ * @return Returns true if the message was successfully placed in to the
+ * message queue. Returns false on failure, usually because the
+ * looper processing the message queue is exiting.
+ */
+ public final boolean sendEmptyMessage(int what) {
+ return mExec.sendEmptyMessage(what);
+ }
+
+ /**
+ * Sends a Message containing only the what value, to be delivered
+ * after the specified amount of time elapses.
+ * @see #sendMessageDelayed(android.os.Message, long)
+ *
+ * @return Returns true if the message was successfully placed in to the
+ * message queue. Returns false on failure, usually because the
+ * looper processing the message queue is exiting.
+ */
+ public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
+ return mExec.sendEmptyMessageDelayed(what, delayMillis);
+ }
+
+ /**
+ * Sends a Message containing only the what value, to be delivered
+ * at a specific time.
+ * @see #sendMessageAtTime(android.os.Message, long)
+ *
+ * @return Returns true if the message was successfully placed in to the
+ * message queue. Returns false on failure, usually because the
+ * looper processing the message queue is exiting.
+ */
+ public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
+ return mExec.sendEmptyMessageAtTime(what, uptimeMillis);
+ }
+
+ /**
+ * Enqueue a message into the message queue after all pending messages
+ * before (current time + delayMillis). You will receive it in
+ * callback, in the thread attached to this handler.
+ *
+ * @return Returns true if the message was successfully placed in to the
+ * message queue. Returns false on failure, usually because the
+ * looper processing the message queue is exiting. Note that a
+ * result of true does not mean the message will be processed -- if
+ * the looper is quit before the delivery time of the message
+ * occurs then the message will be dropped.
+ */
+ public final boolean sendMessageDelayed(Message msg, long delayMillis) {
+ return mExec.sendMessageDelayed(msg, delayMillis);
+ }
+
+ /**
+ * Enqueue a message into the message queue after all pending messages
+ * before the absolute time (in milliseconds) uptimeMillis.
+ * The time-base is {@link android.os.SystemClock#uptimeMillis}.
+ * You will receive it in callback, in the thread attached
+ * to this handler.
+ *
+ * @param uptimeMillis The absolute time at which the message should be
+ * delivered, using the
+ * {@link android.os.SystemClock#uptimeMillis} time-base.
+ *
+ * @return Returns true if the message was successfully placed in to the
+ * message queue. Returns false on failure, usually because the
+ * looper processing the message queue is exiting. Note that a
+ * result of true does not mean the message will be processed -- if
+ * the looper is quit before the delivery time of the message
+ * occurs then the message will be dropped.
+ */
+ public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+ return mExec.sendMessageAtTime(msg, uptimeMillis);
+ }
+
+ /**
+ * Enqueue a message at the front of the message queue, to be processed on
+ * the next iteration of the message loop. You will receive it in
+ * callback, in the thread attached to this handler.
+ * This method is only for use in very special circumstances -- it
+ * can easily starve the message queue, cause ordering problems, or have
+ * other unexpected side-effects.
+ *
+ * @return Returns true if the message was successfully placed in to the
+ * message queue. Returns false on failure, usually because the
+ * looper processing the message queue is exiting.
+ */
+ public final boolean sendMessageAtFrontOfQueue(Message msg) {
+ return mExec.sendMessageAtFrontOfQueue(msg);
+ }
+
+ /**
+ * Remove any pending posts of messages with code 'what' that are in the
+ * message queue.
+ */
+ public final void removeMessages(int what) {
+ mExec.removeMessages(what);
+ }
+
+ /**
+ * Remove any pending posts of messages with code 'what' and whose obj is
+ * 'object' that are in the message queue. If object is null,
+ * all messages will be removed.
+ */
+ public final void removeMessages(int what, Object object) {
+ mExec.removeMessages(what, object);
+ }
+
+ /**
+ * Remove any pending posts of callbacks and sent messages whose
+ * obj is token. If token is null,
+ * all callbacks and messages will be removed.
+ */
+ public final void removeCallbacksAndMessages(Object token) {
+ mExec.removeCallbacksAndMessages(token);
+ }
+
+ /**
+ * Check if there are any pending posts of messages with code 'what' in
+ * the message queue.
+ */
+ public final boolean hasMessages(int what) {
+ return mExec.hasMessages(what);
+ }
+
+ /**
+ * Check if there are any pending posts of messages with code 'what' and
+ * whose obj is 'object' in the message queue.
+ */
+ public final boolean hasMessages(int what, Object object) {
+ return mExec.hasMessages(what, object);
+ }
+
+ public final Looper getLooper() {
+ return mExec.getLooper();
+ }
+
+ private WeakRunnable wrapRunnable(@NonNull Runnable r) {
+ //noinspection ConstantConditions
+ if (r == null) {
+ throw new NullPointerException("Runnable can't be null");
+ }
+ final ChainedRef hardRef = new ChainedRef(mLock, r);
+ mRunnables.insertAfter(hardRef);
+ return hardRef.wrapper;
+ }
+
+ private static class ExecHandler extends Handler {
+ private final WeakReference mCallback;
+
+ ExecHandler() {
+ mCallback = null;
+ }
+
+ ExecHandler(WeakReference callback) {
+ mCallback = callback;
+ }
+
+ ExecHandler(Looper looper) {
+ super(looper);
+ mCallback = null;
+ }
+
+ ExecHandler(Looper looper, WeakReference callback) {
+ super(looper);
+ mCallback = callback;
+ }
+
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ if (mCallback == null) {
+ return;
+ }
+ final Handler.Callback callback = mCallback.get();
+ if (callback == null) { // Already disposed
+ return;
+ }
+ callback.handleMessage(msg);
+ }
+ }
+
+ static class WeakRunnable implements Runnable {
+ private final WeakReference mDelegate;
+ private final WeakReference mReference;
+
+ WeakRunnable(WeakReference delegate, WeakReference reference) {
+ mDelegate = delegate;
+ mReference = reference;
+ }
+
+ @Override
+ public void run() {
+ final Runnable delegate = mDelegate.get();
+ final ChainedRef reference = mReference.get();
+ if (reference != null) {
+ reference.remove();
+ }
+ if (delegate != null) {
+ delegate.run();
+ }
+ }
+ }
+
+ static class ChainedRef {
+ @Nullable
+ ChainedRef next;
+ @Nullable
+ ChainedRef prev;
+ @NonNull
+ final Runnable runnable;
+ @NonNull
+ final WeakRunnable wrapper;
+
+ @NonNull
+ Lock lock;
+
+ public ChainedRef(@NonNull Lock lock, @NonNull Runnable r) {
+ this.runnable = r;
+ this.lock = lock;
+ this.wrapper = new WeakRunnable(new WeakReference<>(r), new WeakReference<>(this));
+ }
+
+ public WeakRunnable remove() {
+ lock.lock();
+ try {
+ if (prev != null) {
+ prev.next = next;
+ }
+ if (next != null) {
+ next.prev = prev;
+ }
+ prev = null;
+ next = null;
+ } finally {
+ lock.unlock();
+ }
+ return wrapper;
+ }
+
+ public void insertAfter(@NonNull ChainedRef candidate) {
+ lock.lock();
+ try {
+ if (this.next != null) {
+ this.next.prev = candidate;
+ }
+
+ candidate.next = this.next;
+ this.next = candidate;
+ candidate.prev = this;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Nullable
+ public WeakRunnable remove(Runnable obj) {
+ lock.lock();
+ try {
+ ChainedRef curr = this.next; // Skipping head
+ while (curr != null) {
+ if (curr.runnable == obj) { // We do comparison exactly how Handler does inside
+ return curr.remove();
+ }
+ curr = curr.next;
+ }
+ } finally {
+ lock.unlock();
+ }
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/arialyy/simple/activity/SingleTaskActivity.java b/app/src/main/java/com/arialyy/simple/activity/SingleTaskActivity.java
index e1ba249c..29c6c1dd 100644
--- a/app/src/main/java/com/arialyy/simple/activity/SingleTaskActivity.java
+++ b/app/src/main/java/com/arialyy/simple/activity/SingleTaskActivity.java
@@ -64,7 +64,7 @@ public class SingleTaskActivity extends BaseActivity {
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- if (action.equals(DownloadManager.ACTION_START)) {
+ if (action.equals(Aria.ACTION_START)) {
L.d("START");
}
}
diff --git a/app/src/main/java/com/arialyy/simple/module/DownloadModule.java b/app/src/main/java/com/arialyy/simple/module/DownloadModule.java
index 9b4d6470..186ddcd4 100644
--- a/app/src/main/java/com/arialyy/simple/module/DownloadModule.java
+++ b/app/src/main/java/com/arialyy/simple/module/DownloadModule.java
@@ -86,8 +86,8 @@ public class DownloadModule extends BaseModule {
}
private DownloadEntity createDownloadEntity(String url) {
- String fileName = CommonUtil.keyToHashCode(url) + ".apk";
- DownloadEntity entity = new DownloadEntity();
+ String fileName = CommonUtil.keyToHashCode(url) + ".apk";
+ DownloadEntity entity = new DownloadEntity();
entity.setDownloadUrl(url);
entity.setDownloadPath(getDownloadPath(url));
entity.setFileName(fileName);
@@ -115,15 +115,15 @@ public class DownloadModule extends BaseModule {
public IntentFilter getDownloadFilter() {
IntentFilter filter = new IntentFilter();
filter.addDataScheme(getContext().getPackageName());
- filter.addAction(DownloadManager.ACTION_PRE);
- filter.addAction(DownloadManager.ACTION_POST_PRE);
- filter.addAction(DownloadManager.ACTION_RESUME);
- filter.addAction(DownloadManager.ACTION_START);
- filter.addAction(DownloadManager.ACTION_RUNNING);
- filter.addAction(DownloadManager.ACTION_STOP);
- filter.addAction(DownloadManager.ACTION_CANCEL);
- filter.addAction(DownloadManager.ACTION_COMPLETE);
- filter.addAction(DownloadManager.ACTION_FAIL);
+ 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;
}
@@ -138,36 +138,36 @@ public class DownloadModule extends BaseModule {
@Override public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
switch (action) {
- case DownloadManager.ACTION_POST_PRE:
- DownloadEntity entity = intent.getParcelableExtra(DownloadManager.ENTITY);
+ case Aria.ACTION_POST_PRE:
+ DownloadEntity entity = intent.getParcelableExtra(Aria.ENTITY);
len = entity.getFileSize();
L.d(TAG, "download pre");
handler.obtainMessage(SingleTaskActivity.DOWNLOAD_PRE, len).sendToTarget();
break;
- case DownloadManager.ACTION_START:
+ case Aria.ACTION_START:
L.d(TAG, "download start");
break;
- case DownloadManager.ACTION_RESUME:
+ case Aria.ACTION_RESUME:
L.d(TAG, "download resume");
- long location = intent.getLongExtra(DownloadManager.CURRENT_LOCATION, 1);
+ long location = intent.getLongExtra(Aria.CURRENT_LOCATION, 1);
handler.obtainMessage(SingleTaskActivity.DOWNLOAD_RESUME, location).sendToTarget();
break;
- case DownloadManager.ACTION_RUNNING:
- long current = intent.getLongExtra(DownloadManager.CURRENT_LOCATION, 0);
+ 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 DownloadManager.ACTION_STOP:
+ case Aria.ACTION_STOP:
L.d(TAG, "download stop");
handler.sendEmptyMessage(SingleTaskActivity.DOWNLOAD_STOP);
break;
- case DownloadManager.ACTION_COMPLETE:
+ case Aria.ACTION_COMPLETE:
handler.sendEmptyMessage(SingleTaskActivity.DOWNLOAD_COMPLETE);
break;
- case DownloadManager.ACTION_CANCEL:
+ case Aria.ACTION_CANCEL:
handler.sendEmptyMessage(SingleTaskActivity.DOWNLOAD_CANCEL);
break;
- case DownloadManager.ACTION_FAIL:
+ case Aria.ACTION_FAIL:
handler.sendEmptyMessage(SingleTaskActivity.DOWNLOAD_FAILE);
break;
}