commit
bf68a84cf0
@ -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; |
||||||
|
} |
||||||
|
} |
@ -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<CMD extends AbsCmd> { |
||||||
|
|
||||||
|
/** |
||||||
|
* @param target 创建任务的对象 |
||||||
|
* @param entity 下载实体 |
||||||
|
*/ |
||||||
|
public abstract <T extends AbsTaskEntity> CMD createCmd(String target, T entity, int type); |
||||||
|
} |
@ -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<T extends AbsTaskEntity> extends AbsCmd<T> { |
||||||
|
|
||||||
|
/** |
||||||
|
* @param targetName 创建任务的对象名 |
||||||
|
*/ |
||||||
|
AbsGroupCmd(String targetName, T entity) { |
||||||
|
mTargetName = targetName; |
||||||
|
mTaskEntity = entity; |
||||||
|
TAG = CommonUtil.getClassName(this); |
||||||
|
if (entity instanceof DownloadGroupTaskEntity) { |
||||||
|
mQueue = DownloadGroupTaskQueue.getInstance(); |
||||||
|
isDownloadCmd = true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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<AbsGroupCmd> { |
||||||
|
/** |
||||||
|
* 启动任务 |
||||||
|
*/ |
||||||
|
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 <T extends AbsTaskEntity> AbsGroupCmd<T> 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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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<T extends AbsTaskEntity> extends AbsGroupCmd<T> { |
||||||
|
/** |
||||||
|
* @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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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<T extends AbsTaskEntity> extends AbsGroupCmd<T>{ |
||||||
|
/** |
||||||
|
* @param targetName 创建任务的对象名 |
||||||
|
*/ |
||||||
|
GroupStopCmd(String targetName, T entity) { |
||||||
|
super(targetName, entity); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void executeCmd() { |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -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<T extends AbsTaskEntity> extends AbsCmd<T> { |
||||||
|
|
||||||
|
/** |
||||||
|
* 能否执行命令 |
||||||
|
*/ |
||||||
|
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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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<T extends AbsTaskEntity> extends AbsNormalCmd<T> { |
||||||
|
/** |
||||||
|
* @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<UploadTaskEntity> allEntity = DbEntity.findAllData(UploadTaskEntity.class); |
||||||
|
for (UploadTaskEntity entity : allEntity) { |
||||||
|
CommonUtil.delUploadTaskConfig(mTaskEntity.removeFile, entity); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 处理下载的删除 |
||||||
|
*/ |
||||||
|
private void handleDownloadRemove() { |
||||||
|
List<DownloadTaskEntity> allEntity = DbEntity.findAllData(DownloadTaskEntity.class); |
||||||
|
for (DownloadTaskEntity entity : allEntity) { |
||||||
|
CommonUtil.delDownloadTaskConfig(mTaskEntity.removeFile, entity); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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<T extends AbsTaskEntity> extends AbsNormalCmd<T> { |
||||||
|
|
||||||
|
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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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<ENTITY extends AbsEntity, TASK extends AbsTask<ENTITY>> |
||||||
|
extends DownloadListener { |
||||||
|
private WeakReference<Handler> 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(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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<DownloadEntity> subtask = |
||||||
|
new ArrayList<>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 子任务链接组 |
||||||
|
*/ |
||||||
|
@NormalList(clazz = String.class) private List<String> urls = new ArrayList<>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 任务组下载文件的文件夹地址 |
||||||
|
* |
||||||
|
* @see DownloadGroupTarget#setDownloadDirPath(String) |
||||||
|
*/ |
||||||
|
private String dirPath = ""; |
||||||
|
|
||||||
|
public List<DownloadEntity> getSubTask() { |
||||||
|
return subtask; |
||||||
|
} |
||||||
|
|
||||||
|
void setSubTasks(List<DownloadEntity> subTasks) { |
||||||
|
this.subtask = subTasks; |
||||||
|
} |
||||||
|
|
||||||
|
public String getDirPath() { |
||||||
|
return dirPath; |
||||||
|
} |
||||||
|
|
||||||
|
public void setDirPath(String dirPath) { |
||||||
|
this.dirPath = dirPath; |
||||||
|
} |
||||||
|
|
||||||
|
public List<String> getUrls() { |
||||||
|
return urls; |
||||||
|
} |
||||||
|
|
||||||
|
void setUrls(List<String> 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<DownloadGroupEntity> CREATOR = new Creator<DownloadGroupEntity>() { |
||||||
|
@Override public DownloadGroupEntity createFromParcel(Parcel source) { |
||||||
|
return new DownloadGroupEntity(source); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public DownloadGroupEntity[] newArray(int size) { |
||||||
|
return new DownloadGroupEntity[size]; |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
@ -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<DownloadGroupTarget, DownloadGroupEntity, DownloadGroupTaskEntity> { |
||||||
|
private List<String> mUrls = new ArrayList<>(); |
||||||
|
private final String TAG = "DownloadGroupTarget"; |
||||||
|
/** |
||||||
|
* 子任务文件名 |
||||||
|
*/ |
||||||
|
private List<String> 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<String> 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<String> 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" |
||||||
|
* <pre> |
||||||
|
* {@code |
||||||
|
* + mnt |
||||||
|
* + sdcard |
||||||
|
* + download |
||||||
|
* + group_test |
||||||
|
* - task1.apk |
||||||
|
* - task2.apk |
||||||
|
* - task3.apk |
||||||
|
* .... |
||||||
|
* |
||||||
|
* } |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* @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<DownloadEntity> 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<String> subTaskFileName) { |
||||||
|
if (subTaskFileName == null || subTaskFileName.isEmpty()) return this; |
||||||
|
mSubTaskFileName.addAll(subTaskFileName); |
||||||
|
if (mUrls.size() != subTaskFileName.size()) { |
||||||
|
throw new IllegalArgumentException("下载链接数必须要和保存路径的数量一致"); |
||||||
|
} |
||||||
|
if (isSetDirPathed) { |
||||||
|
List<DownloadEntity> 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<DownloadEntity> createSubTask() { |
||||||
|
List<DownloadEntity> 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; |
||||||
|
} |
||||||
|
} |
@ -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<DownloadGroupTaskEntity, DownloadGroupEntity> { |
||||||
|
private final String TAG = "DownloadGroupTask"; |
||||||
|
private DListener<DownloadGroupEntity, DownloadGroupTask> 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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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<DownloadGroupEntity> { |
||||||
|
|
||||||
|
@OneToOne(table = DownloadGroupEntity.class, key = "groupName") public DownloadGroupEntity entity; |
||||||
|
|
||||||
|
@Override public DownloadGroupEntity getEntity() { |
||||||
|
return entity; |
||||||
|
} |
||||||
|
} |
@ -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<Runnable> 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<Object> 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; |
|
||||||
} |
|
||||||
} |
|
@ -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; |
||||||
|
} |
@ -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<String, DownloadTaskEntity> mExeMap = new HashMap<>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 下载失败的映射表,key为下载地址 |
||||||
|
*/ |
||||||
|
private Map<String, DownloadTaskEntity> mFailMap = new HashMap<>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 下载器映射表,key为下载地址 |
||||||
|
*/ |
||||||
|
private Map<String, Downloader> mDownloaderMap = new HashMap<>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 文件信息回调组 |
||||||
|
*/ |
||||||
|
private SparseArray<FileInfoThread.OnFileInfoCallback> mFileInfoCallbacks = new SparseArray<>(); |
||||||
|
/** |
||||||
|
* 该任务组对应的所有任务 |
||||||
|
*/ |
||||||
|
private Map<String, DownloadTaskEntity> 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<DownloadTaskEntity> 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<String> keys = mDownloaderMap.keySet(); |
||||||
|
for (String key : keys) { |
||||||
|
Downloader dt = mDownloaderMap.get(key); |
||||||
|
if (dt != null) { |
||||||
|
dt.cancelDownload(); |
||||||
|
} |
||||||
|
} |
||||||
|
delDownloadInfo(); |
||||||
|
mTaskEntity.deleteData(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 删除所有子任务的下载信息 |
||||||
|
*/ |
||||||
|
private void delDownloadInfo() { |
||||||
|
List<DownloadTaskEntity> 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<String> keys = mDownloaderMap.keySet(); |
||||||
|
for (String key : keys) { |
||||||
|
Downloader dt = mDownloaderMap.get(key); |
||||||
|
if (dt != null) { |
||||||
|
dt.stopDownload(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void startDownload() { |
||||||
|
isRunning = true; |
||||||
|
Set<String> 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(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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<Runnable> 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<Object> 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(); |
||||||
|
} |
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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); |
||||||
|
} |
@ -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(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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(); |
||||||
|
} |
||||||
|
} |
@ -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<TARGET extends AbsGroupTarget, ENTITY extends AbsGroupEntity, TASK_ENTITY extends AbsTaskEntity> |
||||||
|
extends AbsTarget<TARGET, ENTITY, TASK_ENTITY> { |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -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<TASK_ENTITY extends AbsTaskEntity, ENTITY extends AbsGroupEntity> |
||||||
|
extends AbsTask<ENTITY> { |
||||||
|
|
||||||
|
protected TASK_ENTITY mTaskEntity; |
||||||
|
|
||||||
|
|
||||||
|
@Override public String getKey() { |
||||||
|
return mEntity.getGroupName(); |
||||||
|
} |
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
} |
@ -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<TARGET extends AbsTarget, ENTITY extends AbsEntity, TASK_ENTITY extends AbsTaskEntity> |
||||||
|
extends AbsTarget<TARGET, ENTITY, TASK_ENTITY> { |
||||||
|
|
||||||
|
/** |
||||||
|
* 将任务设置为最高优先级任务,最高优先级任务有以下特点: |
||||||
|
* 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(); |
||||||
|
} |
||||||
|
} |
@ -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<ENTITY extends AbsEntity> extends AbsTask<ENTITY> { |
||||||
|
|
||||||
|
/** |
||||||
|
* 暂停任务,并让任务处于等待状态 |
||||||
|
*/ |
||||||
|
public void stopAndWait() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 最高优先级命令,最高优先级命令有以下属性 |
||||||
|
* 1、在下载队列中,有且只有一个最高优先级任务 |
||||||
|
* 2、最高优先级任务会一直存在,直到用户手动暂停或任务完成 |
||||||
|
* 3、任务调度器不会暂停最高优先级任务 |
||||||
|
* 4、用户手动暂停或任务完成后,第二次重新执行该任务,该命令将失效 |
||||||
|
* 5、如果下载队列中已经满了,则会停止队尾的任务 |
||||||
|
* 6、把任务设置为最高优先级任务后,将自动执行任务,不需要重新调用start()启动任务 |
||||||
|
*/ |
||||||
|
public void setHighestPriority(boolean isHighestPriority) { |
||||||
|
isHeighestTask = isHighestPriority; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -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<ENTITY extends AbsEntity> implements IReceiver<ENTITY>{ |
||||||
|
public String targetName; |
||||||
|
public Object obj; |
||||||
|
|
||||||
|
} |
@ -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<TARGET extends 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<String, String> headers); |
||||||
|
|
||||||
|
/** |
||||||
|
* 设置请求类型 |
||||||
|
* |
||||||
|
* @param requestEnum {@link RequestEnum} |
||||||
|
*/ |
||||||
|
TARGET setRequestMode(RequestEnum requestEnum); |
||||||
|
|
||||||
|
/** |
||||||
|
* 开始下载 |
||||||
|
*/ |
||||||
|
void start(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 停止下载 |
||||||
|
*/ |
||||||
|
void stop(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 恢复下载 |
||||||
|
*/ |
||||||
|
void resume(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 取消下载 |
||||||
|
*/ |
||||||
|
void cancel(); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,83 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
import android.text.TextUtils; |
||||||
|
import android.util.Log; |
||||||
|
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.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.DownloadGroupSchedulers; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by AriaL on 2017/6/29. |
||||||
|
* 任务组下载队列 |
||||||
|
*/ |
||||||
|
public class DownloadGroupTaskQueue |
||||||
|
extends AbsTaskQueue<DownloadGroupTask, DownloadGroupTaskEntity, DownloadGroupEntity> { |
||||||
|
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<DownloadGroupTask> setCachePool() { |
||||||
|
return DownloadSharePool.getInstance().cachePool; |
||||||
|
} |
||||||
|
|
||||||
|
@Override BaseExecutePool<DownloadGroupTask> 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; |
||||||
|
} |
||||||
|
} |
@ -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<TASK extends AbsTask> extends BaseExecutePool<TASK> { |
||||||
|
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<String> 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; |
||||||
|
} |
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
} |
@ -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<TASK extends AbsTask> extends BaseExecutePool<TASK> { |
||||||
|
@Override protected int getMaxSize() { |
||||||
|
return AriaManager.getInstance(AriaManager.APP).getUploadConfig().getMaxTaskNum(); |
||||||
|
} |
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
} |
@ -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<String, Integer> 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<Map.Entry<String, Integer>> iter = types.entrySet().iterator(); |
||||||
|
if (iter.hasNext()) { |
||||||
|
Map.Entry<String, Integer> 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(); |
||||||
|
} |
||||||
|
} |
@ -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<DownloadGroupTaskEntity, DownloadGroupEntity, DownloadGroupTask, DownloadGroupTaskQueue> { |
||||||
|
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(); |
||||||
|
} |
||||||
|
} |
@ -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<String> 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<String>) dc); |
||||||
|
} |
||||||
|
Object dgc = downloadGroup.invoke(object); |
||||||
|
if (dgc != null) { |
||||||
|
downloadGroupCounter = unmodifiableSet((Set<String>) dgc); |
||||||
|
} |
||||||
|
Object uc = upload.invoke(object); |
||||||
|
if (uc != null) { |
||||||
|
uploadCounter = unmodifiableSet((Set<String>) 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(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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(); |
||||||
|
} |
@ -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<? extends DbEntity> table(); |
||||||
|
/** |
||||||
|
* 关联的主键 |
||||||
|
*/ |
||||||
|
String key(); |
||||||
|
} |
@ -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<? extends DbEntity> table(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 关联的主键 |
||||||
|
*/ |
||||||
|
String key(); |
||||||
|
} |
@ -1,14 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|
||||||
android:layout_width="match_parent" |
|
||||||
android:layout_height="match_parent" |
|
||||||
android:orientation="vertical" |
|
||||||
> |
|
||||||
|
|
||||||
<ListView |
|
||||||
android:id="@+id/list" |
|
||||||
android:layout_width="match_parent" |
|
||||||
android:layout_height="match_parent" |
|
||||||
/> |
|
||||||
|
|
||||||
</LinearLayout> |
|
@ -1,51 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|
||||||
android:layout_width="match_parent" |
|
||||||
android:layout_height="match_parent" |
|
||||||
android:orientation="vertical" |
|
||||||
android:padding="16dp" |
|
||||||
> |
|
||||||
|
|
||||||
<CheckBox |
|
||||||
android:id="@+id/checkbox" |
|
||||||
android:layout_width="wrap_content" |
|
||||||
android:layout_height="wrap_content" |
|
||||||
android:layout_alignParentRight="true" |
|
||||||
android:layout_centerVertical="true" |
|
||||||
android:clickable="false" |
|
||||||
/> |
|
||||||
|
|
||||||
<ImageView |
|
||||||
android:id="@+id/img" |
|
||||||
android:layout_width="50dp" |
|
||||||
android:layout_height="50dp" |
|
||||||
android:layout_centerVertical="true" |
|
||||||
/> |
|
||||||
|
|
||||||
<TextView |
|
||||||
android:id="@+id/title" |
|
||||||
android:layout_width="wrap_content" |
|
||||||
android:layout_height="wrap_content" |
|
||||||
android:layout_alignTop="@+id/img" |
|
||||||
android:layout_marginLeft="10dp" |
|
||||||
android:layout_marginRight="5dp" |
|
||||||
android:layout_toLeftOf="@+id/checkbox" |
|
||||||
android:layout_toRightOf="@+id/img" |
|
||||||
android:ellipsize="end" |
|
||||||
android:maxLines="1" |
|
||||||
android:textSize="18sp" |
|
||||||
android:textStyle="bold" |
|
||||||
/> |
|
||||||
|
|
||||||
<TextView |
|
||||||
android:id="@+id/info" |
|
||||||
android:layout_width="wrap_content" |
|
||||||
android:layout_height="wrap_content" |
|
||||||
android:layout_alignLeft="@+id/title" |
|
||||||
android:layout_below="@+id/title" |
|
||||||
android:layout_marginRight="5dp" |
|
||||||
android:layout_marginTop="10dp" |
|
||||||
android:layout_toLeftOf="@+id/checkbox" |
|
||||||
/> |
|
||||||
|
|
||||||
</RelativeLayout> |
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue