parent
c06b1fd5fb
commit
fa7741b3e9
@ -1,58 +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.dua.group |
|
||||||
|
|
||||||
import com.arialyy.aria.core.listener.IEventListener |
|
||||||
import com.arialyy.aria.exception.AriaException |
|
||||||
|
|
||||||
/** |
|
||||||
* @Author laoyuyu |
|
||||||
* @Description |
|
||||||
* @Date 10:13 2023/3/12 |
|
||||||
**/ |
|
||||||
internal class HttpSubListener : IEventListener { |
|
||||||
override fun onPre() { |
|
||||||
TODO("Not yet implemented") |
|
||||||
} |
|
||||||
|
|
||||||
override fun onStart(startLocation: Long) { |
|
||||||
TODO("Not yet implemented") |
|
||||||
} |
|
||||||
|
|
||||||
override fun onResume(resumeLocation: Long) { |
|
||||||
TODO("Not yet implemented") |
|
||||||
} |
|
||||||
|
|
||||||
override fun onProgress(currentLocation: Long) { |
|
||||||
TODO("Not yet implemented") |
|
||||||
} |
|
||||||
|
|
||||||
override fun onStop(stopLocation: Long) { |
|
||||||
TODO("Not yet implemented") |
|
||||||
} |
|
||||||
|
|
||||||
override fun onComplete() { |
|
||||||
TODO("Not yet implemented") |
|
||||||
} |
|
||||||
|
|
||||||
override fun onCancel() { |
|
||||||
TODO("Not yet implemented") |
|
||||||
} |
|
||||||
|
|
||||||
override fun onFail(needRetry: Boolean, e: AriaException?) { |
|
||||||
TODO("Not yet implemented") |
|
||||||
} |
|
||||||
} |
|
@ -1 +1 @@ |
|||||||
com.arialyy.dua.group.HttpGroupComponent |
com.arialyy.dua.group.HttpDGComponent |
@ -1,68 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
package com.arialyy.aria.core.command; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.download.AbsGroupTaskWrapper; |
|
||||||
import com.arialyy.aria.core.download.DGTaskWrapper; |
|
||||||
import com.arialyy.aria.core.queue.DGroupTaskQueue; |
|
||||||
import com.arialyy.aria.core.task.AbsGroupTask; |
|
||||||
import com.arialyy.aria.core.task.AbsTask; |
|
||||||
import com.arialyy.aria.util.ALog; |
|
||||||
import com.arialyy.aria.util.CommonUtil; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by AriaL on 2017/6/29. |
|
||||||
* 任务组命令 |
|
||||||
*/ |
|
||||||
public abstract class AbsGroupCmd<T extends AbsGroupTaskWrapper> extends AbsCmd<T> { |
|
||||||
/** |
|
||||||
* 需要控制的子任务url |
|
||||||
*/ |
|
||||||
String childUrl; |
|
||||||
|
|
||||||
AbsGroupTask tempTask; |
|
||||||
|
|
||||||
AbsGroupCmd(T entity) { |
|
||||||
mTaskWrapper = entity; |
|
||||||
TAG = CommonUtil.getClassName(this); |
|
||||||
if (entity instanceof DGTaskWrapper) { |
|
||||||
mQueue = DGroupTaskQueue.getInstance(); |
|
||||||
isDownloadCmd = true; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 创建任务 |
|
||||||
* |
|
||||||
* @return 创建的任务 |
|
||||||
*/ |
|
||||||
AbsTask createTask() { |
|
||||||
tempTask = (AbsGroupTask) mQueue.createTask(mTaskWrapper); |
|
||||||
return tempTask; |
|
||||||
} |
|
||||||
|
|
||||||
boolean checkTask() { |
|
||||||
tempTask = (AbsGroupTask) mQueue.getTask(mTaskWrapper.getEntity().getKey()); |
|
||||||
if (tempTask == null) { |
|
||||||
createTask(); |
|
||||||
if (tempTask.isComplete()) { |
|
||||||
ALog.i(TAG, "任务已完成"); |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
return true; |
|
||||||
} |
|
||||||
} |
|
@ -1,44 +0,0 @@ |
|||||||
/* |
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one |
|
||||||
* or more contributor license agreements. See the NOTICE file |
|
||||||
* distributed with this work for additional information |
|
||||||
* regarding copyright ownership. The ASF licenses this file |
|
||||||
* to you under the Apache License, Version 2.0 (the |
|
||||||
* "License"); you may not use this file except in compliance |
|
||||||
* with the License. You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
package com.arialyy.aria.core.command; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.download.AbsGroupTaskWrapper; |
|
||||||
import com.arialyy.aria.core.task.ITask; |
|
||||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper; |
|
||||||
|
|
||||||
public class CmdHelper { |
|
||||||
/** |
|
||||||
* 创建任务命令 |
|
||||||
* |
|
||||||
* @param taskType {@link ITask#SINGLE_DOWNLOAD}、{@link ITask#HTTP_GROUP}、{@link ITask#UPLOAD} |
|
||||||
*/ |
|
||||||
public static <T extends AbsTaskWrapper> AbsNormalCmd createNormalCmd(T entity, int cmd, |
|
||||||
int taskType) { |
|
||||||
return NormalCmdFactory.getInstance().createCmd(entity, cmd, taskType); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 创建任务组命令 |
|
||||||
* |
|
||||||
* @param childUrl 子任务url |
|
||||||
*/ |
|
||||||
public static <T extends AbsGroupTaskWrapper> AbsGroupCmd createGroupCmd(T entity, int cmd, |
|
||||||
String childUrl) { |
|
||||||
return GroupCmdFactory.getInstance().createCmd(entity, cmd, childUrl); |
|
||||||
} |
|
||||||
} |
|
@ -1,34 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
package com.arialyy.aria.core.command; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.download.AbsGroupTaskWrapper; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by AriaL on 2017/6/29. |
|
||||||
* 停止任务组子任务的命令 |
|
||||||
*/ |
|
||||||
final class DGSubStopCmd<T extends AbsGroupTaskWrapper> extends AbsGroupCmd<T> { |
|
||||||
DGSubStopCmd(T entity) { |
|
||||||
super(entity); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void executeCmd() { |
|
||||||
if (checkTask()) { |
|
||||||
tempTask.stopSubTask(childUrl); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,68 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
package com.arialyy.aria.core.command; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.download.AbsGroupTaskWrapper; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by AriaL on 2017/6/29. 任务组子任务控制命令 |
|
||||||
*/ |
|
||||||
public class GroupCmdFactory { |
|
||||||
/** |
|
||||||
* 启动子任务 |
|
||||||
*/ |
|
||||||
public static final int SUB_TASK_START = 0xa1; |
|
||||||
/** |
|
||||||
* 停止子任务 |
|
||||||
*/ |
|
||||||
public static final int SUB_TASK_STOP = 0xa2; |
|
||||||
|
|
||||||
private static volatile GroupCmdFactory INSTANCE = null; |
|
||||||
|
|
||||||
private GroupCmdFactory() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
public static GroupCmdFactory getInstance() { |
|
||||||
if (INSTANCE == null) { |
|
||||||
synchronized (GroupCmdFactory.class) { |
|
||||||
INSTANCE = new GroupCmdFactory(); |
|
||||||
} |
|
||||||
} |
|
||||||
return INSTANCE; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @param wrapper 参数信息 |
|
||||||
* @param type 命令类型{@link #SUB_TASK_START}、{@link #SUB_TASK_STOP} |
|
||||||
* @param childUrl 需要控制的子任务url |
|
||||||
*/ |
|
||||||
public AbsGroupCmd createCmd(AbsGroupTaskWrapper wrapper, int type, String childUrl) { |
|
||||||
AbsGroupCmd cmd = null; |
|
||||||
switch (type) { |
|
||||||
case SUB_TASK_START: |
|
||||||
cmd = new DGSubStartCmd<>(wrapper); |
|
||||||
break; |
|
||||||
case SUB_TASK_STOP: |
|
||||||
cmd = new DGSubStopCmd<>(wrapper); |
|
||||||
break; |
|
||||||
} |
|
||||||
if (cmd != null) { |
|
||||||
cmd.childUrl = childUrl; |
|
||||||
} |
|
||||||
return cmd; |
|
||||||
} |
|
||||||
} |
|
@ -1,57 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
package com.arialyy.aria.core.command; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.AriaConfig; |
|
||||||
import com.arialyy.aria.core.AriaManager; |
|
||||||
import com.arialyy.aria.core.task.SingleDownloadTask; |
|
||||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper; |
|
||||||
import com.arialyy.aria.core.queue.DTaskQueue; |
|
||||||
import com.arialyy.aria.util.ALog; |
|
||||||
import com.arialyy.aria.util.NetUtils; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by lyy on 2017/6/2. |
|
||||||
* 最高优先级命令,最高优先级命令有以下属性 |
|
||||||
* 1、在下载队列中,有且只有一个最高优先级任务 |
|
||||||
* 2、最高优先级任务会一直存在,直到用户手动暂停或任务完成 |
|
||||||
* 3、任务调度器不会暂停最高优先级任务 |
|
||||||
* 4、用户手动暂停或任务完成后,第二次重新执行该任务,该命令将失效 |
|
||||||
* 5、如果下载队列中已经满了,则会停止队尾的任务,当高优先级任务完成后,该队尾任务将自动执行 |
|
||||||
* 6、把任务设置为最高优先级任务后,将自动执行任务,不需要重新调用start()启动任务 |
|
||||||
* |
|
||||||
* 目前只只支持单下载任务的最高优先级任务 |
|
||||||
*/ |
|
||||||
final class HighestPriorityCmd<T extends AbsTaskWrapper> extends AbsNormalCmd<T> { |
|
||||||
HighestPriorityCmd(T entity, int taskType) { |
|
||||||
super(entity, taskType); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void executeCmd() { |
|
||||||
if (!canExeCmd) return; |
|
||||||
if (!NetUtils.isConnected(AriaConfig.getInstance().getAPP())){ |
|
||||||
ALog.e(TAG, "启动任务失败,网络未连接"); |
|
||||||
return; |
|
||||||
} |
|
||||||
SingleDownloadTask task = (SingleDownloadTask) getTask(); |
|
||||||
if (task == null) { |
|
||||||
task = (SingleDownloadTask) createTask(); |
|
||||||
} |
|
||||||
if (task != null) { |
|
||||||
((DTaskQueue) mQueue).setTaskHighestPriority(task); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,41 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
package com.arialyy.aria.core.command; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.inf.TaskSchedulerType; |
|
||||||
import com.arialyy.aria.core.task.AbsTask; |
|
||||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper; |
|
||||||
|
|
||||||
/** |
|
||||||
* 重新开始任务命令 |
|
||||||
*/ |
|
||||||
final class ReStartCmd<T extends AbsTaskWrapper> extends AbsNormalCmd<T> { |
|
||||||
|
|
||||||
ReStartCmd(T entity, int taskType) { |
|
||||||
super(entity, taskType); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void executeCmd() { |
|
||||||
AbsTask task = getTask(); |
|
||||||
if (task == null) { |
|
||||||
task = createTask(); |
|
||||||
} |
|
||||||
if (task != null) { |
|
||||||
mQueue.cancelTask(task, TaskSchedulerType.TYPE_CANCEL_AND_NOT_NOTIFY); |
|
||||||
mQueue.startTask(task, TaskSchedulerType.TYPE_START_AND_RESET_STATE); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,200 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
package com.arialyy.aria.core.command; |
|
||||||
|
|
||||||
import android.text.TextUtils; |
|
||||||
import com.arialyy.aria.core.common.AbsEntity; |
|
||||||
import com.arialyy.aria.core.download.DGTaskWrapper; |
|
||||||
import com.arialyy.aria.core.download.DTaskWrapper; |
|
||||||
import com.arialyy.aria.core.download.DownloadEntity; |
|
||||||
import com.arialyy.aria.core.download.DownloadGroupEntity; |
|
||||||
import com.arialyy.aria.core.inf.IEntity; |
|
||||||
import com.arialyy.aria.core.inf.IOptionConstant; |
|
||||||
import com.arialyy.aria.core.listener.ISchedulers; |
|
||||||
import com.arialyy.aria.core.manager.TaskWrapperManager; |
|
||||||
import com.arialyy.aria.core.queue.AbsTaskQueue; |
|
||||||
import com.arialyy.aria.core.queue.DGroupTaskQueue; |
|
||||||
import com.arialyy.aria.core.queue.DTaskQueue; |
|
||||||
import com.arialyy.aria.core.queue.UTaskQueue; |
|
||||||
import com.arialyy.aria.core.task.AbsTask; |
|
||||||
import com.arialyy.aria.core.upload.UTaskWrapper; |
|
||||||
import com.arialyy.aria.core.upload.UploadEntity; |
|
||||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper; |
|
||||||
import com.arialyy.aria.core.wrapper.ITaskWrapper; |
|
||||||
import com.arialyy.aria.orm.DbEntity; |
|
||||||
import com.arialyy.aria.util.ALog; |
|
||||||
import com.arialyy.aria.util.CommonUtil; |
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.List; |
|
||||||
|
|
||||||
/** |
|
||||||
* 恢复任务工具 |
|
||||||
*/ |
|
||||||
public class ResumeThread implements Runnable { |
|
||||||
private String TAG = CommonUtil.getClassName(getClass()); |
|
||||||
private List<AbsTaskWrapper> mWaitList = new ArrayList<>(); |
|
||||||
private boolean isDownloadCmd; |
|
||||||
private String sqlCondition; |
|
||||||
|
|
||||||
ResumeThread(boolean isDownload, String sqlCondition) { |
|
||||||
this.isDownloadCmd = isDownload; |
|
||||||
this.sqlCondition = sqlCondition; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 查找数据库中的所有任务数据 |
|
||||||
* |
|
||||||
* @param type {@code 1}单任务下载任务;{@code 2}任务组下载任务;{@code 3} 单任务上传任务 |
|
||||||
*/ |
|
||||||
private void findTaskData(int type) { |
|
||||||
if (type == 1) { |
|
||||||
List<DownloadEntity> entities = |
|
||||||
DbEntity.findDatas(DownloadEntity.class, |
|
||||||
String.format("NOT(isGroupChild) AND NOT(isComplete) AND %s ORDER BY stopTime DESC", |
|
||||||
sqlCondition)); |
|
||||||
if (entities != null && !entities.isEmpty()) { |
|
||||||
for (DownloadEntity entity : entities) { |
|
||||||
addResumeEntity(TaskWrapperManager.getInstance() |
|
||||||
.getNormalTaskWrapper(DTaskWrapper.class, entity.getId())); |
|
||||||
} |
|
||||||
} |
|
||||||
} else if (type == 2) { |
|
||||||
List<DownloadGroupEntity> entities = |
|
||||||
DbEntity.findDatas(DownloadGroupEntity.class, |
|
||||||
String.format("NOT(isComplete) AND %s ORDER BY stopTime DESC", |
|
||||||
sqlCondition)); |
|
||||||
if (entities != null && !entities.isEmpty()) { |
|
||||||
for (DownloadGroupEntity entity : entities) { |
|
||||||
addResumeEntity( |
|
||||||
TaskWrapperManager.getInstance() |
|
||||||
.getGroupWrapper(DGTaskWrapper.class, entity.getId())); |
|
||||||
} |
|
||||||
} |
|
||||||
} else if (type == 3) { |
|
||||||
List<UploadEntity> entities = |
|
||||||
DbEntity.findDatas(UploadEntity.class, |
|
||||||
String.format("NOT(isComplete) AND %s ORDER BY stopTime DESC", |
|
||||||
sqlCondition)); |
|
||||||
if (entities != null && !entities.isEmpty()) { |
|
||||||
for (UploadEntity entity : entities) { |
|
||||||
addResumeEntity(TaskWrapperManager.getInstance() |
|
||||||
.getNormalTaskWrapper(UTaskWrapper.class, entity.getId())); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 添加恢复实体 |
|
||||||
*/ |
|
||||||
private void addResumeEntity(AbsTaskWrapper te) { |
|
||||||
if (te == null || te.getEntity() == null || TextUtils.isEmpty(te.getKey())) { |
|
||||||
return; |
|
||||||
} |
|
||||||
mWaitList.add(te); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 处理等待状态的任务 |
|
||||||
*/ |
|
||||||
private void resumeWaitTask() { |
|
||||||
|
|
||||||
if (mWaitList == null || mWaitList.isEmpty()) { |
|
||||||
return; |
|
||||||
} |
|
||||||
List<AbsEntity> resumeEntities = new ArrayList<>(); |
|
||||||
|
|
||||||
for (AbsTaskWrapper wrapper : mWaitList) { |
|
||||||
AbsTaskQueue queue = null; |
|
||||||
if (wrapper instanceof DTaskWrapper) { |
|
||||||
queue = DTaskQueue.getInstance(); |
|
||||||
} else if (wrapper instanceof UTaskWrapper) { |
|
||||||
queue = UTaskQueue.getInstance(); |
|
||||||
} else if (wrapper instanceof DGTaskWrapper) { |
|
||||||
queue = DGroupTaskQueue.getInstance(); |
|
||||||
} |
|
||||||
|
|
||||||
if (queue == null) { |
|
||||||
ALog.e(TAG, "任务类型错误"); |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
if (wrapper.getEntity() == null || TextUtils.isEmpty(wrapper.getKey())) { |
|
||||||
ALog.e(TAG, "任务实体为空或key为空"); |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
AbsTask task = queue.getTask(wrapper.getKey()); |
|
||||||
if (task != null) { |
|
||||||
ALog.w(TAG, "任务已存在"); |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
int maxTaskNum = queue.getMaxTaskNum(); |
|
||||||
task = queue.createTask(wrapper); |
|
||||||
if (task == null) { |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
handleWrapper(wrapper); |
|
||||||
|
|
||||||
if (queue.getCurrentExePoolNum() < maxTaskNum) { |
|
||||||
queue.startTask(task); |
|
||||||
} else { |
|
||||||
wrapper.getEntity().setState(IEntity.STATE_WAIT); |
|
||||||
sendWaitState(task); |
|
||||||
resumeEntities.add(wrapper.getEntity()); |
|
||||||
} |
|
||||||
} |
|
||||||
if (!resumeEntities.isEmpty()) { |
|
||||||
DbEntity.updateManyData(resumeEntities); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 处理ftp的wrapper |
|
||||||
*/ |
|
||||||
private void handleWrapper(AbsTaskWrapper wrapper) { |
|
||||||
int requestType = wrapper.getRequestType(); |
|
||||||
if (requestType == ITaskWrapper.D_FTP |
|
||||||
|| requestType == ITaskWrapper.U_FTP |
|
||||||
|| requestType == ITaskWrapper.D_FTP_DIR) { |
|
||||||
wrapper.getOptionParams() |
|
||||||
.setParams(IOptionConstant.ftpUrlEntity, |
|
||||||
CommonUtil.getFtpUrlInfo(wrapper.getEntity().getKey())); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 发送等待状态 |
|
||||||
*/ |
|
||||||
private void sendWaitState(AbsTask task) { |
|
||||||
if (task != null) { |
|
||||||
task.getTaskWrapper().setState(IEntity.STATE_WAIT); |
|
||||||
task.getOutHandler().obtainMessage(ISchedulers.WAIT, task).sendToTarget(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void run() { |
|
||||||
if (isDownloadCmd) { |
|
||||||
findTaskData(1); |
|
||||||
findTaskData(2); |
|
||||||
} else { |
|
||||||
findTaskData(3); |
|
||||||
} |
|
||||||
resumeWaitTask(); |
|
||||||
} |
|
||||||
} |
|
@ -1,375 +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.group; |
|
||||||
|
|
||||||
import android.os.Handler; |
|
||||||
import android.os.Looper; |
|
||||||
import com.arialyy.aria.core.config.Configuration; |
|
||||||
import com.arialyy.aria.core.download.DGTaskWrapper; |
|
||||||
import com.arialyy.aria.core.download.DTaskWrapper; |
|
||||||
import com.arialyy.aria.core.inf.IEntity; |
|
||||||
import com.arialyy.aria.core.inf.ITaskManager; |
|
||||||
import com.arialyy.aria.core.listener.IDGroupListener; |
|
||||||
import com.arialyy.aria.core.listener.IEventListener; |
|
||||||
import com.arialyy.aria.core.loader.IInfoTask; |
|
||||||
import com.arialyy.aria.core.loader.ILoader; |
|
||||||
import com.arialyy.aria.core.loader.ILoaderVisitor; |
|
||||||
import com.arialyy.aria.core.loader.IRecordHandler; |
|
||||||
import com.arialyy.aria.core.loader.IThreadTaskBuilder; |
|
||||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper; |
|
||||||
import com.arialyy.aria.exception.AriaException; |
|
||||||
import com.arialyy.aria.util.ALog; |
|
||||||
import com.arialyy.aria.util.CommonUtil; |
|
||||||
import java.io.File; |
|
||||||
import java.util.Map; |
|
||||||
import java.util.WeakHashMap; |
|
||||||
import java.util.concurrent.ScheduledThreadPoolExecutor; |
|
||||||
import java.util.concurrent.TimeUnit; |
|
||||||
|
|
||||||
/** |
|
||||||
* 组合任务加载器 |
|
||||||
*/ |
|
||||||
public abstract class AbsGroupLoader implements ILoaderVisitor, ILoader { |
|
||||||
protected final String TAG = CommonUtil.getClassName(getClass()); |
|
||||||
|
|
||||||
private long mCurrentLocation = 0; |
|
||||||
private IDGroupListener mListener; |
|
||||||
private ScheduledThreadPoolExecutor mTimer; |
|
||||||
private long mUpdateInterval; |
|
||||||
private boolean isStop = false, isCancel = false; |
|
||||||
private Handler mScheduler; |
|
||||||
private SimpleSubQueue mSubQueue = SimpleSubQueue.newInstance(); |
|
||||||
private Map<String, AbsSubDLoadAdapter> mExeLoader = new WeakHashMap<>(); |
|
||||||
private Map<String, DTaskWrapper> mCache = new WeakHashMap<>(); |
|
||||||
private DGTaskWrapper mGTWrapper; |
|
||||||
private GroupRunState mState; |
|
||||||
|
|
||||||
protected IInfoTask mInfoTask; |
|
||||||
|
|
||||||
protected AbsGroupLoader(AbsTaskWrapper groupWrapper, IEventListener listener) { |
|
||||||
mListener = (IDGroupListener) listener; |
|
||||||
mGTWrapper = (DGTaskWrapper) groupWrapper; |
|
||||||
mUpdateInterval = Configuration.getInstance().downloadCfg.getUpdateInterval(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 处理任务 |
|
||||||
*/ |
|
||||||
protected abstract void handlerTask(Looper looper); |
|
||||||
|
|
||||||
/** |
|
||||||
* 创建子任务加载器工具 |
|
||||||
* |
|
||||||
* @param needGetFileInfo {@code true} 需要获取文件信息。{@code false} 不需要获取文件信息 |
|
||||||
*/ |
|
||||||
protected abstract AbsSubDLoadAdapter createSubLoader(DTaskWrapper wrapper, boolean needGetFileInfo); |
|
||||||
|
|
||||||
protected IDGroupListener getListener() { |
|
||||||
return mListener; |
|
||||||
} |
|
||||||
|
|
||||||
protected DGTaskWrapper getWrapper() { |
|
||||||
return mGTWrapper; |
|
||||||
} |
|
||||||
|
|
||||||
protected GroupRunState getState() { |
|
||||||
return mState; |
|
||||||
} |
|
||||||
|
|
||||||
public Handler getScheduler() { |
|
||||||
return mScheduler; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 初始化组合任务状态 |
|
||||||
*/ |
|
||||||
private void initState(Looper looper) { |
|
||||||
mState = new GroupRunState(getWrapper().getKey(), mListener, mSubQueue); |
|
||||||
for (DTaskWrapper wrapper : mGTWrapper.getSubTaskWrapper()) { |
|
||||||
long fileLen = checkFileExists(wrapper.getEntity().getFilePath()); |
|
||||||
if (wrapper.getEntity().getState() == IEntity.STATE_COMPLETE |
|
||||||
&& fileLen > 0 |
|
||||||
&& fileLen == wrapper.getEntity().getFileSize()) { |
|
||||||
//mState.updateCompleteNum();
|
|
||||||
mCurrentLocation += wrapper.getEntity().getFileSize(); |
|
||||||
} else { |
|
||||||
if (fileLen <= 0) { |
|
||||||
wrapper.getEntity().setCurrentProgress(0); |
|
||||||
} |
|
||||||
wrapper.getEntity().setState(IEntity.STATE_POST_PRE); |
|
||||||
mCache.put(wrapper.getKey(), wrapper); |
|
||||||
mCurrentLocation += wrapper.getEntity().getCurrentProgress(); |
|
||||||
} |
|
||||||
} |
|
||||||
if (getWrapper().getSubTaskWrapper().size() != mState.getCompleteNum()) { |
|
||||||
getWrapper().setState(IEntity.STATE_POST_PRE); |
|
||||||
} |
|
||||||
mState.updateProgress(mCurrentLocation); |
|
||||||
mScheduler = new Handler(looper, SimpleSchedulers.newInstance(mState, mGTWrapper.getKey())); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 检查文件是否存在,需要检查普通任务和分块任务的 |
|
||||||
* |
|
||||||
* @param filePath 文件路径 |
|
||||||
* @return 文件存在返回文件长度,不存在返回-1 |
|
||||||
*/ |
|
||||||
private long checkFileExists(String filePath) { |
|
||||||
File temp = new File(filePath); |
|
||||||
if (temp.exists()) { |
|
||||||
return temp.length(); |
|
||||||
} |
|
||||||
File block = new File(String.format(IRecordHandler.SUB_PATH, filePath, 0)); |
|
||||||
if (block.exists()) { |
|
||||||
return block.length(); |
|
||||||
} else { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override public String getKey() { |
|
||||||
return mGTWrapper.getKey(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 启动子任务下载 |
|
||||||
* |
|
||||||
* @param url 子任务下载地址 |
|
||||||
*/ |
|
||||||
void startSubTask(String url) { |
|
||||||
if (!checkSubTask(url, "开始")) { |
|
||||||
return; |
|
||||||
} |
|
||||||
if (!mState.isRunning.get()) { |
|
||||||
startTimer(); |
|
||||||
} |
|
||||||
AbsSubDLoadAdapter d = getDownloader(url, false); |
|
||||||
if (d != null && !d.isRunning()) { |
|
||||||
mSubQueue.startTask(d); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 停止子任务下载 |
|
||||||
* |
|
||||||
* @param url 子任务下载地址 |
|
||||||
*/ |
|
||||||
void stopSubTask(String url) { |
|
||||||
if (!checkSubTask(url, "停止")) { |
|
||||||
return; |
|
||||||
} |
|
||||||
AbsSubDLoadAdapter d = getDownloader(url, false); |
|
||||||
if (d != null && d.isRunning()) { |
|
||||||
mSubQueue.stopTask(d); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 检查子任务 |
|
||||||
* |
|
||||||
* @param url 子任务url |
|
||||||
* @param type 任务类型 |
|
||||||
* @return {@code true} 任务可以下载 |
|
||||||
*/ |
|
||||||
private boolean checkSubTask(String url, String type) { |
|
||||||
DTaskWrapper wrapper = mCache.get(url); |
|
||||||
if (wrapper != null) { |
|
||||||
if (wrapper.getState() == IEntity.STATE_COMPLETE) { |
|
||||||
ALog.w(TAG, "任务【" + url + "】已完成," + type + "失败"); |
|
||||||
return false; |
|
||||||
} |
|
||||||
} else { |
|
||||||
ALog.w(TAG, "任务组中没有该任务【" + url + "】," + type + "失败"); |
|
||||||
return false; |
|
||||||
} |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 通过地址获取下载器 |
|
||||||
* |
|
||||||
* @param url 子任务下载地址 |
|
||||||
*/ |
|
||||||
private AbsSubDLoadAdapter getDownloader(String url, boolean needGetFileInfo) { |
|
||||||
AbsSubDLoadAdapter d = mExeLoader.get(url); |
|
||||||
if (d == null) { |
|
||||||
return createSubLoader(mCache.get(url), needGetFileInfo); |
|
||||||
} |
|
||||||
return d; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public boolean isRunning() { |
|
||||||
return mState != null && mState.isRunning.get(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void cancel() { |
|
||||||
isCancel = true; |
|
||||||
if (mInfoTask != null){ |
|
||||||
mInfoTask.cancel(); |
|
||||||
} |
|
||||||
closeTimer(); |
|
||||||
mSubQueue.removeAllTask(); |
|
||||||
mListener.onCancel(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void stop() { |
|
||||||
if (mInfoTask != null){ |
|
||||||
mInfoTask.stop(); |
|
||||||
} |
|
||||||
isStop = true; |
|
||||||
if (mSubQueue.getExecSize() == 0) { |
|
||||||
mListener.onStop(mGTWrapper.getEntity().getCurrentProgress()); |
|
||||||
} else { |
|
||||||
mSubQueue.stopAllTask(); |
|
||||||
} |
|
||||||
closeTimer(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void run() { |
|
||||||
checkComponent(); |
|
||||||
if (isStop || isCancel) { |
|
||||||
closeTimer(); |
|
||||||
return; |
|
||||||
} |
|
||||||
startRunningFlow(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 开始进度流程 |
|
||||||
*/ |
|
||||||
private void startRunningFlow() { |
|
||||||
closeTimer(); |
|
||||||
Looper.prepare(); |
|
||||||
Looper looper = Looper.myLooper(); |
|
||||||
if (looper == Looper.getMainLooper()) { |
|
||||||
throw new IllegalThreadStateException("不能在主线程程序中调用Loader"); |
|
||||||
} |
|
||||||
initState(looper); |
|
||||||
getState().setSubSize(getWrapper().getSubTaskWrapper().size()); |
|
||||||
if (getState().getCompleteNum() != 0 |
|
||||||
&& getState().getCompleteNum() == getState().getSubSize()) { |
|
||||||
mListener.onComplete(); |
|
||||||
return; |
|
||||||
} |
|
||||||
startTimer(); |
|
||||||
handlerTask(looper); |
|
||||||
Looper.loop(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 组合任务获取完成子任务的信息后调用 |
|
||||||
*/ |
|
||||||
protected void onPostStart() { |
|
||||||
if (isBreak()) { |
|
||||||
return; |
|
||||||
} |
|
||||||
getListener().onPostPre(getWrapper().getEntity().getFileSize()); |
|
||||||
if (getWrapper().getEntity().getFileSize() > 0) { |
|
||||||
getListener().onResume(getWrapper().getEntity().getCurrentProgress()); |
|
||||||
} else { |
|
||||||
getListener().onStart(getWrapper().getEntity().getCurrentProgress()); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private synchronized void startTimer() { |
|
||||||
mState.isRunning.set(true); |
|
||||||
mTimer = new ScheduledThreadPoolExecutor(1); |
|
||||||
mTimer.scheduleWithFixedDelay(new Runnable() { |
|
||||||
@Override public void run() { |
|
||||||
if (!mState.isRunning.get()) { |
|
||||||
closeTimer(); |
|
||||||
} else if (mCurrentLocation >= 0) { |
|
||||||
long t = 0; |
|
||||||
for (DTaskWrapper te : mGTWrapper.getSubTaskWrapper()) { |
|
||||||
if (te.getState() == IEntity.STATE_COMPLETE) { |
|
||||||
t += te.getEntity().getFileSize(); |
|
||||||
} else { |
|
||||||
t += te.getEntity().getCurrentProgress(); |
|
||||||
} |
|
||||||
} |
|
||||||
mCurrentLocation = t; |
|
||||||
mState.updateProgress(mCurrentLocation); |
|
||||||
mListener.onProgress(t); |
|
||||||
} |
|
||||||
} |
|
||||||
}, 0, mUpdateInterval, TimeUnit.MILLISECONDS); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 启动子任务下载器 |
|
||||||
*/ |
|
||||||
protected void startSubLoader(AbsSubDLoadAdapter loader) { |
|
||||||
mExeLoader.put(loader.getKey(), loader); |
|
||||||
mSubQueue.startTask(loader); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public boolean isBreak() { |
|
||||||
if (isCancel || isStop) { |
|
||||||
//ALog.d(TAG, "isCancel = " + isCancel + ", isStop = " + isStop);
|
|
||||||
ALog.d(TAG, String.format("任务【%s】已停止或取消了", mGTWrapper.getKey())); |
|
||||||
return true; |
|
||||||
} |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
private synchronized void closeTimer() { |
|
||||||
if (mTimer != null && !mTimer.isShutdown()) { |
|
||||||
mTimer.shutdown(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
protected void fail(AriaException e, boolean needRetry) { |
|
||||||
closeTimer(); |
|
||||||
getListener().onFail(needRetry, e); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public long getCurrentProgress() { |
|
||||||
return mCurrentLocation; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @deprecated 组合任务不需要实现这个,记录交由其子任务处理 |
|
||||||
*/ |
|
||||||
@Deprecated |
|
||||||
@Override public void addComponent(IRecordHandler recordHandler) { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @deprecated 组合任务不需要实现这个,线程创建交有子任务处理 |
|
||||||
*/ |
|
||||||
@Deprecated |
|
||||||
@Override public void addComponent(IThreadTaskBuilder builder) { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @deprecated 组合任务不需要实现这个,其内部是一个子任务调度器,并不是线程状态管理器 |
|
||||||
*/ |
|
||||||
@Deprecated |
|
||||||
@Override public void addComponent(ITaskManager threadState) { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 检查组件: {@link #mInfoTask} |
|
||||||
*/ |
|
||||||
private void checkComponent() { |
|
||||||
if (mInfoTask == null) { |
|
||||||
throw new NullPointerException(("文件信息组件为空")); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,107 +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.group; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.inf.ITaskAdapter; |
|
||||||
import com.arialyy.aria.core.listener.IEventListener; |
|
||||||
import com.arialyy.aria.core.loader.LoaderStructure; |
|
||||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper; |
|
||||||
import com.arialyy.aria.util.ALog; |
|
||||||
import com.arialyy.aria.util.CommonUtil; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by AriaL on 2017/6/30. |
|
||||||
* 任务组核心逻辑 |
|
||||||
*/ |
|
||||||
public abstract class AbsGroupLoaderAdapter implements ITaskAdapter { |
|
||||||
|
|
||||||
protected String TAG = CommonUtil.getClassName(getClass()); |
|
||||||
private IEventListener mListener; |
|
||||||
protected AbsGroupLoader mLoader; |
|
||||||
private AbsTaskWrapper mTaskWrapper; |
|
||||||
private boolean isStop = false, isCancel = false; |
|
||||||
|
|
||||||
|
|
||||||
@Override public ITaskAdapter setParams(AbsTaskWrapper taskWrapper, IEventListener listener) { |
|
||||||
mTaskWrapper = taskWrapper; |
|
||||||
mListener = listener; |
|
||||||
mLoader = getLoader(); |
|
||||||
return this; |
|
||||||
} |
|
||||||
|
|
||||||
protected abstract AbsGroupLoader getLoader(); |
|
||||||
|
|
||||||
protected abstract LoaderStructure buildLoaderStructure(); |
|
||||||
|
|
||||||
public IEventListener getListener() { |
|
||||||
return mListener; |
|
||||||
} |
|
||||||
|
|
||||||
public AbsTaskWrapper getTaskWrapper() { |
|
||||||
return mTaskWrapper; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public String getKey() { |
|
||||||
return mTaskWrapper.getKey(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public long getFileSize() { |
|
||||||
return mTaskWrapper.getEntity().getFileSize(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public long getCurrentLocation() { |
|
||||||
return mLoader.getCurrentProgress(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public boolean isRunning() { |
|
||||||
return mLoader.isRunning(); |
|
||||||
} |
|
||||||
|
|
||||||
public void startSubTask(String url) { |
|
||||||
getLoader().startSubTask(url); |
|
||||||
} |
|
||||||
|
|
||||||
public void stopSubTask(String url) { |
|
||||||
getLoader().stopSubTask(url); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 取消下载 |
|
||||||
*/ |
|
||||||
@Override public void cancel() { |
|
||||||
isCancel = true; |
|
||||||
mLoader.cancel(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 停止下载 |
|
||||||
*/ |
|
||||||
@Override public void stop() { |
|
||||||
isStop = true; |
|
||||||
mLoader.stop(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void start() { |
|
||||||
if (isStop || isCancel) { |
|
||||||
ALog.w(TAG, "启动组合任务失败,任务已停止或已取消"); |
|
||||||
return; |
|
||||||
} |
|
||||||
mListener.onPre(); |
|
||||||
|
|
||||||
buildLoaderStructure(); |
|
||||||
new Thread(mLoader).start(); |
|
||||||
} |
|
||||||
} |
|
@ -1,165 +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.group; |
|
||||||
|
|
||||||
import android.os.Handler; |
|
||||||
import com.arialyy.aria.core.TaskRecord; |
|
||||||
import com.arialyy.aria.core.download.DTaskWrapper; |
|
||||||
import com.arialyy.aria.core.download.DownloadEntity; |
|
||||||
import com.arialyy.aria.core.inf.ITaskAdapter; |
|
||||||
import com.arialyy.aria.core.listener.IEventListener; |
|
||||||
import com.arialyy.aria.core.listener.ISchedulers; |
|
||||||
import com.arialyy.aria.core.loader.LoaderStructure; |
|
||||||
import com.arialyy.aria.core.loader.SubLoader; |
|
||||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper; |
|
||||||
import com.arialyy.aria.util.ALog; |
|
||||||
import com.arialyy.aria.util.CommonUtil; |
|
||||||
|
|
||||||
/** |
|
||||||
* 子任务下载器工具,需要在线程池中执行 |
|
||||||
*/ |
|
||||||
public abstract class AbsSubDLoadAdapter implements ITaskAdapter, Runnable { |
|
||||||
protected final String TAG = CommonUtil.getClassName(getClass()); |
|
||||||
|
|
||||||
protected SubLoader mDLoader; |
|
||||||
private DTaskWrapper mWrapper; |
|
||||||
private Handler mSchedulers; |
|
||||||
private boolean needGetInfo; |
|
||||||
private boolean isStop = false, isCancel = false; |
|
||||||
private String parentKey; |
|
||||||
|
|
||||||
/** |
|
||||||
* @param schedulers 调度器 |
|
||||||
* @param needGetInfo {@code true} 需要获取文件信息。{@code false} 不需要获取文件信息 |
|
||||||
*/ |
|
||||||
protected AbsSubDLoadAdapter(Handler schedulers, boolean needGetInfo, String parentKey) { |
|
||||||
mSchedulers = schedulers; |
|
||||||
this.parentKey = parentKey; |
|
||||||
this.needGetInfo = needGetInfo; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public ITaskAdapter setParams(AbsTaskWrapper taskWrapper, IEventListener listener) { |
|
||||||
mWrapper = (DTaskWrapper) taskWrapper; |
|
||||||
mDLoader = getLoader(); |
|
||||||
return this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 创建加载器 |
|
||||||
*/ |
|
||||||
protected abstract SubLoader getLoader(); |
|
||||||
|
|
||||||
protected abstract LoaderStructure buildLoaderStructure(); |
|
||||||
|
|
||||||
public String getParentKey() { |
|
||||||
return parentKey; |
|
||||||
} |
|
||||||
|
|
||||||
protected boolean isNeedGetInfo() { |
|
||||||
return needGetInfo; |
|
||||||
} |
|
||||||
|
|
||||||
public Handler getSchedulers() { |
|
||||||
return mSchedulers; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public String getKey() { |
|
||||||
return mDLoader.getKey(); |
|
||||||
} |
|
||||||
|
|
||||||
public DTaskWrapper getWrapper() { |
|
||||||
return mWrapper; |
|
||||||
} |
|
||||||
|
|
||||||
public DownloadEntity getEntity() { |
|
||||||
return mWrapper.getEntity(); |
|
||||||
} |
|
||||||
|
|
||||||
public TaskRecord getRecord(){ |
|
||||||
return getLoader().getRecord(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void run() { |
|
||||||
if (isStop || isCancel) { |
|
||||||
return; |
|
||||||
} |
|
||||||
buildLoaderStructure(); |
|
||||||
new Thread(mDLoader).start(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 请在线程池中使用 |
|
||||||
*/ |
|
||||||
@Deprecated |
|
||||||
@Override public void start() { |
|
||||||
throw new AssertionError("请在线程池中使用"); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 重新开始任务 |
|
||||||
*/ |
|
||||||
void reStart() { |
|
||||||
if (mDLoader != null) { |
|
||||||
mDLoader.retryTask(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @deprecated 子任务不实现这个 |
|
||||||
*/ |
|
||||||
@Deprecated |
|
||||||
@Override public long getFileSize() { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 子任务不实现这个 |
|
||||||
*/ |
|
||||||
@Deprecated |
|
||||||
@Override public long getCurrentLocation() { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public boolean isRunning() { |
|
||||||
return mDLoader != null && mDLoader.isRunning(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void cancel() { |
|
||||||
if (isCancel) { |
|
||||||
ALog.w(TAG, "子任务已取消"); |
|
||||||
return; |
|
||||||
} |
|
||||||
isCancel = true; |
|
||||||
if (mDLoader != null && isRunning()) { |
|
||||||
mDLoader.cancel(); |
|
||||||
} else { |
|
||||||
mSchedulers.obtainMessage(ISchedulers.CANCEL, this).sendToTarget(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void stop() { |
|
||||||
if (isStop) { |
|
||||||
ALog.w(TAG, "任务已停止"); |
|
||||||
return; |
|
||||||
} |
|
||||||
isStop = true; |
|
||||||
if (mDLoader != null && isRunning()) { |
|
||||||
mDLoader.stop(); |
|
||||||
} else { |
|
||||||
mSchedulers.obtainMessage(ISchedulers.STOP, this).sendToTarget(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,189 +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.group; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.listener.IDGroupListener; |
|
||||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper; |
|
||||||
import com.arialyy.aria.util.ALog; |
|
||||||
import java.util.HashSet; |
|
||||||
import java.util.Set; |
|
||||||
import java.util.concurrent.atomic.AtomicBoolean; |
|
||||||
import java.util.concurrent.atomic.AtomicInteger; |
|
||||||
|
|
||||||
/** |
|
||||||
* 组合任务执行中的状态信息 |
|
||||||
*/ |
|
||||||
public final class GroupRunState { |
|
||||||
private String TAG = "GroupRunState"; |
|
||||||
/** |
|
||||||
* 子任务数 |
|
||||||
*/ |
|
||||||
private int mSubSize; |
|
||||||
|
|
||||||
/** |
|
||||||
* 已经完成的任务数 |
|
||||||
*/ |
|
||||||
private AtomicInteger mCompleteNum = new AtomicInteger(); |
|
||||||
|
|
||||||
/** |
|
||||||
* 失败的任务数 |
|
||||||
*/ |
|
||||||
private AtomicInteger mFailNum = new AtomicInteger(); |
|
||||||
|
|
||||||
/** |
|
||||||
* 停止的任务数 |
|
||||||
*/ |
|
||||||
private AtomicInteger mStopNum = new AtomicInteger(); |
|
||||||
|
|
||||||
/** |
|
||||||
* 当前进度 |
|
||||||
*/ |
|
||||||
private long mProgress; |
|
||||||
|
|
||||||
/** |
|
||||||
* 组合任务监听 |
|
||||||
*/ |
|
||||||
IDGroupListener listener; |
|
||||||
|
|
||||||
/** |
|
||||||
* 子任务队列 |
|
||||||
*/ |
|
||||||
SimpleSubQueue queue; |
|
||||||
|
|
||||||
/** |
|
||||||
* 是否在执行 |
|
||||||
*/ |
|
||||||
AtomicBoolean isRunning = new AtomicBoolean(false); |
|
||||||
|
|
||||||
/** |
|
||||||
* 子任务失败、停止记录,用于当子任务失败重新被用户点击开始时,更新{@link #mStopNum}或{@link #mFailNum} |
|
||||||
* 保存的数据为:子任务key |
|
||||||
*/ |
|
||||||
private Set<String> mFailTemp = new HashSet<>(), mStopTemp = new HashSet<>(); |
|
||||||
|
|
||||||
private String mGroupHash; |
|
||||||
|
|
||||||
GroupRunState(String groupHash, IDGroupListener listener, SimpleSubQueue queue) { |
|
||||||
this.listener = listener; |
|
||||||
this.queue = queue; |
|
||||||
mGroupHash = groupHash; |
|
||||||
} |
|
||||||
|
|
||||||
public void setSubSize(int subSize) { |
|
||||||
mSubSize = subSize; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 组合任务是否正在自行 |
|
||||||
* |
|
||||||
* @return {@code true}组合任务正在执行 |
|
||||||
*/ |
|
||||||
public boolean isRunning() { |
|
||||||
return isRunning.get(); |
|
||||||
} |
|
||||||
|
|
||||||
public void setRunning(boolean running) { |
|
||||||
isRunning.set(running); |
|
||||||
} |
|
||||||
|
|
||||||
String getGroupHash() { |
|
||||||
return mGroupHash; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 获取组合任务子任务数 |
|
||||||
*/ |
|
||||||
public int getSubSize() { |
|
||||||
return mSubSize; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 获取失败的数量 |
|
||||||
*/ |
|
||||||
public int getFailNum() { |
|
||||||
return mFailNum.get(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 获取停止的数量 |
|
||||||
*/ |
|
||||||
public int getStopNum() { |
|
||||||
return mStopNum.get(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 获取完成的数量 |
|
||||||
*/ |
|
||||||
public int getCompleteNum() { |
|
||||||
return mCompleteNum.get(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 获取当前组合任务总进度 |
|
||||||
*/ |
|
||||||
public long getProgress() { |
|
||||||
return mProgress; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 更新完成的数量,mCompleteNum + 1 |
|
||||||
*/ |
|
||||||
public void updateCompleteNum() { |
|
||||||
mCompleteNum.getAndIncrement(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 更新任务进度 |
|
||||||
*/ |
|
||||||
public void updateProgress(long newProgress) { |
|
||||||
this.mProgress = newProgress; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 当子任务开始时,更新停止\失败的任务数 |
|
||||||
* |
|
||||||
* @param key {@link AbsTaskWrapper#getKey()} |
|
||||||
*/ |
|
||||||
public void updateCount(String key) { |
|
||||||
if (mFailTemp.contains(key)) { |
|
||||||
mFailTemp.remove(key); |
|
||||||
mFailNum.getAndDecrement(); |
|
||||||
} else if (mStopTemp.contains(key)) { |
|
||||||
mStopTemp.remove(key); |
|
||||||
mStopNum.getAndDecrement(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 统计子任务停止的数量 |
|
||||||
* |
|
||||||
* @param key {@link AbsTaskWrapper#getKey()} |
|
||||||
*/ |
|
||||||
public void countStopNum(String key) { |
|
||||||
mStopTemp.add(key); |
|
||||||
mStopNum.getAndIncrement(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 统计子任务失败的数量 |
|
||||||
* |
|
||||||
* @param key {@link AbsTaskWrapper#getKey()} |
|
||||||
*/ |
|
||||||
public void countFailNum(String key) { |
|
||||||
mFailTemp.add(key); |
|
||||||
mFailNum.getAndIncrement(); |
|
||||||
} |
|
||||||
} |
|
@ -1,37 +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.group; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.common.AbsNormalEntity; |
|
||||||
import com.arialyy.aria.core.task.AbsGroupTask; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by lyy on 2017/9/8. |
|
||||||
* 任务组参数传递 |
|
||||||
*/ |
|
||||||
public final class GroupSendParams<GROUP_TASK extends AbsGroupTask, ENTITY extends AbsNormalEntity> { |
|
||||||
|
|
||||||
public GROUP_TASK groupTask; |
|
||||||
public ENTITY entity; |
|
||||||
|
|
||||||
public GroupSendParams() { |
|
||||||
} |
|
||||||
|
|
||||||
public GroupSendParams(GROUP_TASK groupTask, ENTITY entity) { |
|
||||||
this.groupTask = groupTask; |
|
||||||
this.entity = entity; |
|
||||||
} |
|
||||||
} |
|
@ -1,85 +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.group; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.loader.AbsNormalLoader; |
|
||||||
import com.arialyy.aria.core.inf.ITaskAdapter; |
|
||||||
import com.arialyy.aria.core.config.DGroupConfig; |
|
||||||
|
|
||||||
/** |
|
||||||
* 组合任务子任务队列 |
|
||||||
* |
|
||||||
* @param <Fileer> {@link AbsNormalLoader}下载器 |
|
||||||
*/ |
|
||||||
interface ISubQueue<Fileer extends ITaskAdapter> { |
|
||||||
|
|
||||||
/** |
|
||||||
* 添加任务 |
|
||||||
*/ |
|
||||||
|
|
||||||
void addTask(Fileer fileer); |
|
||||||
|
|
||||||
/** |
|
||||||
* 开始任务 |
|
||||||
* 如果执行队列没有达到上限,则启动任务。 |
|
||||||
* 如果执行队列已经到达上限,则将任务添加到等待队列总。 |
|
||||||
* 队列上限配置{@link DGroupConfig#setSubMaxTaskNum(int)} |
|
||||||
*/ |
|
||||||
void startTask(Fileer fileer); |
|
||||||
|
|
||||||
/** |
|
||||||
* 停止单个任务,如果缓存队列中有等待中的任务,则启动等待中的任务 |
|
||||||
*/ |
|
||||||
void stopTask(Fileer fileer); |
|
||||||
|
|
||||||
/** |
|
||||||
* 停止全部任务,停止所有正在执行的任务,并清空所有等待中的端服务 |
|
||||||
*/ |
|
||||||
void stopAllTask(); |
|
||||||
|
|
||||||
/** |
|
||||||
* 修改最大任务数 |
|
||||||
* |
|
||||||
* @param num 任务数不能小于1 |
|
||||||
*/ |
|
||||||
void modifyMaxExecNum(int num); |
|
||||||
|
|
||||||
/** |
|
||||||
* 从执行队列中移除任务,一般用于任务完成的情况 |
|
||||||
*/ |
|
||||||
void removeTaskFromExecQ(Fileer fileer); |
|
||||||
|
|
||||||
/** |
|
||||||
* 删除任务,如果缓存队列中有等待中的任务,则启动等待中的任务 |
|
||||||
*/ |
|
||||||
void removeTask(Fileer fileer); |
|
||||||
|
|
||||||
/** |
|
||||||
* 停止全部任务,停止所有正在执行的任务,并清空所有等待中的端服务 |
|
||||||
*/ |
|
||||||
void removeAllTask(); |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* 获取下一个任务 |
|
||||||
*/ |
|
||||||
Fileer getNextTask(); |
|
||||||
|
|
||||||
/** |
|
||||||
* 清空缓存队列和执行队列 |
|
||||||
*/ |
|
||||||
void clear(); |
|
||||||
} |
|
@ -1,230 +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.group; |
|
||||||
|
|
||||||
import android.os.Bundle; |
|
||||||
import android.os.Handler; |
|
||||||
import android.os.Message; |
|
||||||
import android.util.Log; |
|
||||||
import com.arialyy.aria.core.AriaConfig; |
|
||||||
import com.arialyy.aria.core.TaskRecord; |
|
||||||
import com.arialyy.aria.core.config.Configuration; |
|
||||||
import com.arialyy.aria.core.inf.ITaskManager; |
|
||||||
import com.arialyy.aria.core.loader.IRecordHandler; |
|
||||||
import com.arialyy.aria.core.manager.ThreadTaskManager; |
|
||||||
import com.arialyy.aria.exception.ExceptionFactory; |
|
||||||
import com.arialyy.aria.util.ALog; |
|
||||||
import com.arialyy.aria.util.CommonUtil; |
|
||||||
import com.arialyy.aria.util.NetUtils; |
|
||||||
import java.io.File; |
|
||||||
|
|
||||||
/** |
|
||||||
* 组合任务子任务调度器,用于调度任务的开始、停止、失败、完成等情况 |
|
||||||
* 该调度器生命周期和{@link AbsGroupLoaderAdapter}生命周期一致 |
|
||||||
*/ |
|
||||||
final class SimpleSchedulers implements Handler.Callback { |
|
||||||
private final String TAG = CommonUtil.getClassName(this); |
|
||||||
private SimpleSubQueue mQueue; |
|
||||||
private GroupRunState mGState; |
|
||||||
private String mKey; // 组合任务的key
|
|
||||||
|
|
||||||
private SimpleSchedulers(GroupRunState state, String key) { |
|
||||||
mQueue = state.queue; |
|
||||||
mGState = state; |
|
||||||
mKey = key; |
|
||||||
} |
|
||||||
|
|
||||||
static SimpleSchedulers newInstance(GroupRunState state, String key) { |
|
||||||
return new SimpleSchedulers(state, key); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public boolean handleMessage(Message msg) { |
|
||||||
Bundle b = msg.getData(); |
|
||||||
if (b == null) { |
|
||||||
ALog.w(TAG, "组合任务子任务调度数据为空"); |
|
||||||
return true; |
|
||||||
} |
|
||||||
String threadName = b.getString(ITaskManager.DATA_THREAD_NAME); |
|
||||||
AbsSubDLoadAdapter loaderUtil = mQueue.getLoaderUtil(threadName); |
|
||||||
if (loaderUtil == null) { |
|
||||||
ALog.e(TAG, String.format("子任务loader不存在,state:%s,key:%s", msg.what, threadName)); |
|
||||||
return true; |
|
||||||
} |
|
||||||
long curLocation = b.getLong(ITaskManager.DATA_THREAD_LOCATION, |
|
||||||
loaderUtil.getLoader().getWrapper().getEntity().getCurrentProgress()); |
|
||||||
// 处理状态
|
|
||||||
switch (msg.what) { |
|
||||||
case ITaskManager.STATE_RUNNING: |
|
||||||
long range = (long) msg.obj; |
|
||||||
mGState.listener.onSubRunning(loaderUtil.getEntity(), range); |
|
||||||
break; |
|
||||||
case ITaskManager.STATE_PRE: |
|
||||||
mGState.listener.onSubPre(loaderUtil.getEntity()); |
|
||||||
mGState.updateCount(loaderUtil.getKey()); |
|
||||||
break; |
|
||||||
case ITaskManager.STATE_START: |
|
||||||
mGState.listener.onSubStart(loaderUtil.getEntity()); |
|
||||||
break; |
|
||||||
case ITaskManager.STATE_STOP: |
|
||||||
handleStop(loaderUtil, curLocation); |
|
||||||
ThreadTaskManager.getInstance().removeSingleTaskThread(mKey, threadName); |
|
||||||
break; |
|
||||||
case ITaskManager.STATE_COMPLETE: |
|
||||||
handleComplete(loaderUtil); |
|
||||||
ThreadTaskManager.getInstance().removeSingleTaskThread(mKey, threadName); |
|
||||||
break; |
|
||||||
case ITaskManager.STATE_FAIL: |
|
||||||
boolean needRetry = b.getBoolean(ITaskManager.DATA_RETRY, false); |
|
||||||
handleFail(loaderUtil, needRetry); |
|
||||||
ThreadTaskManager.getInstance().removeSingleTaskThread(mKey, threadName); |
|
||||||
break; |
|
||||||
} |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 处理子任务失败的情况 |
|
||||||
* 1、子任务失败次数大于等于配置的重试次数,才能认为子任务停止 |
|
||||||
* 2、stopNum + failNum + completeNum + cacheNum == subSize,则认为组合任务停止 |
|
||||||
* 3、failNum == subSize,只有全部的子任务都失败了,才能任务组合任务失败 |
|
||||||
* |
|
||||||
* @param needRetry true 需要重试,false 不需要重试 |
|
||||||
*/ |
|
||||||
private synchronized void handleFail(final AbsSubDLoadAdapter loaderUtil, boolean needRetry) { |
|
||||||
Log.d(TAG, String.format("handleFail, size = %s, completeNum = %s, failNum = %s, stopNum = %s", |
|
||||||
mGState.getSubSize(), mGState.getCompleteNum(), mGState.getFailNum(), |
|
||||||
mGState.getSubSize())); |
|
||||||
|
|
||||||
Configuration config = Configuration.getInstance(); |
|
||||||
int num = config.dGroupCfg.getSubReTryNum(); |
|
||||||
boolean isNotNetRetry = config.appCfg.isNotNetRetry(); |
|
||||||
|
|
||||||
if (!needRetry |
|
||||||
|| (!NetUtils.isConnected(AriaConfig.getInstance().getAPP()) && !isNotNetRetry) |
|
||||||
|| loaderUtil.getLoader() == null // 如果获取不到文件信息,loader为空
|
|
||||||
|| loaderUtil.getEntity().getFailNum() > num) { |
|
||||||
mQueue.removeTaskFromExecQ(loaderUtil); |
|
||||||
mGState.listener.onSubFail(loaderUtil.getEntity(), |
|
||||||
ExceptionFactory.getException(ExceptionFactory.TYPE_GROUP, |
|
||||||
String.format("任务组子任务【%s】下载失败,下载地址【%s】", loaderUtil.getEntity().getFileName(), |
|
||||||
loaderUtil.getEntity().getUrl()), null)); |
|
||||||
mGState.countFailNum(loaderUtil.getKey()); |
|
||||||
if (mGState.getFailNum() == mGState.getSubSize() |
|
||||||
|| mGState.getStopNum() + mGState.getFailNum() + mGState.getCompleteNum() |
|
||||||
== mGState.getSubSize()) { |
|
||||||
mGState.isRunning.set(false); |
|
||||||
if (mGState.getCompleteNum() > 0 |
|
||||||
&& Configuration.getInstance().dGroupCfg.isSubFailAsStop()) { |
|
||||||
ALog.e(TAG, String.format("任务组【%s】停止", mGState.getGroupHash())); |
|
||||||
mGState.listener.onStop(mGState.getProgress()); |
|
||||||
return; |
|
||||||
} |
|
||||||
mGState.listener.onFail(false, |
|
||||||
ExceptionFactory.getException(ExceptionFactory.TYPE_GROUP, |
|
||||||
String.format("任务组【%s】下载失败", mGState.getGroupHash()), null)); |
|
||||||
return; |
|
||||||
} |
|
||||||
startNext(); |
|
||||||
return; |
|
||||||
} |
|
||||||
SimpleSubRetryQueue.getInstance().offer(loaderUtil); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 处理子任务停止的情况 |
|
||||||
* 1、所有的子任务已经停止,则认为组合任务停止 |
|
||||||
* 2、completeNum + failNum + stopNum = subSize,则认为组合任务停止 |
|
||||||
*/ |
|
||||||
private synchronized void handleStop(AbsSubDLoadAdapter loadUtil, long curLocation) { |
|
||||||
Log.d(TAG, String.format("handleStop, size = %s, completeNum = %s, failNum = %s, stopNum = %s", |
|
||||||
mGState.getSubSize(), mGState.getCompleteNum(), mGState.getFailNum(), |
|
||||||
mGState.getSubSize())); |
|
||||||
|
|
||||||
mGState.listener.onSubStop(loadUtil.getEntity(), curLocation); |
|
||||||
mGState.countStopNum(loadUtil.getKey()); |
|
||||||
if (mGState.getStopNum() == mGState.getSubSize() |
|
||||||
|| mGState.getStopNum() |
|
||||||
+ mGState.getCompleteNum() |
|
||||||
+ mGState.getFailNum() |
|
||||||
+ mQueue.getCacheSize() |
|
||||||
== mGState.getSubSize()) { |
|
||||||
mGState.isRunning.set(false); |
|
||||||
mGState.listener.onStop(mGState.getProgress()); |
|
||||||
return; |
|
||||||
} |
|
||||||
startNext(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 处理子任务完成的情况,有以下三种情况 |
|
||||||
* 1、已经没有缓存的子任务,并且停止的子任务是数{@link GroupRunState#getStopNum()} ()}为0,失败的子任数{@link |
|
||||||
* GroupRunState#getFailNum()}为0,则认为组合任务已经完成 |
|
||||||
* 2、已经没有缓存的子任务,并且停止的子任务是数{@link GroupRunState#getCompleteNum()}不为0,或者失败的子任数{@link |
|
||||||
* GroupRunState#getFailNum()}不为0,则认为组合任务被停止 |
|
||||||
* 3、只有有缓存的子任务,则任务组合任务没有完成 |
|
||||||
*/ |
|
||||||
private synchronized void handleComplete(AbsSubDLoadAdapter loader) { |
|
||||||
ALog.d(TAG, String.format("子任务【%s】完成", loader.getEntity().getFileName())); |
|
||||||
Log.d(TAG, |
|
||||||
String.format("handleComplete, size = %s, completeNum = %s, failNum = %s, stopNum = %s", |
|
||||||
mGState.getSubSize(), mGState.getCompleteNum(), mGState.getFailNum(), |
|
||||||
mGState.getStopNum())); |
|
||||||
|
|
||||||
TaskRecord record = loader.getRecord(); |
|
||||||
if (record != null && record.isBlock) { |
|
||||||
File partFile = |
|
||||||
new File(String.format(IRecordHandler.SUB_PATH, record.filePath, 0)); |
|
||||||
partFile.renameTo(new File(record.filePath)); |
|
||||||
} |
|
||||||
ThreadTaskManager.getInstance().removeTaskThread(loader.getKey()); |
|
||||||
mGState.listener.onSubComplete(loader.getEntity()); |
|
||||||
mQueue.removeTaskFromExecQ(loader); |
|
||||||
mGState.updateCompleteNum(); |
|
||||||
if (mGState.getCompleteNum() + mGState.getFailNum() + mGState.getStopNum() |
|
||||||
== mGState.getSubSize()) { |
|
||||||
if (mGState.getStopNum() == 0 && mGState.getFailNum() == 0) { |
|
||||||
mGState.listener.onComplete(); |
|
||||||
} else if (mGState.getStopNum() == 0 |
|
||||||
&& !Configuration.getInstance().dGroupCfg.isSubFailAsStop()) { |
|
||||||
mGState.listener.onFail(false, |
|
||||||
ExceptionFactory.getException(ExceptionFactory.TYPE_GROUP, |
|
||||||
String.format("任务组【%s】下载失败", mGState.getGroupHash()), null)); |
|
||||||
} else { |
|
||||||
mGState.listener.onStop(mGState.getProgress()); |
|
||||||
} |
|
||||||
mGState.isRunning.set(false); |
|
||||||
return; |
|
||||||
} |
|
||||||
startNext(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 如果有等待中的任务,则启动下一任务 |
|
||||||
*/ |
|
||||||
private void startNext() { |
|
||||||
if (mQueue.isStopAll()) { |
|
||||||
return; |
|
||||||
} |
|
||||||
AbsSubDLoadAdapter next = mQueue.getNextTask(); |
|
||||||
if (next != null) { |
|
||||||
ALog.d(TAG, String.format("启动任务:%s", next.getEntity().getFileName())); |
|
||||||
mQueue.startTask(next); |
|
||||||
return; |
|
||||||
} |
|
||||||
ALog.i(TAG, "没有下一子任务"); |
|
||||||
} |
|
||||||
} |
|
@ -1,201 +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.group; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.config.Configuration; |
|
||||||
import com.arialyy.aria.util.ALog; |
|
||||||
import com.arialyy.aria.util.CommonUtil; |
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.Collection; |
|
||||||
import java.util.Iterator; |
|
||||||
import java.util.List; |
|
||||||
import java.util.Map; |
|
||||||
import java.util.Set; |
|
||||||
import java.util.concurrent.ConcurrentHashMap; |
|
||||||
|
|
||||||
/** |
|
||||||
* 组合任务队列,该队列生命周期和{@link AbsGroupLoaderAdapter}生命周期一致 |
|
||||||
*/ |
|
||||||
final class SimpleSubQueue implements ISubQueue<AbsSubDLoadAdapter> { |
|
||||||
private final String TAG = CommonUtil.getClassName(getClass()); |
|
||||||
/** |
|
||||||
* 缓存下载器 |
|
||||||
*/ |
|
||||||
private Map<String, AbsSubDLoadAdapter> mCache = new ConcurrentHashMap<>(); |
|
||||||
/** |
|
||||||
* 执行中的下载器 |
|
||||||
*/ |
|
||||||
private Map<String, AbsSubDLoadAdapter> mExec = new ConcurrentHashMap<>(); |
|
||||||
|
|
||||||
/** |
|
||||||
* 最大执行任务数 |
|
||||||
*/ |
|
||||||
private int mMaxExecSize; |
|
||||||
|
|
||||||
/** |
|
||||||
* 是否停止任务任务 |
|
||||||
*/ |
|
||||||
private boolean isStopAll = false; |
|
||||||
|
|
||||||
private SimpleSubQueue() { |
|
||||||
mMaxExecSize = Configuration.getInstance().dGroupCfg.getSubMaxTaskNum(); |
|
||||||
} |
|
||||||
|
|
||||||
static SimpleSubQueue newInstance() { |
|
||||||
return new SimpleSubQueue(); |
|
||||||
} |
|
||||||
|
|
||||||
synchronized AbsSubDLoadAdapter getLoaderUtil(String key) { |
|
||||||
AbsSubDLoadAdapter sub = mExec.get(key); |
|
||||||
if (sub != null) { |
|
||||||
return sub; |
|
||||||
} |
|
||||||
return mCache.get(key); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 获取缓存队列大小 |
|
||||||
*/ |
|
||||||
int getCacheSize() { |
|
||||||
return mCache.size(); |
|
||||||
} |
|
||||||
|
|
||||||
public int getExecSize(){ |
|
||||||
return mExec.size(); |
|
||||||
} |
|
||||||
|
|
||||||
boolean isStopAll() { |
|
||||||
return isStopAll; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void addTask(AbsSubDLoadAdapter fileer) { |
|
||||||
mCache.put(fileer.getKey(), fileer); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void startTask(AbsSubDLoadAdapter fileer) { |
|
||||||
if (mExec.size() < mMaxExecSize) { |
|
||||||
mCache.remove(fileer.getKey()); |
|
||||||
mExec.put(fileer.getKey(), fileer); |
|
||||||
ALog.d(TAG, |
|
||||||
String.format("开始执行子任务:%s,key: %s", fileer.getEntity().getFileName(), fileer.getKey())); |
|
||||||
fileer.run(); |
|
||||||
return; |
|
||||||
} |
|
||||||
ALog.d(TAG, String.format("执行队列已满,任务进入缓存器中,key: %s", fileer.getKey())); |
|
||||||
addTask(fileer); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void stopTask(AbsSubDLoadAdapter fileer) { |
|
||||||
fileer.stop(); |
|
||||||
mExec.remove(fileer.getKey()); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void stopAllTask() { |
|
||||||
isStopAll = true; |
|
||||||
ALog.d(TAG, "停止组合任务"); |
|
||||||
mCache.clear(); |
|
||||||
Set<String> keys = mExec.keySet(); |
|
||||||
for (String key : keys) { |
|
||||||
AbsSubDLoadAdapter loader = mExec.get(key); |
|
||||||
if (loader != null) { |
|
||||||
ALog.d(TAG, String.format("停止子任务:%s", loader.getEntity().getFileName())); |
|
||||||
loader.stop(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void modifyMaxExecNum(int num) { |
|
||||||
if (num < 1) { |
|
||||||
ALog.e(TAG, String.format("修改组合任务子任务队列数失败,num: %s", num)); |
|
||||||
return; |
|
||||||
} |
|
||||||
if (num == mMaxExecSize) { |
|
||||||
ALog.i(TAG, String.format("忽略此次修改,oldSize: %s, num: %s", mMaxExecSize, num)); |
|
||||||
return; |
|
||||||
} |
|
||||||
int oldSize = mMaxExecSize; |
|
||||||
mMaxExecSize = num; |
|
||||||
int diff = Math.abs(oldSize - num); |
|
||||||
|
|
||||||
if (oldSize < num) { // 处理队列变小的情况,该情况下将停止队尾任务,并将这些任务添加到缓存队列中
|
|
||||||
if (mExec.size() > num) { |
|
||||||
Set<String> keys = mExec.keySet(); |
|
||||||
List<AbsSubDLoadAdapter> caches = new ArrayList<>(); |
|
||||||
int i = 0; |
|
||||||
for (String key : keys) { |
|
||||||
if (i > num) { |
|
||||||
caches.add(mExec.get(key)); |
|
||||||
} |
|
||||||
i++; |
|
||||||
} |
|
||||||
Collection<AbsSubDLoadAdapter> temp = mCache.values(); |
|
||||||
mCache.clear(); |
|
||||||
for (AbsSubDLoadAdapter cache : caches) { |
|
||||||
addTask(cache); |
|
||||||
} |
|
||||||
for (AbsSubDLoadAdapter t : temp) { |
|
||||||
addTask(t); |
|
||||||
} |
|
||||||
} |
|
||||||
return; |
|
||||||
} |
|
||||||
// 处理队列变大的情况,该情况下将增加任务
|
|
||||||
if (mExec.size() < num) { |
|
||||||
for (int i = 0; i < diff; i++) { |
|
||||||
AbsSubDLoadAdapter next = getNextTask(); |
|
||||||
if (next != null) { |
|
||||||
startTask(next); |
|
||||||
} else { |
|
||||||
ALog.d(TAG, "子任务中没有缓存任务"); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void removeTaskFromExecQ(AbsSubDLoadAdapter fileer) { |
|
||||||
mExec.remove(fileer.getKey()); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void removeTask(AbsSubDLoadAdapter fileer) { |
|
||||||
removeTaskFromExecQ(fileer); |
|
||||||
mCache.remove(fileer.getKey()); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void removeAllTask() { |
|
||||||
ALog.d(TAG, "删除组合任务"); |
|
||||||
Set<String> keys = mExec.keySet(); |
|
||||||
for (String key : keys) { |
|
||||||
AbsSubDLoadAdapter loader = mExec.get(key); |
|
||||||
if (loader != null) { |
|
||||||
ALog.d(TAG, String.format("停止子任务:%s", loader.getEntity().getFileName())); |
|
||||||
loader.cancel(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override public AbsSubDLoadAdapter getNextTask() { |
|
||||||
Iterator<String> keys = mCache.keySet().iterator(); |
|
||||||
if (keys.hasNext()) { |
|
||||||
return mCache.get(keys.next()); |
|
||||||
} |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void clear() { |
|
||||||
mCache.clear(); |
|
||||||
mExec.clear(); |
|
||||||
} |
|
||||||
} |
|
@ -1,49 +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.group; |
|
||||||
|
|
||||||
import java.util.concurrent.ExecutorService; |
|
||||||
import java.util.concurrent.SynchronousQueue; |
|
||||||
import java.util.concurrent.ThreadPoolExecutor; |
|
||||||
import java.util.concurrent.TimeUnit; |
|
||||||
|
|
||||||
/** |
|
||||||
* 子任务重试队列 |
|
||||||
*/ |
|
||||||
final class SimpleSubRetryQueue { |
|
||||||
private volatile static SimpleSubRetryQueue INSTANCE = null; |
|
||||||
private ExecutorService pool = new ThreadPoolExecutor(5, 100, |
|
||||||
60L, TimeUnit.SECONDS, |
|
||||||
new SynchronousQueue<Runnable>()); |
|
||||||
|
|
||||||
public synchronized static SimpleSubRetryQueue getInstance() { |
|
||||||
if (INSTANCE == null) { |
|
||||||
synchronized (SimpleSubRetryQueue.class) { |
|
||||||
INSTANCE = new SimpleSubRetryQueue(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return INSTANCE; |
|
||||||
} |
|
||||||
|
|
||||||
private SimpleSubRetryQueue() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
void offer(AbsSubDLoadAdapter subDLoadUtil) { |
|
||||||
pool.submit(subDLoadUtil.getLoader()); |
|
||||||
} |
|
||||||
} |
|
@ -1,108 +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.group; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.TaskRecord; |
|
||||||
import com.arialyy.aria.core.ThreadRecord; |
|
||||||
import com.arialyy.aria.core.common.RecordHandler; |
|
||||||
import com.arialyy.aria.core.common.RecordHelper; |
|
||||||
import com.arialyy.aria.core.config.Configuration; |
|
||||||
import com.arialyy.aria.core.download.DownloadEntity; |
|
||||||
import com.arialyy.aria.core.loader.IRecordHandler; |
|
||||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper; |
|
||||||
import com.arialyy.aria.core.wrapper.ITaskWrapper; |
|
||||||
import com.arialyy.aria.util.RecordUtil; |
|
||||||
import java.util.ArrayList; |
|
||||||
|
|
||||||
/** |
|
||||||
* 子任务记录处理 |
|
||||||
*/ |
|
||||||
public class SubRecordHandler extends RecordHandler { |
|
||||||
public SubRecordHandler(AbsTaskWrapper wrapper) { |
|
||||||
super(wrapper); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void handlerTaskRecord(TaskRecord record) { |
|
||||||
RecordHelper helper = new RecordHelper(getWrapper(), record); |
|
||||||
if (getWrapper().isSupportBP() && record.threadNum > 1) { |
|
||||||
if (record.isBlock) { |
|
||||||
helper.handleBlockRecord(); |
|
||||||
} else { |
|
||||||
helper.handleMultiRecord(); |
|
||||||
} |
|
||||||
} else if (!getWrapper().isSupportBP()) { |
|
||||||
helper.handleNoSupportBPRecord(); |
|
||||||
} else { |
|
||||||
helper.handleSingleThreadRecord(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public ThreadRecord createThreadRecord(TaskRecord record, int threadId, long startL, long endL) { |
|
||||||
ThreadRecord tr; |
|
||||||
tr = new ThreadRecord(); |
|
||||||
tr.taskKey = record.filePath; |
|
||||||
tr.threadId = threadId; |
|
||||||
tr.startLocation = startL; |
|
||||||
tr.isComplete = false; |
|
||||||
|
|
||||||
tr.threadType = record.taskType; |
|
||||||
//最后一个线程的结束位置即为文件的总长度
|
|
||||||
if (threadId == (record.threadNum - 1)) { |
|
||||||
endL = getFileSize(); |
|
||||||
} |
|
||||||
tr.endLocation = endL; |
|
||||||
tr.blockLen = RecordUtil.getBlockLen(getFileSize(), threadId, record.threadNum); |
|
||||||
return tr; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public TaskRecord createTaskRecord(int threadNum) { |
|
||||||
TaskRecord record = new TaskRecord(); |
|
||||||
record.fileName = getEntity().getFileName(); |
|
||||||
record.filePath = getEntity().getFilePath(); |
|
||||||
record.threadRecords = new ArrayList<>(); |
|
||||||
record.threadNum = threadNum; |
|
||||||
|
|
||||||
int requestType = getWrapper().getRequestType(); |
|
||||||
if (requestType == ITaskWrapper.D_HTTP || requestType == ITaskWrapper.DG_HTTP) { |
|
||||||
record.isBlock = Configuration.getInstance().downloadCfg.isUseBlock(); |
|
||||||
} else { |
|
||||||
record.isBlock = false; |
|
||||||
} |
|
||||||
record.taskType = requestType; |
|
||||||
record.isGroupRecord = getEntity().isGroupChild(); |
|
||||||
if (record.isGroupRecord) { |
|
||||||
if (getEntity() instanceof DownloadEntity) { |
|
||||||
record.dGroupHash = ((DownloadEntity) getEntity()).getGroupHash(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return record; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public int initTaskThreadNum() { |
|
||||||
int requestTpe = getWrapper().getRequestType(); |
|
||||||
if (requestTpe == ITaskWrapper.U_HTTP |
|
||||||
|| (requestTpe == ITaskWrapper.D_HTTP && (!getWrapper().isSupportBP()) |
|
||||||
)) { |
|
||||||
return 1; |
|
||||||
} |
|
||||||
int threadNum = Configuration.getInstance().downloadCfg.getThreadNum(); |
|
||||||
return getFileSize() <= IRecordHandler.SUB_LEN |
|
||||||
? 1 |
|
||||||
: threadNum; |
|
||||||
} |
|
||||||
} |
|
@ -1,340 +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.loader; |
|
||||||
|
|
||||||
import android.os.Looper; |
|
||||||
import android.util.Log; |
|
||||||
import com.arialyy.aria.core.TaskRecord; |
|
||||||
import com.arialyy.aria.core.inf.ITaskManager; |
|
||||||
import com.arialyy.aria.core.listener.IEventListener; |
|
||||||
import com.arialyy.aria.core.manager.ThreadTaskManager; |
|
||||||
import com.arialyy.aria.core.task.IThreadTask; |
|
||||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper; |
|
||||||
import com.arialyy.aria.util.ALog; |
|
||||||
import com.arialyy.aria.util.CommonUtil; |
|
||||||
import java.io.File; |
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.List; |
|
||||||
import java.util.concurrent.ScheduledThreadPoolExecutor; |
|
||||||
import java.util.concurrent.TimeUnit; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by AriaL on 2017/7/1. |
|
||||||
* 任务执行器,用于处理任务的开始,停止 |
|
||||||
* 流程: |
|
||||||
* 1、获取任务记录 |
|
||||||
* 2、创建任务状态管理器,用于管理任务的状态 |
|
||||||
* 3、创建文件信息获取器,获取文件信息,根据文件信息执行任务 |
|
||||||
* 4、创建线程任务执行下载、上传操作 |
|
||||||
*/ |
|
||||||
public abstract class AbsNormalLoader<T extends AbsTaskWrapper> implements ILoaderVisitor, ILoader { |
|
||||||
protected final String TAG = CommonUtil.getClassName(getClass()); |
|
||||||
private IEventListener mListener; |
|
||||||
protected T mTaskWrapper; |
|
||||||
protected File mTempFile; |
|
||||||
|
|
||||||
private List<IThreadTask> mTask = new ArrayList<>(); |
|
||||||
private ScheduledThreadPoolExecutor mTimer; |
|
||||||
|
|
||||||
/** |
|
||||||
* 进度刷新间隔 |
|
||||||
*/ |
|
||||||
private long mUpdateInterval = 1000; |
|
||||||
protected TaskRecord mRecord; |
|
||||||
protected boolean isCancel = false, isStop = false; |
|
||||||
private boolean isRuning = false; |
|
||||||
|
|
||||||
protected IRecordHandler mRecordHandler; |
|
||||||
protected ITaskManager mStateManager; |
|
||||||
protected IInfoTask mInfoTask; |
|
||||||
protected IThreadTaskBuilder mTTBuilder; |
|
||||||
|
|
||||||
protected AbsNormalLoader(T wrapper, IEventListener listener) { |
|
||||||
mListener = listener; |
|
||||||
mTaskWrapper = wrapper; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 启动线程任务 |
|
||||||
*/ |
|
||||||
protected abstract void handleTask(Looper looper); |
|
||||||
|
|
||||||
/** |
|
||||||
* 获取文件长度 |
|
||||||
*/ |
|
||||||
public abstract long getFileSize(); |
|
||||||
|
|
||||||
protected IEventListener getListener() { |
|
||||||
return mListener; |
|
||||||
} |
|
||||||
|
|
||||||
protected ITaskManager getStateManager() { |
|
||||||
return mStateManager; |
|
||||||
} |
|
||||||
|
|
||||||
public String getKey() { |
|
||||||
return mTaskWrapper.getKey(); |
|
||||||
} |
|
||||||
|
|
||||||
public List<IThreadTask> getTaskList() { |
|
||||||
return mTask; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 重置任务状态 |
|
||||||
*/ |
|
||||||
private void resetState() { |
|
||||||
closeTimer(); |
|
||||||
if (mTask != null && mTask.size() != 0) { |
|
||||||
for (int i = 0; i < mTask.size(); i++) { |
|
||||||
mTask.get(i).breakTask(); |
|
||||||
} |
|
||||||
mTask.clear(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void run() { |
|
||||||
checkComponent(); |
|
||||||
if (isRunning()) { |
|
||||||
ALog.d(TAG, String.format("任务【%s】正在执行,启动任务失败", mTaskWrapper.getKey())); |
|
||||||
return; |
|
||||||
} |
|
||||||
startFlow(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 开始流程 |
|
||||||
*/ |
|
||||||
private void startFlow() { |
|
||||||
if (isBreak()) { |
|
||||||
return; |
|
||||||
} |
|
||||||
Looper.prepare(); |
|
||||||
Looper looper = Looper.myLooper(); |
|
||||||
if (looper == Looper.getMainLooper()) { |
|
||||||
throw new IllegalThreadStateException("不能在主线程程序中调用Loader"); |
|
||||||
} |
|
||||||
isRuning = true; |
|
||||||
resetState(); |
|
||||||
onPostPre(); |
|
||||||
handleTask(looper); |
|
||||||
Looper.loop(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 预处理完成 |
|
||||||
*/ |
|
||||||
protected void onPostPre() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 延迟启动定时器 |
|
||||||
*/ |
|
||||||
protected long delayTimer() { |
|
||||||
return 1000; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 启动进度获取定时器 |
|
||||||
*/ |
|
||||||
protected synchronized void startTimer() { |
|
||||||
if (isBreak()) { |
|
||||||
return; |
|
||||||
} |
|
||||||
ALog.d(TAG, String.format("启动定时器,delayTimer = %s, updateInterval = %s", delayTimer(), |
|
||||||
mUpdateInterval)); |
|
||||||
closeTimer(); |
|
||||||
try { |
|
||||||
mTimer = new ScheduledThreadPoolExecutor(1); |
|
||||||
mTimer.scheduleWithFixedDelay(new Runnable() { |
|
||||||
@Override public void run() { |
|
||||||
// 线程池中是不抛异常的,没有日志,很难定位问题,需要手动try-catch
|
|
||||||
try { |
|
||||||
if (mStateManager == null) { |
|
||||||
ALog.e(TAG, "stateManager is null"); |
|
||||||
} else if (mStateManager.isCompleted() |
|
||||||
|| mStateManager.hasFailedBlock() |
|
||||||
|| !isRunning() |
|
||||||
|| isBreak()) { |
|
||||||
//ALog.d(TAG, "isComplete = " + mStateManager.isComplete()
|
|
||||||
// + "; isFail = " + mStateManager.isFail()
|
|
||||||
// + "; isRunning = " + isRunning()
|
|
||||||
// + "; isBreak = " + isBreak());
|
|
||||||
ThreadTaskManager.getInstance().removeTaskThread(mTaskWrapper.getKey()); |
|
||||||
closeTimer(); |
|
||||||
onDestroy(); |
|
||||||
} else if (mStateManager.getCurrentProgress() >= 0) { |
|
||||||
Log.d(TAG, "running..."); |
|
||||||
mListener.onProgress(mStateManager.getCurrentProgress()); |
|
||||||
} else { |
|
||||||
Log.d(TAG, "未知状态"); |
|
||||||
} |
|
||||||
} catch (Exception e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
} |
|
||||||
}, delayTimer(), mUpdateInterval, TimeUnit.MILLISECONDS); |
|
||||||
} catch (Exception e) { |
|
||||||
ALog.e(TAG, "启动定时器失败"); |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private synchronized void closeTimer() { |
|
||||||
if (mTimer != null && !mTimer.isShutdown()) { |
|
||||||
mTimer.shutdown(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public void onDestroy() { |
|
||||||
isRuning = false; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 设置定时器更新间隔 |
|
||||||
* |
|
||||||
* @param interval 单位毫秒,不能小于0 |
|
||||||
*/ |
|
||||||
protected void setUpdateInterval(long interval) { |
|
||||||
if (interval < 0) { |
|
||||||
ALog.w(TAG, "更新间隔不能小于0,默认为1000毫秒"); |
|
||||||
return; |
|
||||||
} |
|
||||||
mUpdateInterval = interval; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public synchronized boolean isRunning() { |
|
||||||
boolean b = ThreadTaskManager.getInstance().taskIsRunning(mTaskWrapper.getKey()); |
|
||||||
//ALog.d(TAG, "isRunning = " + b);
|
|
||||||
return b && isRuning; |
|
||||||
} |
|
||||||
|
|
||||||
@Override final public synchronized void cancel() { |
|
||||||
if (isCancel) { |
|
||||||
ALog.d(TAG, String.format("任务【%s】正在删除,删除任务失败", mTaskWrapper.getKey())); |
|
||||||
return; |
|
||||||
} |
|
||||||
if (mInfoTask != null){ |
|
||||||
mInfoTask.cancel(); |
|
||||||
} |
|
||||||
closeTimer(); |
|
||||||
isCancel = true; |
|
||||||
onCancel(); |
|
||||||
for (int i = 0; i < mTask.size(); i++) { |
|
||||||
IThreadTask task = mTask.get(i); |
|
||||||
if (task != null && !task.isThreadComplete()) { |
|
||||||
task.cancel(); |
|
||||||
} |
|
||||||
} |
|
||||||
ThreadTaskManager.getInstance().removeTaskThread(mTaskWrapper.getKey()); |
|
||||||
onPostCancel(); |
|
||||||
onDestroy(); |
|
||||||
mListener.onCancel(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 删除线程任务前的操作 |
|
||||||
*/ |
|
||||||
protected void onCancel() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 删除操作处理完成 |
|
||||||
*/ |
|
||||||
protected void onPostCancel() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
final public synchronized void stop() { |
|
||||||
if (isStop) { |
|
||||||
return; |
|
||||||
} |
|
||||||
if (mInfoTask != null){ |
|
||||||
mInfoTask.stop(); |
|
||||||
} |
|
||||||
closeTimer(); |
|
||||||
isStop = true; |
|
||||||
onStop(); |
|
||||||
for (int i = 0; i < mTask.size(); i++) { |
|
||||||
IThreadTask task = mTask.get(i); |
|
||||||
if (task != null && !task.isThreadComplete()) { |
|
||||||
task.stop(); |
|
||||||
} |
|
||||||
} |
|
||||||
ThreadTaskManager.getInstance().removeTaskThread(mTaskWrapper.getKey()); |
|
||||||
onPostStop(); |
|
||||||
onDestroy(); |
|
||||||
mListener.onStop(getCurrentProgress()); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 停止线程任务前的操作 |
|
||||||
*/ |
|
||||||
protected void onStop() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 停止操作完成 |
|
||||||
*/ |
|
||||||
protected void onPostStop() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 重试任务 |
|
||||||
*/ |
|
||||||
public void retryTask() { |
|
||||||
ALog.w(TAG, String.format("任务【%s】开始重试", mTaskWrapper.getKey())); |
|
||||||
startFlow(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 任务是否已经中断 |
|
||||||
* |
|
||||||
* @return {@code true}中断 |
|
||||||
*/ |
|
||||||
public boolean isBreak() { |
|
||||||
if (isCancel || isStop) { |
|
||||||
//closeTimer();
|
|
||||||
ALog.d(TAG, "isCancel = " + isCancel + ", isStop = " + isStop); |
|
||||||
ALog.d(TAG, String.format("任务【%s】已停止或取消了", mTaskWrapper.getKey())); |
|
||||||
return true; |
|
||||||
} |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 检查组件: {@link #mRecordHandler}、{@link #mInfoTask}、{@link #mStateManager}、{@link #mTTBuilder} |
|
||||||
*/ |
|
||||||
protected void checkComponent() { |
|
||||||
if (mRecordHandler == null) { |
|
||||||
throw new NullPointerException("任务记录组件为空"); |
|
||||||
} |
|
||||||
if (mInfoTask == null) { |
|
||||||
throw new NullPointerException(("文件信息组件为空")); |
|
||||||
} |
|
||||||
if (mStateManager == null) { |
|
||||||
throw new NullPointerException("任务状态管理组件为空"); |
|
||||||
} |
|
||||||
if (mTTBuilder == null) { |
|
||||||
throw new NullPointerException("线程任务组件为空"); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,153 +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.loader; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.inf.ITaskAdapter; |
|
||||||
import com.arialyy.aria.core.listener.IEventListener; |
|
||||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper; |
|
||||||
import com.arialyy.aria.exception.AriaException; |
|
||||||
import com.arialyy.aria.util.ALog; |
|
||||||
import com.arialyy.aria.util.CommonUtil; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by lyy on 2015/8/25. |
|
||||||
* HTTP\FTP单任务下载工具 |
|
||||||
*/ |
|
||||||
public abstract class AbsNormalLoaderAdapter implements ITaskAdapter { |
|
||||||
protected String TAG = CommonUtil.getClassName(getClass()); |
|
||||||
private IEventListener mListener; |
|
||||||
protected AbsNormalLoader mLoader; |
|
||||||
private AbsTaskWrapper mTaskWrapper; |
|
||||||
private boolean isStop = false, isCancel = false; |
|
||||||
|
|
||||||
protected AbsNormalLoaderAdapter() { |
|
||||||
} |
|
||||||
|
|
||||||
@Override public ITaskAdapter setParams(AbsTaskWrapper taskWrapper, IEventListener listener) { |
|
||||||
mTaskWrapper = taskWrapper; |
|
||||||
mListener = listener; |
|
||||||
mLoader = getLoader(); |
|
||||||
return this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 获取加载器 |
|
||||||
*/ |
|
||||||
public abstract AbsNormalLoader getLoader(); |
|
||||||
|
|
||||||
/** |
|
||||||
* 获取构造器 |
|
||||||
*/ |
|
||||||
public abstract LoaderStructure BuildLoaderStructure(); |
|
||||||
|
|
||||||
@Override public String getKey() { |
|
||||||
return mTaskWrapper.getKey(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public long getFileSize() { |
|
||||||
return mLoader.getFileSize(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 获取当前下载位置 |
|
||||||
*/ |
|
||||||
@Override public long getCurrentLocation() { |
|
||||||
return mLoader.getCurrentProgress(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public boolean isRunning() { |
|
||||||
return mLoader.isRunning(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 取消下载 |
|
||||||
*/ |
|
||||||
@Override public void cancel() { |
|
||||||
isCancel = true; |
|
||||||
mLoader.cancel(); |
|
||||||
onCancel(); |
|
||||||
} |
|
||||||
|
|
||||||
protected void onCancel() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 停止下载 |
|
||||||
*/ |
|
||||||
@Override public void stop() { |
|
||||||
isStop = true; |
|
||||||
mLoader.stop(); |
|
||||||
onStop(); |
|
||||||
} |
|
||||||
|
|
||||||
protected void onStop() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 多线程断点续传下载文件,开始下载 |
|
||||||
*/ |
|
||||||
@Override public void start() { |
|
||||||
if (isStop || isCancel) { |
|
||||||
ALog.w(TAG, "启动任务失败,任务已停止或已取消"); |
|
||||||
return; |
|
||||||
} |
|
||||||
mListener.onPre(); |
|
||||||
// 如果网址没有变,而服务器端端文件改变,以下代码就没有用了
|
|
||||||
//if (mTaskWrapper.getEntity().getFileSize() <= 1
|
|
||||||
// || mTaskWrapper.isRefreshInfo()
|
|
||||||
// || mTaskWrapper.getRequestType() == AbsTaskWrapper.D_FTP
|
|
||||||
// || mTaskWrapper.getState() == IEntity.STATE_FAIL) {
|
|
||||||
// new Thread(createInfoThread()).create();
|
|
||||||
//} else {
|
|
||||||
// mDownloader.create();
|
|
||||||
//}
|
|
||||||
|
|
||||||
BuildLoaderStructure(); |
|
||||||
new Thread(mLoader).start(); |
|
||||||
onStart(); |
|
||||||
} |
|
||||||
|
|
||||||
protected void onStart() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
public boolean isStop() { |
|
||||||
return isStop; |
|
||||||
} |
|
||||||
|
|
||||||
public boolean isCancel() { |
|
||||||
return isCancel; |
|
||||||
} |
|
||||||
|
|
||||||
public IEventListener getListener() { |
|
||||||
return mListener; |
|
||||||
} |
|
||||||
|
|
||||||
protected void fail(AriaException e, boolean needRetry) { |
|
||||||
if (isStop || isCancel) { |
|
||||||
return; |
|
||||||
} |
|
||||||
mListener.onFail(needRetry, e); |
|
||||||
mLoader.onDestroy(); |
|
||||||
} |
|
||||||
|
|
||||||
public AbsTaskWrapper getTaskWrapper() { |
|
||||||
return mTaskWrapper; |
|
||||||
} |
|
||||||
} |
|
@ -1,90 +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.loader; |
|
||||||
|
|
||||||
import android.os.Handler; |
|
||||||
import com.arialyy.aria.core.TaskRecord; |
|
||||||
import com.arialyy.aria.core.ThreadRecord; |
|
||||||
import com.arialyy.aria.core.common.AbsNormalEntity; |
|
||||||
import com.arialyy.aria.core.common.SubThreadConfig; |
|
||||||
import com.arialyy.aria.core.task.IThreadTaskAdapter; |
|
||||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper; |
|
||||||
import com.arialyy.aria.util.CommonUtil; |
|
||||||
import java.io.File; |
|
||||||
|
|
||||||
public abstract class AbsNormalTTBuilderAdapter { |
|
||||||
protected String TAG = CommonUtil.getClassName(this); |
|
||||||
protected AbsTaskWrapper wrapper; |
|
||||||
private File tempFile; |
|
||||||
|
|
||||||
public AbsNormalTTBuilderAdapter() { |
|
||||||
} |
|
||||||
|
|
||||||
protected void setWrapper(AbsTaskWrapper wrapper) { |
|
||||||
this.wrapper = wrapper; |
|
||||||
tempFile = new File(((AbsNormalEntity) wrapper.getEntity()).getFilePath()); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 创建线程任务适配器 |
|
||||||
*/ |
|
||||||
public abstract IThreadTaskAdapter getAdapter(SubThreadConfig config); |
|
||||||
|
|
||||||
/** |
|
||||||
* 处理新任务 |
|
||||||
* |
|
||||||
* @param record 任务记录 |
|
||||||
* @param totalThreadNum 任务的线程总数 |
|
||||||
* @return {@code true}创建新任务成功 |
|
||||||
*/ |
|
||||||
public abstract boolean handleNewTask(TaskRecord record, int totalThreadNum); |
|
||||||
|
|
||||||
/** |
|
||||||
* SubThreadConfig 模版,如果不使用该方法创建配置,则默认使用{@link #createNormalSubThreadConfig(Handler, ThreadRecord, |
|
||||||
* boolean, int)}创建配置 |
|
||||||
*/ |
|
||||||
protected SubThreadConfig getSubThreadConfig(Handler stateHandler, ThreadRecord threadRecord, |
|
||||||
boolean isBlock, int startNum) { |
|
||||||
return createNormalSubThreadConfig(stateHandler, threadRecord, isBlock, startNum); |
|
||||||
} |
|
||||||
|
|
||||||
private SubThreadConfig createNormalSubThreadConfig(Handler stateHandler, |
|
||||||
ThreadRecord threadRecord, |
|
||||||
boolean isBlock, int startNum) { |
|
||||||
SubThreadConfig config = new SubThreadConfig(); |
|
||||||
config.url = getEntity().isRedirect() ? getEntity().getRedirectUrl() : getEntity().getUrl(); |
|
||||||
config.tempFile = |
|
||||||
isBlock ? new File( |
|
||||||
String.format(IRecordHandler.SUB_PATH, tempFile.getPath(), threadRecord.threadId)) |
|
||||||
: tempFile; |
|
||||||
config.isBlock = isBlock; |
|
||||||
config.startThreadNum = startNum; |
|
||||||
config.taskWrapper = wrapper; |
|
||||||
config.record = threadRecord; |
|
||||||
config.stateHandler = stateHandler; |
|
||||||
config.threadType = SubThreadConfig.getThreadType(wrapper.getRequestType()); |
|
||||||
config.updateInterval = SubThreadConfig.getUpdateInterval(wrapper.getRequestType()); |
|
||||||
return config; |
|
||||||
} |
|
||||||
|
|
||||||
protected AbsNormalEntity getEntity() { |
|
||||||
return (AbsNormalEntity) wrapper.getEntity(); |
|
||||||
} |
|
||||||
|
|
||||||
protected File getTempFile() { |
|
||||||
return tempFile; |
|
||||||
} |
|
||||||
} |
|
@ -1,311 +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.loader; |
|
||||||
|
|
||||||
import android.os.Bundle; |
|
||||||
import android.os.Handler; |
|
||||||
import android.os.Looper; |
|
||||||
import android.os.Message; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.TaskRecord; |
|
||||||
import com.arialyy.aria.core.inf.ITaskManager; |
|
||||||
import com.arialyy.aria.util.ALog; |
|
||||||
import com.arialyy.aria.util.CommonUtil; |
|
||||||
import com.arialyy.aria.util.FileUtil; |
|
||||||
|
|
||||||
import java.io.File; |
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.List; |
|
||||||
import java.util.concurrent.atomic.AtomicInteger; |
|
||||||
|
|
||||||
/** |
|
||||||
* 线程任务管理器,用于处理多线程下载时任务的状态回调 |
|
||||||
*/ |
|
||||||
public class GroupSubThreadStateManager implements ITaskManager { |
|
||||||
private final String TAG = CommonUtil.getClassName(this); |
|
||||||
|
|
||||||
/** |
|
||||||
* 任务状态回调 |
|
||||||
*/ |
|
||||||
private Handler mHandler;//SimpleSchedulers
|
|
||||||
private int mThreadNum; // 启动的线程总数
|
|
||||||
private AtomicInteger mCancelNum = new AtomicInteger(0); // 已经取消的线程的数
|
|
||||||
private AtomicInteger mStopNum = new AtomicInteger(0); // 已经停止的线程数
|
|
||||||
private AtomicInteger mFailNum = new AtomicInteger(0); // 失败的线程数
|
|
||||||
private AtomicInteger mCompleteNum = new AtomicInteger(0); // 完成的线程数
|
|
||||||
private long mProgress; //当前总进度
|
|
||||||
private TaskRecord mTaskRecord; // 任务记录
|
|
||||||
private Looper mLooper; |
|
||||||
private String mKey; |
|
||||||
/** |
|
||||||
* @param handler 任务事件 |
|
||||||
*/ |
|
||||||
public GroupSubThreadStateManager(Handler handler,String key) { |
|
||||||
mHandler = handler; |
|
||||||
mKey = key; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void setLooper(TaskRecord taskRecord, Looper looper) { |
|
||||||
mTaskRecord = taskRecord; |
|
||||||
mThreadNum = mTaskRecord.threadNum; |
|
||||||
mLooper = looper; |
|
||||||
} |
|
||||||
|
|
||||||
private void checkLooper() { |
|
||||||
if (mTaskRecord == null) { |
|
||||||
throw new NullPointerException("任务记录为空"); |
|
||||||
} |
|
||||||
if (mLooper == null) { |
|
||||||
throw new NullPointerException("Looper为空"); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private Handler.Callback callback = new Handler.Callback() { |
|
||||||
@Override public boolean handleMessage(Message msg) { |
|
||||||
checkLooper(); |
|
||||||
switch (msg.what) { |
|
||||||
case STATE_STOP: |
|
||||||
mStopNum.getAndIncrement(); |
|
||||||
if (isStop()) { |
|
||||||
quitLooper(); |
|
||||||
} |
|
||||||
sendMessageFromMsg(msg); |
|
||||||
break; |
|
||||||
case STATE_CANCEL: |
|
||||||
mCancelNum.getAndIncrement(); |
|
||||||
if (isCancel()) { |
|
||||||
quitLooper(); |
|
||||||
} |
|
||||||
sendMessageFromMsg(msg); |
|
||||||
break; |
|
||||||
case STATE_FAIL: |
|
||||||
mFailNum.getAndIncrement(); |
|
||||||
if (hasFailedBlock()) { |
|
||||||
sendMessageFromMsg(msg); |
|
||||||
|
|
||||||
/* Bundle b = msg.getData(); |
|
||||||
mListener.onFail(b.getBoolean(DATA_RETRY, false), |
|
||||||
(AriaException) b.getSerializable(DATA_ERROR_INFO));*/ |
|
||||||
quitLooper(); |
|
||||||
} |
|
||||||
//sendMessageFromMsg(msg);
|
|
||||||
break; |
|
||||||
case STATE_COMPLETE: |
|
||||||
mCompleteNum.getAndIncrement(); |
|
||||||
if (isCompleted()) { |
|
||||||
ALog.d(TAG, "isComplete, completeNum = " + mCompleteNum); |
|
||||||
//if (mTaskRecord.taskType == ITaskWrapper.D_SFTP) {
|
|
||||||
// mergerSFtp();
|
|
||||||
// mListener.onComplete();
|
|
||||||
//} else
|
|
||||||
|
|
||||||
if (mTaskRecord.isBlock) { |
|
||||||
/*if (mergeFile()) { |
|
||||||
mListener.onComplete(); |
|
||||||
} else { |
|
||||||
mListener.onFail(false, null); |
|
||||||
}*/ |
|
||||||
if (!mergeFile()) { |
|
||||||
Bundle b=msg.getData(); |
|
||||||
b.putBoolean(ITaskManager.DATA_RETRY, false); |
|
||||||
msg.setData(b); |
|
||||||
msg.what = STATE_FAIL; |
|
||||||
sendMessageFromMsg(msg); |
|
||||||
} |
|
||||||
sendMessageFromMsg(msg); |
|
||||||
} else { |
|
||||||
sendMessageFromMsg(msg); |
|
||||||
//mListener.onComplete();
|
|
||||||
} |
|
||||||
quitLooper(); |
|
||||||
}else if (hasFailedBlock()) { |
|
||||||
sendMessageFromMsg(msg); |
|
||||||
quitLooper(); |
|
||||||
} |
|
||||||
break; |
|
||||||
case STATE_RUNNING: |
|
||||||
Bundle b = msg.getData(); |
|
||||||
if (b != null) { |
|
||||||
long len = b.getLong(ITaskManager.DATA_ADD_LEN, 0); |
|
||||||
mProgress += len; |
|
||||||
} |
|
||||||
msg.obj=mProgress; |
|
||||||
sendMessageFromMsg(msg); |
|
||||||
|
|
||||||
break; |
|
||||||
case STATE_UPDATE_PROGRESS: |
|
||||||
|
|
||||||
if (msg.obj == null) { |
|
||||||
mProgress = updateBlockProgress(); |
|
||||||
} else if (msg.obj instanceof Long) { |
|
||||||
mProgress = (long) msg.obj; |
|
||||||
} |
|
||||||
msg.obj=mProgress; |
|
||||||
sendMessageFromMsg(msg); |
|
||||||
break; |
|
||||||
} |
|
||||||
return false; |
|
||||||
} |
|
||||||
public void sendMessageFromMsg(Message msg){ |
|
||||||
Message mMsg=mHandler.obtainMessage(); |
|
||||||
Bundle b=mMsg.getData(); |
|
||||||
b.putString(ITaskManager.DATA_THREAD_NAME,mKey); |
|
||||||
msg.setData(b); |
|
||||||
mMsg.copyFrom(msg); |
|
||||||
mHandler.sendMessage(mMsg); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
@Override public void updateCurrentProgress(long currentProgress) { |
|
||||||
mProgress = currentProgress; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 退出looper循环 |
|
||||||
*/ |
|
||||||
private void quitLooper() { |
|
||||||
mLooper.quit(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 获取当前任务下载进度 |
|
||||||
* |
|
||||||
* @return 当前任务下载进度 |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public long getCurrentProgress() { |
|
||||||
return mProgress; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public Handler.Callback getHandlerCallback() { |
|
||||||
return callback; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 所有子线程是否都已经停止 |
|
||||||
*/ |
|
||||||
public boolean isStop() { |
|
||||||
//ALog.d(TAG,
|
|
||||||
// String.format("isStop; stopNum: %s, cancelNum: %s, failNum: %s, completeNum: %s", mStopNum,
|
|
||||||
// mCancelNum, mFailNum, mCompleteNum));
|
|
||||||
return mStopNum.get() == mThreadNum || mStopNum.get() + mCompleteNum.get() == mThreadNum; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 所有子线程是否都已经失败 |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public boolean hasFailedBlock() { |
|
||||||
//ALog.d(TAG,
|
|
||||||
// String.format("isFail; stopNum: %s, cancelNum: %s, failNum: %s, completeNum: %s", mStopNum,
|
|
||||||
// mCancelNum, mFailNum, mCompleteNum));
|
|
||||||
return mCompleteNum.get() != mThreadNum |
|
||||||
&& (mFailNum.get() == mThreadNum || mFailNum.get() + mCompleteNum.get() == mThreadNum); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 所有子线程是否都已经完成 |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public boolean isCompleted() { |
|
||||||
//ALog.d(TAG,
|
|
||||||
// String.format("isComplete; stopNum: %s, cancelNum: %s, failNum: %s, completeNum: %s",
|
|
||||||
// mStopNum,
|
|
||||||
// mCancelNum, mFailNum, mCompleteNum));
|
|
||||||
return mCompleteNum.get() == mThreadNum; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 所有子线程是否都已经取消 |
|
||||||
*/ |
|
||||||
public boolean isCancel() { |
|
||||||
//ALog.d(TAG, String.format("isCancel; stopNum: %s, cancelNum: %s, failNum: %s, completeNum: %s",
|
|
||||||
// mStopNum,
|
|
||||||
// mCancelNum, mFailNum, mCompleteNum));
|
|
||||||
return mCancelNum.get() == mThreadNum; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 更新分块任务s的真实进度 |
|
||||||
*/ |
|
||||||
private long updateBlockProgress() { |
|
||||||
long size = 0; |
|
||||||
for (int i = 0, len = mTaskRecord.threadRecords.size(); i < len; i++) { |
|
||||||
File temp = new File(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, i)); |
|
||||||
if (temp.exists()) { |
|
||||||
size += temp.length(); |
|
||||||
} |
|
||||||
} |
|
||||||
return size; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 合并sftp的分块 |
|
||||||
*/ |
|
||||||
private boolean mergerSFtp() { |
|
||||||
if (mTaskRecord.threadNum == 1) { |
|
||||||
File partFile = new File(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, 0)); |
|
||||||
return partFile.renameTo(new File(mTaskRecord.filePath)); |
|
||||||
} |
|
||||||
|
|
||||||
List<String> partPath = new ArrayList<>(); |
|
||||||
for (int i = 0, len = mTaskRecord.threadNum; i < len; i++) { |
|
||||||
partPath.add(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, i)); |
|
||||||
} |
|
||||||
FileUtil.mergeSFtpFile(mTaskRecord.filePath, partPath, mTaskRecord.fileLength); |
|
||||||
for (String pp : partPath) { |
|
||||||
FileUtil.deleteFile(pp); |
|
||||||
} |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 合并文件 |
|
||||||
* |
|
||||||
* @return {@code true} 合并成功,{@code false}合并失败 |
|
||||||
*/ |
|
||||||
private boolean mergeFile() { |
|
||||||
if (mTaskRecord.threadNum == 1) { |
|
||||||
File partFile = new File(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, 0)); |
|
||||||
return partFile.renameTo(new File(mTaskRecord.filePath)); |
|
||||||
} |
|
||||||
|
|
||||||
List<String> partPath = new ArrayList<>(); |
|
||||||
for (int i = 0, len = mTaskRecord.threadNum; i < len; i++) { |
|
||||||
partPath.add(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, i)); |
|
||||||
} |
|
||||||
boolean isSuccess = FileUtil.mergeFile(mTaskRecord.filePath, partPath); |
|
||||||
if (isSuccess) { |
|
||||||
for (String pp : partPath) { |
|
||||||
FileUtil.deleteFile(pp); |
|
||||||
} |
|
||||||
File targetFile = new File(mTaskRecord.filePath); |
|
||||||
if (targetFile.exists() && targetFile.length() > mTaskRecord.fileLength) { |
|
||||||
ALog.e(TAG, String.format("任务【%s】分块文件合并失败,下载长度超出文件真实长度,downloadLen: %s,fileSize: %s", |
|
||||||
targetFile.getName(), targetFile.length(), mTaskRecord.fileLength)); |
|
||||||
return false; |
|
||||||
} |
|
||||||
return true; |
|
||||||
} else { |
|
||||||
ALog.e(TAG, "合并失败"); |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void accept(ILoaderVisitor visitor) { |
|
||||||
visitor.addComponent(this); |
|
||||||
} |
|
||||||
} |
|
@ -1,62 +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.loader; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.common.AbsEntity; |
|
||||||
import com.arialyy.aria.core.common.CompleteInfo; |
|
||||||
import com.arialyy.aria.exception.AriaException; |
|
||||||
|
|
||||||
/** |
|
||||||
* 任务信息采集 |
|
||||||
*/ |
|
||||||
public interface IInfoTask extends ILoaderComponent { |
|
||||||
|
|
||||||
/** |
|
||||||
* 执行任务 |
|
||||||
*/ |
|
||||||
void run(); |
|
||||||
|
|
||||||
/** |
|
||||||
* 设置回调 |
|
||||||
*/ |
|
||||||
void setCallback(Callback callback); |
|
||||||
|
|
||||||
/** |
|
||||||
* 任务停止 |
|
||||||
*/ |
|
||||||
void stop(); |
|
||||||
|
|
||||||
/** |
|
||||||
* 任务取消 |
|
||||||
*/ |
|
||||||
void cancel(); |
|
||||||
|
|
||||||
interface Callback { |
|
||||||
/** |
|
||||||
* 处理完成 |
|
||||||
* |
|
||||||
* @param info 一些回调的信息 |
|
||||||
*/ |
|
||||||
void onSucceed(String key, CompleteInfo info); |
|
||||||
|
|
||||||
/** |
|
||||||
* 请求失败 |
|
||||||
* |
|
||||||
* @param e 错误信息 |
|
||||||
*/ |
|
||||||
void onFail(AbsEntity entity, AriaException e, boolean needRetry); |
|
||||||
} |
|
||||||
} |
|
@ -1,43 +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.loader; |
|
||||||
|
|
||||||
public interface ILoader extends Runnable { |
|
||||||
|
|
||||||
//void start();
|
|
||||||
|
|
||||||
/** |
|
||||||
* 任务是否在执行 |
|
||||||
* |
|
||||||
* @return true 任务执行中 |
|
||||||
*/ |
|
||||||
boolean isRunning(); |
|
||||||
|
|
||||||
void cancel(); |
|
||||||
|
|
||||||
void stop(); |
|
||||||
|
|
||||||
/** |
|
||||||
* 任务是否被中断(停止,取消) |
|
||||||
* |
|
||||||
* @return true 任务中断,false 任务没有中断 |
|
||||||
*/ |
|
||||||
boolean isBreak(); |
|
||||||
|
|
||||||
String getKey(); |
|
||||||
|
|
||||||
long getCurrentProgress(); |
|
||||||
} |
|
@ -1,47 +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.loader; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.TaskRecord; |
|
||||||
import com.arialyy.aria.core.task.IThreadTask; |
|
||||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper; |
|
||||||
import com.arialyy.aria.core.common.SubThreadConfig; |
|
||||||
|
|
||||||
/** |
|
||||||
* @author lyy |
|
||||||
* Date: 2019-09-18 |
|
||||||
*/ |
|
||||||
public interface ILoaderAdapter { |
|
||||||
|
|
||||||
/** |
|
||||||
* 处理新任务 |
|
||||||
* |
|
||||||
* @param record 任务记录 |
|
||||||
* @param totalThreadNum 任务的线程总数 |
|
||||||
* @return {@code true}创建新任务成功 |
|
||||||
*/ |
|
||||||
boolean handleNewTask(TaskRecord record, int totalThreadNum); |
|
||||||
|
|
||||||
/** |
|
||||||
* 创建线程任务 |
|
||||||
*/ |
|
||||||
IThreadTask createThreadTask(SubThreadConfig config); |
|
||||||
|
|
||||||
/** |
|
||||||
* 处理任务记录 |
|
||||||
*/ |
|
||||||
IRecordHandler recordHandler(AbsTaskWrapper wrapper); |
|
||||||
} |
|
@ -1,44 +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.loader; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.inf.ITaskManager; |
|
||||||
|
|
||||||
/** |
|
||||||
* 加载器访问者 |
|
||||||
*/ |
|
||||||
public interface ILoaderVisitor { |
|
||||||
|
|
||||||
/** |
|
||||||
* 处理任务记录 |
|
||||||
*/ |
|
||||||
void addComponent(IRecordHandler recordHandler); |
|
||||||
|
|
||||||
/** |
|
||||||
* 处理任务的文件信息 |
|
||||||
*/ |
|
||||||
void addComponent(IInfoTask infoTask); |
|
||||||
|
|
||||||
/** |
|
||||||
* 线程状态 |
|
||||||
*/ |
|
||||||
void addComponent(ITaskManager threadState); |
|
||||||
|
|
||||||
/** |
|
||||||
* 构造线程任务 |
|
||||||
*/ |
|
||||||
void addComponent(IThreadTaskBuilder builder); |
|
||||||
} |
|
@ -1,87 +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.loader; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.TaskRecord; |
|
||||||
import com.arialyy.aria.core.ThreadRecord; |
|
||||||
|
|
||||||
/** |
|
||||||
* @author lyy |
|
||||||
* Date: 2019-09-18 |
|
||||||
*/ |
|
||||||
public interface IRecordHandler extends ILoaderComponent { |
|
||||||
|
|
||||||
int TYPE_DOWNLOAD = 1; |
|
||||||
int TYPE_UPLOAD = 2; |
|
||||||
int TYPE_M3U8_VOD = 3; |
|
||||||
int TYPE_M3U8_LIVE = 4; |
|
||||||
|
|
||||||
String STATE = "_state_"; |
|
||||||
String RECORD = "_record_"; |
|
||||||
/** |
|
||||||
* 小于1m的文件不启用多线程 |
|
||||||
*/ |
|
||||||
long SUB_LEN = 1024 * 1024; |
|
||||||
|
|
||||||
/** |
|
||||||
* 分块文件路径,文件路径.线程id.part |
|
||||||
*/ |
|
||||||
String SUB_PATH = "%s.%s.part"; |
|
||||||
|
|
||||||
/** |
|
||||||
* 获取任务记录 |
|
||||||
*/ |
|
||||||
TaskRecord getRecord(long fileSize); |
|
||||||
|
|
||||||
/** |
|
||||||
* 记录处理前的操作,可用来删除任务记录 |
|
||||||
*/ |
|
||||||
void onPre(); |
|
||||||
|
|
||||||
/** |
|
||||||
* 处理任务记录 |
|
||||||
*/ |
|
||||||
void handlerTaskRecord(TaskRecord record); |
|
||||||
|
|
||||||
/** |
|
||||||
* 处理线程任务 |
|
||||||
* |
|
||||||
* @param record 任务记录 |
|
||||||
* @param threadId 线程id |
|
||||||
* @param startL 线程开始位置 |
|
||||||
* @param endL 线程结束位置 |
|
||||||
*/ |
|
||||||
ThreadRecord createThreadRecord(TaskRecord record, int threadId, long startL, long endL); |
|
||||||
|
|
||||||
/** |
|
||||||
* 新任务创建任务记录 |
|
||||||
*/ |
|
||||||
TaskRecord createTaskRecord(int threadNum); |
|
||||||
|
|
||||||
/** |
|
||||||
* 配置新任务的线程数 |
|
||||||
* |
|
||||||
* @return 新任务的线程数 |
|
||||||
*/ |
|
||||||
int initTaskThreadNum(); |
|
||||||
|
|
||||||
/** |
|
||||||
* 检查任务是否已完成 |
|
||||||
* |
|
||||||
* @return true 任务已完成 |
|
||||||
*/ |
|
||||||
boolean checkTaskCompleted(); |
|
||||||
} |
|
@ -1,37 +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.loader; |
|
||||||
|
|
||||||
import android.os.Handler; |
|
||||||
import com.arialyy.aria.core.TaskRecord; |
|
||||||
import com.arialyy.aria.core.task.IThreadTask; |
|
||||||
import java.util.List; |
|
||||||
|
|
||||||
/** |
|
||||||
* 线程任务构造器 |
|
||||||
*/ |
|
||||||
public interface IThreadTaskBuilder extends ILoaderComponent { |
|
||||||
|
|
||||||
/** |
|
||||||
* 构造线程任务 |
|
||||||
*/ |
|
||||||
List<IThreadTask> buildThreadTask(TaskRecord record, Handler stateHandler); |
|
||||||
|
|
||||||
/** |
|
||||||
* 获取创建的线程任务数,需要先调用{@link #buildThreadTask(TaskRecord, Handler)}方法才能获取创建的线程任务数 |
|
||||||
*/ |
|
||||||
int getCreatedThreadNum(); |
|
||||||
} |
|
@ -1,45 +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.loader; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.inf.ITaskManager; |
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.List; |
|
||||||
|
|
||||||
public class LoaderStructure { |
|
||||||
private List<ILoaderComponent> parts = new ArrayList<>(); |
|
||||||
|
|
||||||
public void accept(ILoaderVisitor visitor) { |
|
||||||
|
|
||||||
for (ILoaderComponent part : parts) { |
|
||||||
part.accept(visitor); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 将组件加入到集合,必须添加以下集合: |
|
||||||
* 1 {@link IRecordHandler} |
|
||||||
* 2 {@link IInfoTask} |
|
||||||
* 3 {@link ITaskManager} |
|
||||||
* 4 {@link IThreadTaskBuilder} |
|
||||||
* |
|
||||||
* @param component 待添加的组件 |
|
||||||
*/ |
|
||||||
public LoaderStructure addComponent(ILoaderComponent component) { |
|
||||||
parts.add(component); |
|
||||||
return this; |
|
||||||
} |
|
||||||
} |
|
@ -1,169 +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.loader; |
|
||||||
|
|
||||||
import android.os.Handler; |
|
||||||
import android.os.Looper; |
|
||||||
import com.arialyy.aria.core.common.AbsEntity; |
|
||||||
import com.arialyy.aria.core.common.AbsNormalEntity; |
|
||||||
import com.arialyy.aria.core.common.CompleteInfo; |
|
||||||
import com.arialyy.aria.core.event.EventMsgUtil; |
|
||||||
import com.arialyy.aria.core.inf.ITaskManager; |
|
||||||
import com.arialyy.aria.core.listener.IDLoadListener; |
|
||||||
import com.arialyy.aria.core.listener.IEventListener; |
|
||||||
import com.arialyy.aria.core.manager.ThreadTaskManager; |
|
||||||
import com.arialyy.aria.core.task.IThreadTask; |
|
||||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper; |
|
||||||
import com.arialyy.aria.exception.AriaException; |
|
||||||
import com.arialyy.aria.util.FileUtil; |
|
||||||
import java.io.File; |
|
||||||
|
|
||||||
/** |
|
||||||
* 单文件 |
|
||||||
*/ |
|
||||||
public class NormalLoader<T extends AbsTaskWrapper> extends AbsNormalLoader<T> { |
|
||||||
private int startThreadNum; //启动的线程数
|
|
||||||
protected boolean isComplete = false; |
|
||||||
private Looper looper; |
|
||||||
|
|
||||||
public NormalLoader(T wrapper, IEventListener listener) { |
|
||||||
super(wrapper, listener); |
|
||||||
mTempFile = new File(getEntity().getFilePath()); |
|
||||||
EventMsgUtil.getDefault().register(this); |
|
||||||
setUpdateInterval(wrapper.getConfig().getUpdateInterval()); |
|
||||||
} |
|
||||||
|
|
||||||
public AbsNormalEntity getEntity() { |
|
||||||
return (AbsNormalEntity) mTaskWrapper.getEntity(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public long getFileSize() { |
|
||||||
return getEntity().getFileSize(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 设置最大下载/上传速度AbsFtpInfoThread |
|
||||||
* |
|
||||||
* @param maxSpeed 单位为:kb |
|
||||||
*/ |
|
||||||
protected void setMaxSpeed(int maxSpeed) { |
|
||||||
for (IThreadTask threadTask : getTaskList()) { |
|
||||||
if (threadTask != null && startThreadNum > 0) { |
|
||||||
threadTask.setMaxSpeed(maxSpeed / startThreadNum); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void onDestroy() { |
|
||||||
super.onDestroy(); |
|
||||||
EventMsgUtil.getDefault().unRegister(this); |
|
||||||
} |
|
||||||
|
|
||||||
///**
|
|
||||||
// * 如果使用"Content-Disposition"中的文件名,需要更新{@link #mTempFile}的路径
|
|
||||||
// */
|
|
||||||
//public void updateTempFile() {
|
|
||||||
// if (!mTempFile.getPath().equals(getEntity().getFilePath())) {
|
|
||||||
// boolean b = mTempFile.renameTo(new File(getEntity().getFilePath()));
|
|
||||||
// ALog.d(TAG, String.format("更新tempFile文件名%s", b ? "成功" : "失败"));
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
protected Looper getLooper() { |
|
||||||
return looper; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 启动单线程任务 |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public void handleTask(Looper looper) { |
|
||||||
if (isBreak() || isComplete) { |
|
||||||
return; |
|
||||||
} |
|
||||||
this.looper = looper; |
|
||||||
mInfoTask.run(); |
|
||||||
} |
|
||||||
|
|
||||||
protected void startThreadTask() { |
|
||||||
if (isBreak()){ |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
if (getListener() instanceof IDLoadListener) { |
|
||||||
((IDLoadListener) getListener()).onPostPre(getEntity().getFileSize()); |
|
||||||
} |
|
||||||
File file = new File(getEntity().getFilePath()); |
|
||||||
if (file.getParentFile() != null && !file.getParentFile().exists()) { |
|
||||||
FileUtil.createDir(file.getPath()); |
|
||||||
} |
|
||||||
// 处理记录、初始化状态管理器
|
|
||||||
mRecord = mRecordHandler.getRecord(getFileSize()); |
|
||||||
mStateManager.setLooper(mRecord, looper); |
|
||||||
getTaskList().addAll(mTTBuilder.buildThreadTask(mRecord, |
|
||||||
new Handler(looper, mStateManager.getHandlerCallback()))); |
|
||||||
startThreadNum = mTTBuilder.getCreatedThreadNum(); |
|
||||||
|
|
||||||
mStateManager.updateCurrentProgress(getEntity().getCurrentProgress()); |
|
||||||
if (mStateManager.getCurrentProgress() > 0) { |
|
||||||
getListener().onResume(mStateManager.getCurrentProgress()); |
|
||||||
} else { |
|
||||||
getListener().onStart(mStateManager.getCurrentProgress()); |
|
||||||
} |
|
||||||
|
|
||||||
// 启动线程任务
|
|
||||||
for (IThreadTask threadTask : getTaskList()) { |
|
||||||
ThreadTaskManager.getInstance().startThread(mTaskWrapper.getKey(), threadTask); |
|
||||||
} |
|
||||||
|
|
||||||
// 启动定时器
|
|
||||||
startTimer(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public long getCurrentProgress() { |
|
||||||
return isRunning() ? mStateManager.getCurrentProgress() : getEntity().getCurrentProgress(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void addComponent(IRecordHandler recordHandler) { |
|
||||||
mRecordHandler = recordHandler; |
|
||||||
if (recordHandler.checkTaskCompleted()) { |
|
||||||
mRecord.deleteData(); |
|
||||||
isComplete = true; |
|
||||||
getListener().onComplete(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void addComponent(IInfoTask infoTask) { |
|
||||||
mInfoTask = infoTask; |
|
||||||
infoTask.setCallback(new IInfoTask.Callback() { |
|
||||||
@Override public void onSucceed(String key, CompleteInfo info) { |
|
||||||
startThreadTask(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void onFail(AbsEntity entity, AriaException e, boolean needRetry) { |
|
||||||
getListener().onFail(needRetry, e); |
|
||||||
} |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void addComponent(ITaskManager threadState) { |
|
||||||
mStateManager = threadState; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void addComponent(IThreadTaskBuilder builder) { |
|
||||||
mTTBuilder = builder; |
|
||||||
} |
|
||||||
} |
|
@ -1,166 +0,0 @@ |
|||||||
package com.arialyy.aria.core.loader; |
|
||||||
|
|
||||||
import android.os.Bundle; |
|
||||||
import android.os.Handler; |
|
||||||
import android.os.Message; |
|
||||||
import com.arialyy.aria.core.TaskRecord; |
|
||||||
import com.arialyy.aria.core.ThreadRecord; |
|
||||||
import com.arialyy.aria.core.common.AbsNormalEntity; |
|
||||||
import com.arialyy.aria.core.common.SubThreadConfig; |
|
||||||
import com.arialyy.aria.core.download.DGTaskWrapper; |
|
||||||
import com.arialyy.aria.core.inf.ITaskManager; |
|
||||||
import com.arialyy.aria.core.task.IThreadTask; |
|
||||||
import com.arialyy.aria.core.task.ThreadTask; |
|
||||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper; |
|
||||||
import com.arialyy.aria.util.ALog; |
|
||||||
import com.arialyy.aria.util.CommonUtil; |
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.List; |
|
||||||
|
|
||||||
public final class NormalTTBuilder implements IThreadTaskBuilder { |
|
||||||
protected String TAG = CommonUtil.getClassName(this); |
|
||||||
|
|
||||||
private Handler mStateHandler; |
|
||||||
private AbsTaskWrapper mWrapper; |
|
||||||
private TaskRecord mRecord; |
|
||||||
private int mTotalThreadNum; |
|
||||||
private int mStartThreadNum; |
|
||||||
private AbsNormalTTBuilderAdapter mAdapter; |
|
||||||
|
|
||||||
public NormalTTBuilder(AbsTaskWrapper wrapper, AbsNormalTTBuilderAdapter adapter) { |
|
||||||
if (wrapper instanceof DGTaskWrapper) { |
|
||||||
throw new AssertionError("NormalTTBuilder 不适用于组合任务"); |
|
||||||
} |
|
||||||
mWrapper = wrapper; |
|
||||||
mAdapter = adapter; |
|
||||||
mAdapter.setWrapper(wrapper); |
|
||||||
} |
|
||||||
|
|
||||||
protected AbsNormalEntity getEntity() { |
|
||||||
return (AbsNormalEntity) mWrapper.getEntity(); |
|
||||||
} |
|
||||||
|
|
||||||
public AbsNormalTTBuilderAdapter getAdapter() { |
|
||||||
return mAdapter; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 创建线程任务 |
|
||||||
*/ |
|
||||||
private IThreadTask createThreadTask(SubThreadConfig config) { |
|
||||||
ThreadTask task = new ThreadTask(config); |
|
||||||
task.setAdapter(mAdapter.getAdapter(config)); |
|
||||||
return task; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 启动断点任务时,创建单线程任务 |
|
||||||
* |
|
||||||
* @param record 线程记录 |
|
||||||
* @param startNum 启动的线程数 |
|
||||||
*/ |
|
||||||
private IThreadTask createSingThreadTask(ThreadRecord record, int startNum) { |
|
||||||
return createThreadTask( |
|
||||||
mAdapter.getSubThreadConfig(mStateHandler, record, mRecord.isBlock, startNum)); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 处理不支持断点的任务 |
|
||||||
*/ |
|
||||||
private List<IThreadTask> handleNoSupportBP() { |
|
||||||
List<IThreadTask> list = new ArrayList<>(); |
|
||||||
mStartThreadNum = 1; |
|
||||||
mRecord.isBlock = false; |
|
||||||
mRecord.update(); |
|
||||||
IThreadTask task = createSingThreadTask(mRecord.threadRecords.get(0), 1); |
|
||||||
list.add(task); |
|
||||||
return list; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 处理支持断点的任务 |
|
||||||
*/ |
|
||||||
private List<IThreadTask> handleBreakpoint() { |
|
||||||
long fileLength = getEntity().getFileSize(); |
|
||||||
long blockSize = fileLength / mTotalThreadNum; |
|
||||||
long currentProgress = 0; |
|
||||||
List<IThreadTask> threadTasks = new ArrayList<>(mTotalThreadNum); |
|
||||||
|
|
||||||
mRecord.fileLength = fileLength; |
|
||||||
if (mWrapper.isNewTask() && !mAdapter.handleNewTask(mRecord, mTotalThreadNum)) { |
|
||||||
ALog.e(TAG, "初始化线程任务失败"); |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
for (ThreadRecord tr : mRecord.threadRecords) { |
|
||||||
if (!tr.isComplete) { |
|
||||||
mStartThreadNum++; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
for (int i = 0; i < mTotalThreadNum; i++) { |
|
||||||
long startL = i * blockSize, endL = (i + 1) * blockSize; |
|
||||||
ThreadRecord tr = mRecord.threadRecords.get(i); |
|
||||||
|
|
||||||
if (tr.isComplete) {//该线程已经完成
|
|
||||||
currentProgress += endL - startL; |
|
||||||
ALog.d(TAG, String.format("任务【%s】线程__%s__已完成", mWrapper.getKey(), i)); |
|
||||||
Message msg = mStateHandler.obtainMessage(); |
|
||||||
msg.what = ITaskManager.STATE_COMPLETE; |
|
||||||
Bundle b = msg.getData(); |
|
||||||
if (b == null) { |
|
||||||
b = new Bundle(); |
|
||||||
} |
|
||||||
b.putString(ITaskManager.DATA_THREAD_NAME, |
|
||||||
CommonUtil.getThreadName(getEntity().getKey(), tr.threadId)); |
|
||||||
msg.setData(b); |
|
||||||
msg.sendToTarget(); |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
//如果有记录,则恢复任务
|
|
||||||
long r = tr.startLocation; |
|
||||||
//记录的位置需要在线程区间中
|
|
||||||
if (startL < r && r <= (i == (mTotalThreadNum - 1) ? fileLength : endL)) { |
|
||||||
currentProgress += r - startL; |
|
||||||
} |
|
||||||
ALog.d(TAG, String.format("任务【%s】线程__%s__恢复任务", getEntity().getFileName(), i)); |
|
||||||
|
|
||||||
IThreadTask task = createSingThreadTask(tr, mStartThreadNum); |
|
||||||
if (task == null) { |
|
||||||
ALog.e(TAG, "创建线程任务失败"); |
|
||||||
return null; |
|
||||||
} |
|
||||||
threadTasks.add(task); |
|
||||||
} |
|
||||||
if (currentProgress != getEntity().getCurrentProgress()) { |
|
||||||
ALog.d(TAG, String.format("进度修正,当前进度:%s", currentProgress)); |
|
||||||
getEntity().setCurrentProgress(currentProgress); |
|
||||||
} |
|
||||||
//mStateManager.updateProgress(currentProgress);
|
|
||||||
return threadTasks; |
|
||||||
} |
|
||||||
|
|
||||||
private List<IThreadTask> handleTask() { |
|
||||||
if (mWrapper.isSupportBP()) { |
|
||||||
return handleBreakpoint(); |
|
||||||
} else { |
|
||||||
return handleNoSupportBP(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override public List<IThreadTask> buildThreadTask(TaskRecord record, Handler stateHandler) { |
|
||||||
mRecord = record; |
|
||||||
mStateHandler = stateHandler; |
|
||||||
mTotalThreadNum = mRecord.threadNum; |
|
||||||
return handleTask(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public int getCreatedThreadNum() { |
|
||||||
return mStartThreadNum; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void accept(ILoaderVisitor visitor) { |
|
||||||
visitor.addComponent(this); |
|
||||||
} |
|
||||||
} |
|
@ -1,291 +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.loader; |
|
||||||
|
|
||||||
import android.os.Bundle; |
|
||||||
import android.os.Handler; |
|
||||||
import android.os.Looper; |
|
||||||
import android.os.Message; |
|
||||||
import android.text.TextUtils; |
|
||||||
import com.arialyy.aria.core.TaskRecord; |
|
||||||
import com.arialyy.aria.core.common.AbsEntity; |
|
||||||
import com.arialyy.aria.core.common.CompleteInfo; |
|
||||||
import com.arialyy.aria.core.inf.ITaskManager; |
|
||||||
import com.arialyy.aria.core.manager.ThreadTaskManager; |
|
||||||
import com.arialyy.aria.core.task.IThreadTask; |
|
||||||
import com.arialyy.aria.core.task.ThreadTask; |
|
||||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper; |
|
||||||
import com.arialyy.aria.exception.AriaException; |
|
||||||
import com.arialyy.aria.util.ALog; |
|
||||||
import com.arialyy.aria.util.CommonUtil; |
|
||||||
import java.io.File; |
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.List; |
|
||||||
|
|
||||||
/** |
|
||||||
* 子任务加载器 |
|
||||||
*/ |
|
||||||
public final class SubLoader implements ILoader, ILoaderVisitor { |
|
||||||
private String TAG = CommonUtil.getClassName(this); |
|
||||||
// 是否需要获取信息
|
|
||||||
private boolean needGetInfo = true; |
|
||||||
private Handler schedulers; |
|
||||||
private boolean isCancel = false, isStop = false; |
|
||||||
private AbsTaskWrapper wrapper; |
|
||||||
private IInfoTask infoTask; |
|
||||||
private IThreadTaskBuilder ttBuild; |
|
||||||
private IRecordHandler recordHandler; |
|
||||||
private List<IThreadTask> mTask = new ArrayList<>(); |
|
||||||
private String parentKey; |
|
||||||
private TaskRecord record; |
|
||||||
protected ITaskManager mStateManager; |
|
||||||
|
|
||||||
public SubLoader(AbsTaskWrapper wrapper, Handler schedulers) { |
|
||||||
this.wrapper = wrapper; |
|
||||||
this.schedulers = schedulers; |
|
||||||
} |
|
||||||
|
|
||||||
public AbsTaskWrapper getWrapper() { |
|
||||||
return wrapper; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 发送状态到调度器 |
|
||||||
* |
|
||||||
* @param state {@link ITaskManager} |
|
||||||
*/ |
|
||||||
private void sendNormalState(int state) { |
|
||||||
Message msg = schedulers.obtainMessage(); |
|
||||||
Bundle b = msg.getData(); |
|
||||||
if (b == null) { |
|
||||||
b = new Bundle(); |
|
||||||
} |
|
||||||
b.putString(ITaskManager.DATA_THREAD_NAME, getKey()); |
|
||||||
msg.what = state; |
|
||||||
msg.setData(b); |
|
||||||
msg.sendToTarget(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 发送失败的状态 |
|
||||||
*/ |
|
||||||
private void sendFailState(boolean needRetry) { |
|
||||||
Message msg = schedulers.obtainMessage(); |
|
||||||
Bundle b = msg.getData(); |
|
||||||
if (b == null) { |
|
||||||
b = new Bundle(); |
|
||||||
} |
|
||||||
b.putString(ITaskManager.DATA_THREAD_NAME, getKey()); |
|
||||||
b.putBoolean(ITaskManager.DATA_RETRY, needRetry); |
|
||||||
msg.what = ITaskManager.STATE_FAIL; |
|
||||||
msg.setData(b); |
|
||||||
msg.sendToTarget(); |
|
||||||
} |
|
||||||
|
|
||||||
private void handlerTask() { |
|
||||||
if (isBreak()) { |
|
||||||
return; |
|
||||||
} |
|
||||||
Looper looper = Looper.myLooper(); |
|
||||||
if (looper == null) { |
|
||||||
Looper.prepare(); |
|
||||||
looper = Looper.myLooper(); |
|
||||||
} |
|
||||||
|
|
||||||
record = recordHandler.getRecord(wrapper.getEntity().getFileSize()); |
|
||||||
if (record == null) { |
|
||||||
ALog.d(TAG, "子任务记录为空"); |
|
||||||
sendFailState(false); |
|
||||||
return; |
|
||||||
} |
|
||||||
if (record.threadRecords != null |
|
||||||
&& !TextUtils.isEmpty(record.filePath) |
|
||||||
&& new File(record.filePath).exists() |
|
||||||
&& !record.threadRecords.isEmpty() |
|
||||||
&& record.threadRecords.get(0).isComplete) { |
|
||||||
ALog.d(TAG, "子任务已完成,key:" + wrapper.getKey()); |
|
||||||
sendNormalState(ITaskManager.STATE_COMPLETE); |
|
||||||
return; |
|
||||||
} |
|
||||||
List<IThreadTask> task = |
|
||||||
ttBuild.buildThreadTask(record, new Handler(looper, mStateManager.getHandlerCallback())); |
|
||||||
mStateManager.setLooper(record, looper); |
|
||||||
if (task == null || task.isEmpty()) { |
|
||||||
ALog.e(TAG, "创建子任务的线程任务失败,key:" + wrapper.getKey()); |
|
||||||
sendFailState(false); |
|
||||||
return; |
|
||||||
} |
|
||||||
if (TextUtils.isEmpty(parentKey)) { |
|
||||||
ALog.e(TAG, "parentKey为空"); |
|
||||||
sendFailState(false); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
sendNormalState(ITaskManager.STATE_PRE); |
|
||||||
mTask.addAll(task); |
|
||||||
try { |
|
||||||
for (IThreadTask iThreadTask : mTask) { |
|
||||||
ThreadTaskManager.getInstance().startThread(parentKey, iThreadTask); |
|
||||||
} |
|
||||||
|
|
||||||
sendNormalState(ITaskManager.STATE_START); |
|
||||||
|
|
||||||
mStateManager.updateCurrentProgress(getWrapper().getEntity().getCurrentProgress()); |
|
||||||
} catch (Exception e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
Looper.loop(); |
|
||||||
} |
|
||||||
|
|
||||||
public TaskRecord getRecord() { |
|
||||||
return record; |
|
||||||
} |
|
||||||
|
|
||||||
public void setParentKey(String parentKey) { |
|
||||||
this.parentKey = parentKey; |
|
||||||
} |
|
||||||
|
|
||||||
public void setNeedGetInfo(boolean needGetInfo) { |
|
||||||
this.needGetInfo = needGetInfo; |
|
||||||
} |
|
||||||
|
|
||||||
public void retryTask() { |
|
||||||
try { |
|
||||||
if (!mTask.isEmpty()) { |
|
||||||
for (IThreadTask iThreadTask : mTask) { |
|
||||||
iThreadTask.call(); |
|
||||||
} |
|
||||||
return; |
|
||||||
} |
|
||||||
ALog.e(TAG, "子任务的线程任务为空"); |
|
||||||
} catch (Exception e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void stop() { |
|
||||||
if (isStop) { |
|
||||||
ALog.w(TAG, "子任务已停止"); |
|
||||||
return; |
|
||||||
} |
|
||||||
isStop = true; |
|
||||||
if (infoTask != null){ |
|
||||||
infoTask.stop(); |
|
||||||
} |
|
||||||
for (IThreadTask iThreadTask : mTask) { |
|
||||||
iThreadTask.stop(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override public boolean isRunning() { |
|
||||||
if (mTask.isEmpty()) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
for (IThreadTask iThreadTask : mTask) { |
|
||||||
if (!iThreadTask.isBreak()) return true; |
|
||||||
} |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void cancel() { |
|
||||||
if (isCancel) { |
|
||||||
ALog.w(TAG, "子任务已取消"); |
|
||||||
return; |
|
||||||
} |
|
||||||
isCancel = true; |
|
||||||
if (infoTask != null){ |
|
||||||
infoTask.cancel(); |
|
||||||
} |
|
||||||
for (IThreadTask iThreadTask : mTask) { |
|
||||||
iThreadTask.cancel(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override public boolean isBreak() { |
|
||||||
if (isCancel || isStop) { |
|
||||||
ALog.d(TAG, "isCancel = " + isCancel + ", isStop = " + isStop); |
|
||||||
ALog.d(TAG, String.format("任务【%s】已停止或取消了", wrapper.getKey())); |
|
||||||
return true; |
|
||||||
} |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 线程名,一个子任务的loader只有一个线程,使用线程名标示key |
|
||||||
* |
|
||||||
* @return {@link ThreadTask#getThreadName()} |
|
||||||
*/ |
|
||||||
@Override public String getKey() { |
|
||||||
return CommonUtil.getThreadName(wrapper.getKey(), 0); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public long getCurrentProgress() { |
|
||||||
return isRunning() ? mStateManager.getCurrentProgress() |
|
||||||
: getWrapper().getEntity().getCurrentProgress(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void addComponent(IRecordHandler recordHandler) { |
|
||||||
this.recordHandler = recordHandler; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void addComponent(IInfoTask infoTask) { |
|
||||||
this.infoTask = infoTask; |
|
||||||
infoTask.setCallback(new IInfoTask.Callback() { |
|
||||||
@Override public void onSucceed(String key, CompleteInfo info) { |
|
||||||
handlerTask(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void onFail(AbsEntity entity, AriaException e, boolean needRetry) { |
|
||||||
sendFailState(needRetry); |
|
||||||
} |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void addComponent(ITaskManager threadState) { |
|
||||||
mStateManager = threadState; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void addComponent(IThreadTaskBuilder builder) { |
|
||||||
ttBuild = builder; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void run() { |
|
||||||
checkComponent(); |
|
||||||
if (isBreak()) { |
|
||||||
return; |
|
||||||
} |
|
||||||
if (needGetInfo) { |
|
||||||
infoTask.run(); |
|
||||||
return; |
|
||||||
} |
|
||||||
handlerTask(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 检查组件: {@link #recordHandler}、{@link #infoTask}、{@link #ttBuild} |
|
||||||
*/ |
|
||||||
private void checkComponent() { |
|
||||||
if (recordHandler == null) { |
|
||||||
throw new NullPointerException("任务记录组件为空"); |
|
||||||
} |
|
||||||
if (infoTask == null) { |
|
||||||
throw new NullPointerException(("文件信息组件为空")); |
|
||||||
} |
|
||||||
if (ttBuild == null) { |
|
||||||
throw new NullPointerException("线程任务组件为空"); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,38 +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.loader; |
|
||||||
|
|
||||||
import android.os.Handler; |
|
||||||
import com.arialyy.aria.core.TaskRecord; |
|
||||||
import com.arialyy.aria.core.task.IThreadTask; |
|
||||||
import java.util.List; |
|
||||||
|
|
||||||
public class SubTTBuilder implements IThreadTaskBuilder{ |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override public List<IThreadTask> buildThreadTask(TaskRecord record, Handler stateHandler) { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public int getCreatedThreadNum() { |
|
||||||
return 1; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void accept(ILoaderVisitor visitor) { |
|
||||||
visitor.addComponent(this); |
|
||||||
} |
|
||||||
} |
|
@ -1,259 +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.loader; |
|
||||||
|
|
||||||
import android.os.Bundle; |
|
||||||
import android.os.Handler; |
|
||||||
import android.os.Looper; |
|
||||||
import android.os.Message; |
|
||||||
import com.arialyy.aria.core.inf.ITaskManager; |
|
||||||
import com.arialyy.aria.core.listener.IEventListener; |
|
||||||
import com.arialyy.aria.exception.AriaException; |
|
||||||
import com.arialyy.aria.util.ALog; |
|
||||||
import com.arialyy.aria.util.FileUtil; |
|
||||||
import java.io.File; |
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.List; |
|
||||||
import java.util.concurrent.atomic.AtomicInteger; |
|
||||||
import timber.log.Timber; |
|
||||||
|
|
||||||
/** |
|
||||||
* 线程任务管理器,用于处理多线程下载时任务的状态回调 |
|
||||||
*/ |
|
||||||
public class TaskThreadStateManager implements ITaskManager { |
|
||||||
/** |
|
||||||
* 分块文件路径: 文件路径.blockId.part |
|
||||||
*/ |
|
||||||
public static String SUB_PATH = "%s.%s.part"; |
|
||||||
/** |
|
||||||
* 任务状态回调 |
|
||||||
*/ |
|
||||||
private final IEventListener mListener; |
|
||||||
private final int mThreadNum; // 启动的线程总数
|
|
||||||
private final AtomicInteger mCancelNum = new AtomicInteger(0); // 已经取消的线程的数
|
|
||||||
private final AtomicInteger mStopNum = new AtomicInteger(0); // 已经停止的线程数
|
|
||||||
private final AtomicInteger mFailNum = new AtomicInteger(0); // 失败的线程数
|
|
||||||
private final AtomicInteger mCompleteNum = new AtomicInteger(0); // 完成的线程数
|
|
||||||
private long mProgress; //当前总进度
|
|
||||||
private Looper mLooper; |
|
||||||
|
|
||||||
/** |
|
||||||
* @param listener 任务事件 |
|
||||||
*/ |
|
||||||
public TaskThreadStateManager(IEventListener listener, int threadNum) { |
|
||||||
mListener = listener; |
|
||||||
mThreadNum = threadNum; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void setLooper(Looper looper) { |
|
||||||
mLooper = looper; |
|
||||||
} |
|
||||||
|
|
||||||
private void checkLooper() { |
|
||||||
if (mLooper == null) { |
|
||||||
throw new NullPointerException("Looper为空"); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private final Handler.Callback callback = new Handler.Callback() { |
|
||||||
@Override public boolean handleMessage(Message msg) { |
|
||||||
checkLooper(); |
|
||||||
switch (msg.what) { |
|
||||||
case STATE_STOP: |
|
||||||
mStopNum.getAndIncrement(); |
|
||||||
if (isStop()) { |
|
||||||
quitLooper(); |
|
||||||
} |
|
||||||
break; |
|
||||||
case STATE_CANCEL: |
|
||||||
mCancelNum.getAndIncrement(); |
|
||||||
if (isCancel()) { |
|
||||||
quitLooper(); |
|
||||||
} |
|
||||||
break; |
|
||||||
case STATE_FAIL: |
|
||||||
mFailNum.getAndIncrement(); |
|
||||||
if (hasFailedBlock()) { |
|
||||||
Bundle b = msg.getData(); |
|
||||||
mListener.onFail(b.getBoolean(DATA_RETRY, false), |
|
||||||
(AriaException) b.getSerializable(DATA_ERROR_INFO)); |
|
||||||
quitLooper(); |
|
||||||
} |
|
||||||
break; |
|
||||||
case STATE_COMPLETE: |
|
||||||
mCompleteNum.getAndIncrement(); |
|
||||||
if (isCompleted()) { |
|
||||||
Timber.d("isComplete, completeNum = %s", mCompleteNum); |
|
||||||
if (mTaskRecord.isBlock || mTaskRecord.threadNum == 1) { |
|
||||||
if (mergeFile()) { |
|
||||||
mListener.onComplete(); |
|
||||||
} else { |
|
||||||
mListener.onFail(false, null); |
|
||||||
} |
|
||||||
quitLooper(); |
|
||||||
break; |
|
||||||
} |
|
||||||
mListener.onComplete(); |
|
||||||
quitLooper(); |
|
||||||
} |
|
||||||
break; |
|
||||||
case STATE_RUNNING: |
|
||||||
Bundle b = msg.getData(); |
|
||||||
if (b != null) { |
|
||||||
long len = b.getLong(ITaskManager.DATA_ADD_LEN, 0); |
|
||||||
mProgress += len; |
|
||||||
} |
|
||||||
|
|
||||||
break; |
|
||||||
case STATE_UPDATE_PROGRESS: |
|
||||||
if (msg.obj == null) { |
|
||||||
mProgress = updateBlockProgress(); |
|
||||||
} else if (msg.obj instanceof Long) { |
|
||||||
mProgress = (long) msg.obj; |
|
||||||
} |
|
||||||
break; |
|
||||||
} |
|
||||||
return false; |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
@Override public void updateCurrentProgress(long currentProgress) { |
|
||||||
mProgress = currentProgress; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 退出looper循环 |
|
||||||
*/ |
|
||||||
private void quitLooper() { |
|
||||||
mLooper.quit(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 获取当前任务下载进度 |
|
||||||
* |
|
||||||
* @return 当前任务下载进度 |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public long getCurrentProgress() { |
|
||||||
return mProgress; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public Handler.Callback getHandlerCallback() { |
|
||||||
return callback; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 所有子线程是否都已经停止 |
|
||||||
*/ |
|
||||||
public boolean isStop() { |
|
||||||
//ALog.d(TAG,
|
|
||||||
// String.format("isStop; stopNum: %s, cancelNum: %s, failNum: %s, completeNum: %s", mStopNum,
|
|
||||||
// mCancelNum, mFailNum, mCompleteNum));
|
|
||||||
return mStopNum.get() == mThreadNum || mStopNum.get() + mCompleteNum.get() == mThreadNum; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 所有子线程是否都已经失败 |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public boolean hasFailedBlock() { |
|
||||||
//ALog.d(TAG,
|
|
||||||
// String.format("isFail; stopNum: %s, cancelNum: %s, failNum: %s, completeNum: %s", mStopNum,
|
|
||||||
// mCancelNum, mFailNum, mCompleteNum));
|
|
||||||
return mCompleteNum.get() != mThreadNum |
|
||||||
&& (mFailNum.get() == mThreadNum || mFailNum.get() + mCompleteNum.get() == mThreadNum); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 所有子线程是否都已经完成 |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public boolean isCompleted() { |
|
||||||
//ALog.d(TAG,
|
|
||||||
// String.format("isComplete; stopNum: %s, cancelNum: %s, failNum: %s, completeNum: %s",
|
|
||||||
// mStopNum,
|
|
||||||
// mCancelNum, mFailNum, mCompleteNum));
|
|
||||||
return mCompleteNum.get() == mThreadNum; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 所有子线程是否都已经取消 |
|
||||||
*/ |
|
||||||
public boolean isCancel() { |
|
||||||
//ALog.d(TAG, String.format("isCancel; stopNum: %s, cancelNum: %s, failNum: %s, completeNum: %s",
|
|
||||||
// mStopNum,
|
|
||||||
// mCancelNum, mFailNum, mCompleteNum));
|
|
||||||
return mCancelNum.get() == mThreadNum; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 更新分块任务s的真实进度 |
|
||||||
*/ |
|
||||||
private long updateBlockProgress() { |
|
||||||
long size = 0; |
|
||||||
for (int i = 0, len = mTaskRecord.threadRecords.size(); i < len; i++) { |
|
||||||
File temp = new File(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, i)); |
|
||||||
if (temp.exists()) { |
|
||||||
size += temp.length(); |
|
||||||
} |
|
||||||
} |
|
||||||
return size; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 合并文件 |
|
||||||
* |
|
||||||
* @return {@code true} 合并成功,{@code false}合并失败 |
|
||||||
*/ |
|
||||||
private boolean mergeFile() { |
|
||||||
if (mTaskRecord.threadNum == 1) { |
|
||||||
File targetFile = new File(mTaskRecord.filePath); |
|
||||||
if (targetFile.exists()) { |
|
||||||
//没有获得文件长度:不支持断点续传
|
|
||||||
if (mTaskRecord.fileLength == 0 && targetFile.length() != 0) { |
|
||||||
return true; |
|
||||||
} |
|
||||||
if (targetFile.length() != 0 && targetFile.length() == mTaskRecord.fileLength) { |
|
||||||
return true; |
|
||||||
} |
|
||||||
} |
|
||||||
FileUtil.deleteFile(targetFile); |
|
||||||
File partFile = new File(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, 0)); |
|
||||||
return partFile.renameTo(targetFile); |
|
||||||
} |
|
||||||
|
|
||||||
List<String> partPath = new ArrayList<>(); |
|
||||||
for (int i = 0, len = mTaskRecord.threadNum; i < len; i++) { |
|
||||||
partPath.add(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, i)); |
|
||||||
} |
|
||||||
boolean isSuccess = FileUtil.mergeFile(mTaskRecord.filePath, partPath); |
|
||||||
if (isSuccess) { |
|
||||||
for (String pp : partPath) { |
|
||||||
FileUtil.deleteFile(pp); |
|
||||||
} |
|
||||||
File targetFile = new File(mTaskRecord.filePath); |
|
||||||
if (targetFile.exists() && targetFile.length() > mTaskRecord.fileLength) { |
|
||||||
ALog.e(TAG, String.format("任务【%s】分块文件合并失败,下载长度超出文件真实长度,downloadLen: %s,fileSize: %s", |
|
||||||
targetFile.getName(), targetFile.length(), mTaskRecord.fileLength)); |
|
||||||
return false; |
|
||||||
} |
|
||||||
return true; |
|
||||||
} else { |
|
||||||
ALog.e(TAG, "合并失败"); |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,34 +1,34 @@ |
|||||||
/* |
///*
|
||||||
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
// * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
||||||
* |
// *
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
// * Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License. |
// * you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at |
// * You may obtain a copy of the License at
|
||||||
* |
// *
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
// * http://www.apache.org/licenses/LICENSE-2.0
|
||||||
* |
// *
|
||||||
* Unless required by applicable law or agreed to in writing, software |
// * Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
// * distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and |
// * See the License for the specific language governing permissions and
|
||||||
* limitations under the License. |
// * limitations under the License.
|
||||||
*/ |
// */
|
||||||
package com.arialyy.aria.core.processor; |
//package com.arialyy.aria.core.processor;
|
||||||
|
//
|
||||||
import com.arialyy.aria.core.inf.IEventHandler; |
//import com.arialyy.aria.core.inf.IEventHandler;
|
||||||
import com.arialyy.aria.core.upload.UploadEntity; |
//import com.arialyy.aria.core.upload.UploadEntity;
|
||||||
import java.util.List; |
//import java.util.List;
|
||||||
|
//
|
||||||
/** |
///**
|
||||||
* FTP文件上传拦截器,如果远端已有同名文件,可使用该拦截器控制覆盖文件或修改该文件上传到服务器端端文件名 |
// * FTP文件上传拦截器,如果远端已有同名文件,可使用该拦截器控制覆盖文件或修改该文件上传到服务器端端文件名
|
||||||
*/ |
// */
|
||||||
public interface IFtpUploadInterceptor extends IEventHandler { |
//public interface IFtpUploadInterceptor extends IEventHandler {
|
||||||
|
//
|
||||||
/** |
// /**
|
||||||
* 处理拦截事件 |
// * 处理拦截事件
|
||||||
* |
// *
|
||||||
* @param entity 上传信息实体 |
// * @param entity 上传信息实体
|
||||||
* @param fileList ftp服务器端remotePath下的文件列表 |
// * @param fileList ftp服务器端remotePath下的文件列表
|
||||||
*/ |
// */
|
||||||
FtpInterceptHandler onIntercept(UploadEntity entity, List<String> fileList); |
// FtpInterceptHandler onIntercept(UploadEntity entity, List<String> fileList);
|
||||||
} |
//}
|
||||||
|
@ -1,73 +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.task; |
|
||||||
|
|
||||||
import android.os.Handler; |
|
||||||
import android.os.Looper; |
|
||||||
import com.arialyy.aria.core.listener.ISchedulers; |
|
||||||
import com.arialyy.aria.core.upload.UTaskWrapper; |
|
||||||
import com.arialyy.aria.core.upload.UploadEntity; |
|
||||||
import com.arialyy.aria.util.ComponentUtil; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by lyy on 2017/2/23. 上传任务 |
|
||||||
*/ |
|
||||||
public class UploadTask extends AbsTask<UTaskWrapper> { |
|
||||||
|
|
||||||
private UploadTask(UTaskWrapper taskWrapper, Handler outHandler) { |
|
||||||
mTaskWrapper = taskWrapper; |
|
||||||
mOutHandler = outHandler; |
|
||||||
mListener = |
|
||||||
ComponentUtil.getInstance().buildListener(taskWrapper.getRequestType(), this, mOutHandler); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public int getTaskType() { |
|
||||||
return UPLOAD; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public String getKey() { |
|
||||||
return mTaskWrapper.getEntity().getKey(); |
|
||||||
} |
|
||||||
|
|
||||||
public UploadEntity getEntity() { |
|
||||||
return mTaskWrapper.getEntity(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public String getTaskName() { |
|
||||||
return mTaskWrapper.getEntity().getFileName(); |
|
||||||
} |
|
||||||
|
|
||||||
public static class Builder { |
|
||||||
private Handler mOutHandler; |
|
||||||
private UTaskWrapper mTaskEntity; |
|
||||||
|
|
||||||
public void setOutHandler(ISchedulers outHandler) { |
|
||||||
mOutHandler = new Handler(Looper.getMainLooper(), outHandler); |
|
||||||
} |
|
||||||
|
|
||||||
public void setUploadTaskEntity(UTaskWrapper taskEntity) { |
|
||||||
mTaskEntity = taskEntity; |
|
||||||
} |
|
||||||
|
|
||||||
public Builder() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
public UploadTask build() { |
|
||||||
return new UploadTask(mTaskEntity, mOutHandler); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,74 +0,0 @@ |
|||||||
/* |
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one |
|
||||||
* or more contributor license agreements. See the NOTICE file |
|
||||||
* distributed with this work for additional information |
|
||||||
* regarding copyright ownership. The ASF licenses this file |
|
||||||
* to you under the Apache License, Version 2.0 (the |
|
||||||
* "License"); you may not use this file except in compliance |
|
||||||
* with the License. You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
package com.arialyy.aria.util; |
|
||||||
|
|
||||||
import java.util.concurrent.ExecutorService; |
|
||||||
import java.util.concurrent.Executors; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by Aria.Lao on 2017/10/25. |
|
||||||
* 程序异常日志捕获器 |
|
||||||
*/ |
|
||||||
public class AriaCrashHandler implements Thread.UncaughtExceptionHandler { |
|
||||||
private Thread.UncaughtExceptionHandler mDefaultHandler; |
|
||||||
private ExecutorService mExecutorService; |
|
||||||
|
|
||||||
public AriaCrashHandler() { |
|
||||||
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); |
|
||||||
mExecutorService = Executors.newSingleThreadExecutor(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void uncaughtException(Thread thread, Throwable ex) { |
|
||||||
ex.printStackTrace(); |
|
||||||
//ALog.d(thread.getName(), ex.getLocalizedMessage());
|
|
||||||
handleException(thread.getName(), ex); |
|
||||||
try { |
|
||||||
Thread.sleep(1000); |
|
||||||
} catch (InterruptedException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} finally { |
|
||||||
mDefaultHandler.uncaughtException(thread, ex); |
|
||||||
exit(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 处理异常 |
|
||||||
*/ |
|
||||||
private void handleException(final String name, final Throwable ex) { |
|
||||||
if (ex == null) { |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
mExecutorService.execute(new Runnable() { |
|
||||||
@Override |
|
||||||
public void run() { |
|
||||||
ErrorHelp.saveError("", ALog.getExceptionString(ex)); |
|
||||||
} |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 退出当前应用 |
|
||||||
*/ |
|
||||||
private void exit() { |
|
||||||
android.os.Process.killProcess(android.os.Process.myPid()); |
|
||||||
System.exit(1); |
|
||||||
} |
|
||||||
} |
|
@ -1,209 +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.util; |
|
||||||
|
|
||||||
import android.text.TextUtils; |
|
||||||
import com.arialyy.aria.core.download.DownloadEntity; |
|
||||||
import com.arialyy.aria.core.download.DownloadGroupEntity; |
|
||||||
import com.arialyy.aria.core.upload.UploadEntity; |
|
||||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper; |
|
||||||
import com.arialyy.aria.orm.DbEntity; |
|
||||||
import java.lang.reflect.Modifier; |
|
||||||
import java.net.HttpURLConnection; |
|
||||||
import java.util.List; |
|
||||||
import java.util.regex.Matcher; |
|
||||||
import java.util.regex.Pattern; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by Lyy on 2016/9/23. |
|
||||||
* 检查帮助类 |
|
||||||
*/ |
|
||||||
public class CheckUtil { |
|
||||||
private static final String TAG = "CheckUtil"; |
|
||||||
|
|
||||||
/** |
|
||||||
* 检查ip是否正确 |
|
||||||
* |
|
||||||
* @param ip ipv4 地址 |
|
||||||
* @return {@code true} ip 正确,{@code false} ip 错误 |
|
||||||
*/ |
|
||||||
public static boolean checkIp(String ip) { |
|
||||||
if (TextUtils.isEmpty(ip) || ip.length() < 7 || ip.length() > 15) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
Pattern p = Pattern.compile(Regular.REG_IP_V4); |
|
||||||
Matcher m = p.matcher(ip); |
|
||||||
return m.find() && m.groupCount() > 0; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 判断http请求是否有效 |
|
||||||
* {@link HttpURLConnection#HTTP_BAD_GATEWAY} or {@link HttpURLConnection#HTTP_BAD_METHOD} |
|
||||||
* or {@link HttpURLConnection#HTTP_BAD_REQUEST} 无法重试 |
|
||||||
* |
|
||||||
* @param errorCode http 返回码 |
|
||||||
* @return {@code true} 无效请求;{@code false} 有效请求 |
|
||||||
*/ |
|
||||||
public static boolean httpIsBadRequest(int errorCode) { |
|
||||||
return errorCode == HttpURLConnection.HTTP_BAD_GATEWAY |
|
||||||
|| errorCode == HttpURLConnection.HTTP_BAD_METHOD |
|
||||||
|| errorCode == HttpURLConnection.HTTP_BAD_REQUEST; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 判断ftp请求是否有效 |
|
||||||
* |
|
||||||
* @return {@code true} 无效请求;{@code false} 有效请求 |
|
||||||
*/ |
|
||||||
public static boolean ftpIsBadRequest(int errorCode) { |
|
||||||
return errorCode >= 400 && errorCode < 600; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 检查和处理下载任务的路径冲突 |
|
||||||
* |
|
||||||
* @param isForceDownload true,如果路径冲突,将删除其它任务的记录的 |
|
||||||
* @param filePath 文件保存路径 |
|
||||||
* @param type {@link AbsTaskWrapper#getRequestType()} |
|
||||||
* @return false 任务不再执行,true 任务继续执行 |
|
||||||
*/ |
|
||||||
public static boolean checkDPathConflicts(boolean isForceDownload, String filePath, int type) { |
|
||||||
if (DbEntity.checkDataExist(DownloadEntity.class, "downloadPath=?", filePath)) { |
|
||||||
if (!isForceDownload) { |
|
||||||
ALog.e(TAG, String.format("下载失败,保存路径【%s】已经被其它任务占用,请设置其它保存路径", filePath)); |
|
||||||
return false; |
|
||||||
} else { |
|
||||||
ALog.w(TAG, String.format("保存路径【%s】已经被其它任务占用,当前任务将覆盖该路径的文件", filePath)); |
|
||||||
RecordUtil.delTaskRecord(filePath, type, false, true); |
|
||||||
return true; |
|
||||||
} |
|
||||||
} |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 检查和处理上传任务的路径冲突 |
|
||||||
* |
|
||||||
* @param isForceUpload true,如果路径冲突,将删除其它任务的记录的 |
|
||||||
* @param filePath 文件保存路径 |
|
||||||
* @param type {@link AbsTaskWrapper#getRequestType()} |
|
||||||
* @return false 任务不再执行,true 任务继续执行 |
|
||||||
*/ |
|
||||||
public static boolean checkUPathConflicts(boolean isForceUpload, String filePath, int type) { |
|
||||||
if (DbEntity.checkDataExist(UploadEntity.class, "filePath=?", filePath)) { |
|
||||||
if (!isForceUpload) { |
|
||||||
ALog.e(TAG, String.format("上传失败,文件路径【%s】已经被其它任务占用,请设置其它文件路径", filePath)); |
|
||||||
return false; |
|
||||||
} else { |
|
||||||
ALog.w(TAG, String.format("文件路径【%s】已经被其它任务占用,当前任务将覆盖该路径的文件", filePath)); |
|
||||||
RecordUtil.delTaskRecord(filePath, type, false, true); |
|
||||||
return true; |
|
||||||
} |
|
||||||
} |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 检查和处理组合任务的路径冲突 |
|
||||||
* |
|
||||||
* @param isForceDownload true,如果路径冲突,将删除其它任务的记录的 |
|
||||||
* @param dirPath 文件保存路径 |
|
||||||
* @return false 任务不再执行,true 任务继续执行 |
|
||||||
*/ |
|
||||||
public static boolean checkDGPathConflicts(boolean isForceDownload, String dirPath) { |
|
||||||
if (DbEntity.checkDataExist(DownloadGroupEntity.class, "dirPath=?", dirPath)) { |
|
||||||
if (!isForceDownload) { |
|
||||||
ALog.e(TAG, String.format("下载失败,文件夹路径【%s】已经被其它任务占用,请设置其它文件路径", dirPath)); |
|
||||||
return false; |
|
||||||
} else { |
|
||||||
ALog.w(TAG, String.format("文件夹路径【%s】已经被其它任务占用,当前任务将覆盖该路径", dirPath)); |
|
||||||
DeleteDGRecord.getInstance().deleteRecord(dirPath, false, true); |
|
||||||
return true; |
|
||||||
} |
|
||||||
} |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 检查成员类是否是静态和public |
|
||||||
*/ |
|
||||||
public static void checkMemberClass(Class clazz) { |
|
||||||
int modifiers = clazz.getModifiers(); |
|
||||||
//ALog.d(TAG, "isMemberClass = "
|
|
||||||
// + clazz.isMemberClass()
|
|
||||||
// + "; isStatic = "
|
|
||||||
// + Modifier.isStatic(modifiers)
|
|
||||||
// + "; isPrivate = "
|
|
||||||
// + Modifier.isPrivate(modifiers));
|
|
||||||
if (!clazz.isMemberClass() || !Modifier.isStatic(modifiers)) { |
|
||||||
ALog.e(TAG, String.format("为了防止内存泄漏,请使用静态的成员类(public static class %s)或文件类(%s.java)", |
|
||||||
clazz.getSimpleName(), clazz.getSimpleName())); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 检查分页数据,需要查询的页数,从1开始,如果page小于1 或 num 小于1,则抛出{@link NullPointerException} |
|
||||||
* |
|
||||||
* @param page 从1 开始 |
|
||||||
* @param num 每页数量 |
|
||||||
*/ |
|
||||||
public static void checkPageParams(int page, int num) { |
|
||||||
if (page < 1 || num < 1) throw new NullPointerException("page和num不能小于1"); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 检测url是否合法 |
|
||||||
* |
|
||||||
* @return {@code true} 合法,{@code false} 非法 |
|
||||||
*/ |
|
||||||
public static boolean checkUrl(String url) { |
|
||||||
if (TextUtils.isEmpty(url)) { |
|
||||||
ALog.e(TAG, "url不能为null"); |
|
||||||
return false; |
|
||||||
} else if (!url.startsWith("http") && !url.startsWith("ftp") && !url.startsWith("sftp")) { |
|
||||||
ALog.e(TAG, "url【" + url + "】错误"); |
|
||||||
return false; |
|
||||||
} |
|
||||||
int index = url.indexOf("://"); |
|
||||||
if (index == -1) { |
|
||||||
ALog.e(TAG, "url【" + url + "】不合法"); |
|
||||||
} |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 检测下载链接组是否为null |
|
||||||
* |
|
||||||
* @return true 组合任务url为空 |
|
||||||
*/ |
|
||||||
public static boolean checkDownloadUrlsIsEmpty(List<String> urls) { |
|
||||||
if (urls == null || urls.isEmpty()) { |
|
||||||
ALog.e(TAG, "链接组不能为null"); |
|
||||||
return true; |
|
||||||
} |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 检测上传地址是否为null |
|
||||||
*/ |
|
||||||
public static void checkUploadPathIsEmpty(String uploadPath) { |
|
||||||
if (TextUtils.isEmpty(uploadPath)) { |
|
||||||
throw new IllegalArgumentException("上传地址不能为null"); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,262 +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.util; |
|
||||||
|
|
||||||
import android.os.Handler; |
|
||||||
import com.arialyy.aria.core.TaskOptionParams; |
|
||||||
import com.arialyy.aria.core.inf.IEventHandler; |
|
||||||
import com.arialyy.aria.core.inf.ITaskOption; |
|
||||||
import com.arialyy.aria.core.inf.ITaskAdapter; |
|
||||||
import com.arialyy.aria.core.listener.IEventListener; |
|
||||||
import com.arialyy.aria.core.task.AbsTask; |
|
||||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper; |
|
||||||
import com.arialyy.aria.core.wrapper.ITaskWrapper; |
|
||||||
import java.lang.ref.SoftReference; |
|
||||||
import java.lang.reflect.Constructor; |
|
||||||
import java.lang.reflect.Field; |
|
||||||
import java.lang.reflect.InvocationTargetException; |
|
||||||
import java.lang.reflect.Method; |
|
||||||
import java.util.List; |
|
||||||
|
|
||||||
/** |
|
||||||
* 组件工具,用于跨组件创建对应的工具类 |
|
||||||
* |
|
||||||
* @author lyy |
|
||||||
* Date: 2019-09-23 |
|
||||||
*/ |
|
||||||
public class ComponentUtil { |
|
||||||
public static final int COMPONENT_TYPE_HTTP = 1; |
|
||||||
public static final int COMPONENT_TYPE_FTP = 2; |
|
||||||
public static final int COMPONENT_TYPE_M3U8 = 3; |
|
||||||
public static final int COMPONENT_TYPE_SFTP = 4; |
|
||||||
|
|
||||||
private String TAG = CommonUtil.getClassName(getClass()); |
|
||||||
private static volatile ComponentUtil INSTANCE = null; |
|
||||||
|
|
||||||
private ComponentUtil() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
public static ComponentUtil getInstance() { |
|
||||||
if (INSTANCE == null) { |
|
||||||
synchronized (ComponentUtil.class) { |
|
||||||
if (INSTANCE == null) { |
|
||||||
INSTANCE = new ComponentUtil(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return INSTANCE; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 检查组件是否存在,不存在抛出异常退出 |
|
||||||
* |
|
||||||
* @param componentType 组件类型{@link #COMPONENT_TYPE_FTP}, {@link #COMPONENT_TYPE_M3U8}, {@link |
|
||||||
* #COMPONENT_TYPE_HTTP} |
|
||||||
* @return true 组件存在 |
|
||||||
*/ |
|
||||||
public boolean checkComponentExist(int componentType) { |
|
||||||
String errorStr = "", className = null; |
|
||||||
switch (componentType) { |
|
||||||
case COMPONENT_TYPE_M3U8: |
|
||||||
className = "com.arialyy.aria.m3u8.M3U8TaskOption"; |
|
||||||
errorStr = "m3u8插件不存在,请添加m3u8插件"; |
|
||||||
break; |
|
||||||
case COMPONENT_TYPE_FTP: |
|
||||||
className = "com.arialyy.aria.ftp.FtpTaskOption"; |
|
||||||
errorStr = "ftp插件不存在,请添加ftp插件"; |
|
||||||
break; |
|
||||||
case COMPONENT_TYPE_HTTP: |
|
||||||
className = "com.arialyy.aria.http.HttpTaskOption"; |
|
||||||
errorStr = "http插件不存在,请添加http插件"; |
|
||||||
break; |
|
||||||
case COMPONENT_TYPE_SFTP: |
|
||||||
className = "com.arialyy.aria.sftp.SFtpTaskOption"; |
|
||||||
errorStr = "sftp插件不存在,请添加sftp插件"; |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
try { |
|
||||||
getClass().getClassLoader().loadClass(className); |
|
||||||
} catch (ClassNotFoundException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
throw new IllegalArgumentException(errorStr); |
|
||||||
} |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 创建任务工具 |
|
||||||
* |
|
||||||
* @return 返回任务工具 |
|
||||||
*/ |
|
||||||
public synchronized <T extends ITaskAdapter> T buildUtil(AbsTaskWrapper wrapper, |
|
||||||
IEventListener listener) { |
|
||||||
int requestType = wrapper.getRequestType(); |
|
||||||
String className = null; |
|
||||||
switch (requestType) { |
|
||||||
case ITaskWrapper.M3U8_LIVE: |
|
||||||
className = "com.arialyy.aria.m3u8.live.M3U8LiveUtil"; |
|
||||||
break; |
|
||||||
case ITaskWrapper.M3U8_VOD: |
|
||||||
className = "com.arialyy.aria.m3u8.vod.M3U8VodUtil"; |
|
||||||
break; |
|
||||||
case ITaskWrapper.D_FTP: |
|
||||||
className = "com.arialyy.aria.ftp.download.FtpDLoaderUtil"; |
|
||||||
break; |
|
||||||
case ITaskWrapper.D_HTTP: |
|
||||||
className = "com.arialyy.aria.http.download.HttpDLoaderUtil"; |
|
||||||
break; |
|
||||||
case ITaskWrapper.U_FTP: |
|
||||||
className = "com.arialyy.aria.ftp.upload.FtpULoaderUtil"; |
|
||||||
break; |
|
||||||
case ITaskWrapper.U_HTTP: |
|
||||||
className = "com.arialyy.aria.http.upload.HttpULoaderUtil"; |
|
||||||
break; |
|
||||||
case ITaskWrapper.D_FTP_DIR: |
|
||||||
className = "com.arialyy.aria.ftp.download.FtpDGLoaderUtil"; |
|
||||||
break; |
|
||||||
case ITaskWrapper.DG_HTTP: |
|
||||||
className = "com.arialyy.aria.http.download.HttpDGLoaderUtil"; |
|
||||||
break; |
|
||||||
case ITaskWrapper.D_SFTP: |
|
||||||
className = "com.arialyy.aria.sftp.download.SFtpDLoaderUtil"; |
|
||||||
break; |
|
||||||
case ITaskWrapper.U_SFTP: |
|
||||||
className = "com.arialyy.aria.sftp.upload.SFtpULoaderUtil"; |
|
||||||
break; |
|
||||||
} |
|
||||||
if (className == null) { |
|
||||||
ALog.e(TAG, "不识别的类名:" + className); |
|
||||||
return null; |
|
||||||
} |
|
||||||
T util = null; |
|
||||||
try { |
|
||||||
Class<T> clazz = (Class<T>) getClass().getClassLoader().loadClass(className); |
|
||||||
Constructor<T> con = clazz.getConstructor(); |
|
||||||
util = con.newInstance(); |
|
||||||
Method method = |
|
||||||
CommonUtil.getMethod(clazz, "setParams", AbsTaskWrapper.class, IEventListener.class); |
|
||||||
method.invoke(util, wrapper, listener); |
|
||||||
} catch (ClassNotFoundException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} catch (NoSuchMethodException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} catch (IllegalAccessException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} catch (InstantiationException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} catch (InvocationTargetException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
return util; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 创建任务事件监听 |
|
||||||
* |
|
||||||
* @param wrapperType 任务类型{@link ITaskWrapper} |
|
||||||
* @return 返回事件监听,如果创建失败返回null |
|
||||||
*/ |
|
||||||
public synchronized <T extends IEventListener> T buildListener(int wrapperType, AbsTask task, |
|
||||||
Handler outHandler) { |
|
||||||
String className = null, errorStr = "请添加FTP插件"; |
|
||||||
switch (wrapperType) { |
|
||||||
case ITaskWrapper.M3U8_LIVE: |
|
||||||
case ITaskWrapper.M3U8_VOD: |
|
||||||
className = "com.arialyy.aria.m3u8.M3U8Listener"; |
|
||||||
errorStr = "请添加m3u8插件"; |
|
||||||
break; |
|
||||||
case ITaskWrapper.D_FTP: |
|
||||||
case ITaskWrapper.D_HTTP: |
|
||||||
case ITaskWrapper.D_SFTP: |
|
||||||
className = "com.arialyy.aria.core.listener.BaseDListener"; |
|
||||||
break; |
|
||||||
case ITaskWrapper.U_FTP: |
|
||||||
case ITaskWrapper.U_HTTP: |
|
||||||
case ITaskWrapper.U_SFTP: |
|
||||||
className = "com.arialyy.aria.core.listener.BaseUListener"; |
|
||||||
break; |
|
||||||
case ITaskWrapper.DG_HTTP: |
|
||||||
case ITaskWrapper.D_FTP_DIR: |
|
||||||
className = "com.arialyy.aria.core.listener.DownloadGroupListener"; |
|
||||||
break; |
|
||||||
} |
|
||||||
if (className == null) { |
|
||||||
return null; |
|
||||||
} |
|
||||||
T listener = null; |
|
||||||
try { |
|
||||||
Class<T> clazz = (Class<T>) getClass().getClassLoader().loadClass(className); |
|
||||||
Constructor<T> con = clazz.getConstructor(); |
|
||||||
listener = con.newInstance(); |
|
||||||
Method method = |
|
||||||
CommonUtil.getMethod(clazz, "setParams", AbsTask.class, Handler.class); |
|
||||||
method.invoke(listener, task, outHandler); |
|
||||||
} catch (ClassNotFoundException e) { |
|
||||||
throw new IllegalArgumentException(errorStr); |
|
||||||
} catch (NoSuchMethodException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} catch (IllegalAccessException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} catch (InstantiationException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} catch (InvocationTargetException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
return listener; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 构建TaskOption信息 |
|
||||||
* |
|
||||||
* @param clazz 实现{@link ITaskOption}接口的配置信息类 |
|
||||||
* @param params 任务配置信息参数 |
|
||||||
* @return 构建失败,返回null |
|
||||||
*/ |
|
||||||
public synchronized <T extends ITaskOption> T buildTaskOption(Class<T> clazz, |
|
||||||
TaskOptionParams params) { |
|
||||||
List<Field> fields = CommonUtil.getAllFields(clazz); |
|
||||||
T taskOption = null; |
|
||||||
try { |
|
||||||
taskOption = clazz.newInstance(); |
|
||||||
for (Field field : fields) { |
|
||||||
field.setAccessible(true); |
|
||||||
Class type = field.getType(); |
|
||||||
String key = field.getName(); |
|
||||||
if (type != SoftReference.class) { |
|
||||||
Object obj = params.getParams().get(key); |
|
||||||
if (obj == null) { |
|
||||||
continue; |
|
||||||
} |
|
||||||
field.set(taskOption, obj); |
|
||||||
} else { |
|
||||||
IEventHandler handler = params.getHandler().get(key); |
|
||||||
if (handler == null) { |
|
||||||
continue; |
|
||||||
} |
|
||||||
field.set(taskOption, new SoftReference<>(handler)); |
|
||||||
} |
|
||||||
} |
|
||||||
} catch (IllegalAccessException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} catch (InstantiationException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
return taskOption; |
|
||||||
} |
|
||||||
} |
|
@ -1,42 +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.util; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.common.AbsEntity; |
|
||||||
|
|
||||||
/** |
|
||||||
* 删除记录 |
|
||||||
*/ |
|
||||||
public interface IDeleteRecord { |
|
||||||
|
|
||||||
/** |
|
||||||
* 删除记录 |
|
||||||
* |
|
||||||
* @param key 记录关联的key |
|
||||||
* @param needRemoveFile 是否需要删除文件 |
|
||||||
* @param needRemoveEntity 是否需要删除实体,true 删除实体 |
|
||||||
*/ |
|
||||||
void deleteRecord(String key, boolean needRemoveFile, boolean needRemoveEntity); |
|
||||||
|
|
||||||
/** |
|
||||||
* 删除记录 |
|
||||||
* |
|
||||||
* @param absEntity 记录关联的实体 |
|
||||||
* @param needRemoveFile 是否需要删除文件 |
|
||||||
* @param needRemoveEntity 是否需要删除实体,true 删除实体 |
|
||||||
*/ |
|
||||||
void deleteRecord(AbsEntity absEntity, boolean needRemoveFile, boolean needRemoveEntity); |
|
||||||
} |
|
Loading…
Reference in new issue