Command Reconfiguration

v4
laoyuyu 2 years ago
parent c06b1fd5fb
commit fa7741b3e9
  1. 4
      Aria/src/main/java/com/arialyy/aria/core/common/HttpOption.java
  2. 10
      Http/src/main/java/com/arialyy/aria/http/HttpComponent.kt
  3. 13
      Http/src/main/java/com/arialyy/aria/http/HttpComponentLoader.kt
  4. 7
      Http/src/main/java/com/arialyy/aria/http/HttpTaskOption.kt
  5. 2
      Http/src/main/java/com/arialyy/aria/http/IHttpFileLenAdapter.java
  6. 3
      Http/src/main/java/com/arialyy/aria/http/IHttpFileNameAdapter.java
  7. 2
      Http/src/main/java/com/arialyy/aria/http/IHttpTaskOptionDelegate.kt
  8. 8
      Http/src/main/java/com/arialyy/aria/http/download/HttpBlockThreadInterceptor.kt
  9. 2
      Http/src/main/java/com/arialyy/aria/http/download/HttpDBTaskAdapter.kt
  10. 2
      Http/src/main/java/com/arialyy/aria/http/download/HttpDBlockInterceptor.kt
  11. 2
      Http/src/main/java/com/arialyy/aria/http/download/HttpDBlockManager.kt
  12. 6
      Http/src/main/java/com/arialyy/aria/http/download/HttpDHeaderInterceptor.kt
  13. 6
      Http/src/main/java/com/arialyy/aria/http/download/HttpDOptionDelegate.kt
  14. 6
      Http/src/main/java/com/arialyy/aria/http/download/HttpDStartController.kt
  15. 6
      HttpGroup/src/main/java/com/arialyy/dua/group/HttpDGAdapter.kt
  16. 8
      HttpGroup/src/main/java/com/arialyy/dua/group/HttpDGCheckInterceptor.kt
  17. 29
      HttpGroup/src/main/java/com/arialyy/dua/group/HttpDGComponent.kt
  18. 7
      HttpGroup/src/main/java/com/arialyy/dua/group/HttpDGComponentLoader.kt
  19. 20
      HttpGroup/src/main/java/com/arialyy/dua/group/HttpDGEventListener.kt
  20. 11
      HttpGroup/src/main/java/com/arialyy/dua/group/HttpDGListener.kt
  21. 7
      HttpGroup/src/main/java/com/arialyy/dua/group/HttpDGLoader.kt
  22. 4
      HttpGroup/src/main/java/com/arialyy/dua/group/HttpDGOptionDelegate.kt
  23. 64
      HttpGroup/src/main/java/com/arialyy/dua/group/HttpDGStartController.kt
  24. 8
      HttpGroup/src/main/java/com/arialyy/dua/group/HttpDGSubTaskInterceptor.kt
  25. 8
      HttpGroup/src/main/java/com/arialyy/dua/group/HttpDGTask.java
  26. 63
      HttpGroup/src/main/java/com/arialyy/dua/group/HttpDGTaskManager.kt
  27. 5
      HttpGroup/src/main/java/com/arialyy/dua/group/HttpSubBlockManager.kt
  28. 58
      HttpGroup/src/main/java/com/arialyy/dua/group/HttpSubListener.kt
  29. 2
      HttpGroup/src/main/resources/META-INF/services/com.arialyy.aria.core.inf.IComponentInit
  30. 68
      PublicComponent/src/main/java/com/arialyy/aria/core/command/AbsGroupCmd.java
  31. 44
      PublicComponent/src/main/java/com/arialyy/aria/core/command/CmdHelper.java
  32. 34
      PublicComponent/src/main/java/com/arialyy/aria/core/command/DGSubStopCmd.java
  33. 68
      PublicComponent/src/main/java/com/arialyy/aria/core/command/GroupCmdFactory.java
  34. 57
      PublicComponent/src/main/java/com/arialyy/aria/core/command/HighestPriorityCmd.java
  35. 41
      PublicComponent/src/main/java/com/arialyy/aria/core/command/ReStartCmd.java
  36. 28
      PublicComponent/src/main/java/com/arialyy/aria/core/command/ResumeAllCmd.kt
  37. 200
      PublicComponent/src/main/java/com/arialyy/aria/core/command/ResumeThread.java
  38. 3
      PublicComponent/src/main/java/com/arialyy/aria/core/command/StopAllCmd.java
  39. 14
      PublicComponent/src/main/java/com/arialyy/aria/core/event/ResumeAllEvent.kt
  40. 375
      PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoader.java
  41. 107
      PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoaderAdapter.java
  42. 165
      PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsSubDLoadAdapter.java
  43. 189
      PublicComponent/src/main/java/com/arialyy/aria/core/group/GroupRunState.java
  44. 37
      PublicComponent/src/main/java/com/arialyy/aria/core/group/GroupSendParams.java
  45. 85
      PublicComponent/src/main/java/com/arialyy/aria/core/group/ISubQueue.java
  46. 230
      PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSchedulers.java
  47. 201
      PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSubQueue.java
  48. 49
      PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSubRetryQueue.java
  49. 108
      PublicComponent/src/main/java/com/arialyy/aria/core/group/SubRecordHandler.java
  50. 9
      PublicComponent/src/main/java/com/arialyy/aria/core/inf/IComponentLoader.kt
  51. 340
      PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalLoader.java
  52. 153
      PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalLoaderAdapter.java
  53. 90
      PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalTTBuilderAdapter.java
  54. 311
      PublicComponent/src/main/java/com/arialyy/aria/core/loader/GroupSubThreadStateManager.java
  55. 62
      PublicComponent/src/main/java/com/arialyy/aria/core/loader/IInfoTask.java
  56. 43
      PublicComponent/src/main/java/com/arialyy/aria/core/loader/ILoader.java
  57. 47
      PublicComponent/src/main/java/com/arialyy/aria/core/loader/ILoaderAdapter.java
  58. 44
      PublicComponent/src/main/java/com/arialyy/aria/core/loader/ILoaderVisitor.java
  59. 87
      PublicComponent/src/main/java/com/arialyy/aria/core/loader/IRecordHandler.java
  60. 37
      PublicComponent/src/main/java/com/arialyy/aria/core/loader/IThreadTaskBuilder.java
  61. 45
      PublicComponent/src/main/java/com/arialyy/aria/core/loader/LoaderStructure.java
  62. 169
      PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java
  63. 166
      PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalTTBuilder.java
  64. 291
      PublicComponent/src/main/java/com/arialyy/aria/core/loader/SubLoader.java
  65. 38
      PublicComponent/src/main/java/com/arialyy/aria/core/loader/SubTTBuilder.java
  66. 259
      PublicComponent/src/main/java/com/arialyy/aria/core/loader/TaskThreadStateManager.java
  67. 68
      PublicComponent/src/main/java/com/arialyy/aria/core/processor/IFtpUploadInterceptor.java
  68. 2
      PublicComponent/src/main/java/com/arialyy/aria/core/provider/DuaStartupProvider.kt
  69. 73
      PublicComponent/src/main/java/com/arialyy/aria/core/task/UploadTask.java
  70. 74
      PublicComponent/src/main/java/com/arialyy/aria/util/AriaCrashHandler.java
  71. 209
      PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java
  72. 262
      PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java
  73. 24
      PublicComponent/src/main/java/com/arialyy/aria/util/DuaUtil.kt
  74. 42
      PublicComponent/src/main/java/com/arialyy/aria/util/IDeleteRecord.java
  75. 2
      app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java
  76. 2
      app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java

@ -16,8 +16,8 @@
package com.arialyy.aria.core.common; package com.arialyy.aria.core.common;
import android.text.TextUtils; import android.text.TextUtils;
import com.arialyy.aria.core.processor.IHttpFileLenAdapter; import com.arialyy.aria.http.IHttpFileLenAdapter;
import com.arialyy.aria.core.processor.IHttpFileNameAdapter; import com.arialyy.aria.http.IHttpFileNameAdapter;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.FileUtils; import com.arialyy.aria.util.FileUtils;
import java.net.Proxy; import java.net.Proxy;

@ -1,6 +1,9 @@
package com.arialyy.aria.http package com.arialyy.aria.http
import android.content.Context import android.content.Context
import com.arialyy.aria.core.event.Event
import com.arialyy.aria.core.event.EventMsgUtil
import com.arialyy.aria.core.event.ResumeAllEvent
import com.arialyy.aria.core.inf.IComponentInit import com.arialyy.aria.core.inf.IComponentInit
/** /**
@ -10,5 +13,12 @@ import com.arialyy.aria.core.inf.IComponentInit
**/ **/
class HttpComponent : IComponentInit { class HttpComponent : IComponentInit {
override fun init(context: Context) { override fun init(context: Context) {
EventMsgUtil.getDefault().register(this)
} }
@Event
fun resumeAll(resumeAllEvent: ResumeAllEvent) {
}
} }

@ -19,7 +19,6 @@ import com.arialyy.annotations.TaskEnum
import com.arialyy.aria.core.DuaContext import com.arialyy.aria.core.DuaContext
import com.arialyy.aria.core.inf.IBaseLoader import com.arialyy.aria.core.inf.IBaseLoader
import com.arialyy.aria.core.inf.IComponentLoader import com.arialyy.aria.core.inf.IComponentLoader
import com.arialyy.aria.core.inf.IDownloader
import com.arialyy.aria.http.download.HttpDownloader import com.arialyy.aria.http.download.HttpDownloader
import com.arialyy.aria.http.upload.HttpULoader import com.arialyy.aria.http.upload.HttpULoader
import kotlin.LazyThreadSafetyMode.SYNCHRONIZED import kotlin.LazyThreadSafetyMode.SYNCHRONIZED
@ -41,17 +40,17 @@ class HttpComponentLoader : IComponentLoader {
private lateinit var loader: IBaseLoader private lateinit var loader: IBaseLoader
override fun <T : IDownloader> download(): T { fun download(): HttpDownloader {
loader = downloader loader = downloader
return downloader as T return downloader
}
override fun getTaskEnum(): TaskEnum {
return TaskEnum.DOWNLOAD
} }
// override fun <T : IUploader> upload(): T { // override fun <T : IUploader> upload(): T {
// loader = uploader // loader = uploader
// return uploader as T // return uploader as T
// } // }
override fun getTaskEnum(): TaskEnum {
return loader.getTaskEnum()
}
} }

@ -17,7 +17,6 @@ package com.arialyy.aria.http
import com.arialyy.aria.core.common.TaskOption import com.arialyy.aria.core.common.TaskOption
import com.arialyy.aria.core.task.ITaskInterceptor import com.arialyy.aria.core.task.ITaskInterceptor
import com.arialyy.aria.orm.entity.BlockRecord
/** /**
* @Author laoyuyu * @Author laoyuyu
@ -28,9 +27,9 @@ class HttpTaskOption : TaskOption() {
var httpOption: HttpOption? = null var httpOption: HttpOption? = null
var taskInterceptor = mutableListOf<ITaskInterceptor>() var taskInterceptor = mutableListOf<ITaskInterceptor>()
lateinit var taskOptionAdapter: IHttpTaskOptionAdapter lateinit var taskOptionDelegate: IHttpTaskOptionDelegate
fun <T : IHttpTaskOptionAdapter> getOptionAdapter(clazz: Class<T>): T { fun <T : IHttpTaskOptionDelegate> getOptionDelegate(clazz: Class<T>): T {
return taskOptionAdapter as T return taskOptionDelegate as T
} }
} }

@ -13,7 +13,7 @@
* 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.http;
import com.arialyy.aria.core.inf.IEventHandler; import com.arialyy.aria.core.inf.IEventHandler;
import java.net.URLConnection; import java.net.URLConnection;

@ -13,9 +13,8 @@
* 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.http;
import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.inf.IEventHandler; import com.arialyy.aria.core.inf.IEventHandler;
import java.net.URLConnection; import java.net.URLConnection;

@ -20,4 +20,4 @@ package com.arialyy.aria.http
* @Description * @Description
* @Date 8:03 PM 2023/3/6 * @Date 8:03 PM 2023/3/6
**/ **/
interface IHttpTaskOptionAdapter interface IHttpTaskOptionDelegate

@ -33,12 +33,12 @@ import com.arialyy.aria.orm.entity.BlockRecord
**/ **/
class HttpBlockThreadInterceptor : ITaskInterceptor { class HttpBlockThreadInterceptor : ITaskInterceptor {
private lateinit var blockManager: IBlockManager private lateinit var blockManager: IBlockManager
private lateinit var taskOption: HttpDOptionAdapter private lateinit var taskOption: HttpDOptionDelegate
override suspend fun interceptor(chain: TaskChain): TaskResp { override suspend fun interceptor(chain: TaskChain): TaskResp {
blockManager = chain.blockManager blockManager = chain.blockManager
taskOption = chain.getTask().getTaskOption(HttpTaskOption::class.java) taskOption = chain.getTask().getTaskOption(HttpTaskOption::class.java)
.getOptionAdapter(HttpDOptionAdapter::class.java) .getOptionDelegate(HttpDOptionDelegate::class.java)
val unfinishedBlockList = taskOption.getUnfinishedBlockList() val unfinishedBlockList = taskOption.getUnfinishedBlockList()
if (unfinishedBlockList.isEmpty()) { if (unfinishedBlockList.isEmpty()) {
return TaskResp(TaskResp.CODE_INTERRUPT) return TaskResp(TaskResp.CODE_INTERRUPT)
@ -55,7 +55,7 @@ class HttpBlockThreadInterceptor : ITaskInterceptor {
val threadConfig = ThreadConfig(it, option, DuaContext.getDConfig().maxSpeed) val threadConfig = ThreadConfig(it, option, DuaContext.getDConfig().maxSpeed)
threadTaskList.add( threadTaskList.add(
ThreadTask2( ThreadTask2(
adapter = if (option.getOptionAdapter(HttpDOptionAdapter::class.java).isChunkTask) HttpDCTTaskAdapter( adapter = if (option.getOptionDelegate(HttpDOptionDelegate::class.java).isChunkTask) HttpDCTTaskAdapter(
threadConfig threadConfig
) else HttpDBTaskAdapter( ) else HttpDBTaskAdapter(
threadConfig threadConfig
@ -65,7 +65,7 @@ class HttpBlockThreadInterceptor : ITaskInterceptor {
) )
) )
} }
option.getOptionAdapter(HttpDOptionAdapter::class.java).threadList = threadTaskList option.getOptionDelegate(HttpDOptionDelegate::class.java).threadList = threadTaskList
blockManager.start() blockManager.start()
} }
} }

@ -43,7 +43,7 @@ class HttpDBTaskAdapter(threadConfig: ThreadConfig) : AbsThreadTaskAdapter(threa
val taskOption = getTaskOption() val taskOption = getTaskOption()
val option = taskOption.httpOption!! val option = taskOption.httpOption!!
val conn = getRequest(option).getDConnection(taskOption.sourUrl!!, option) val conn = getRequest(option).getDConnection(taskOption.sourUrl!!, option)
if (!taskOption.getOptionAdapter(HttpDOptionAdapter::class.java).isSupportResume) { if (!taskOption.getOptionDelegate(HttpDOptionDelegate::class.java).isSupportResume) {
Timber.w("this task not support resume, url: %s", taskOption.sourUrl) Timber.w("this task not support resume, url: %s", taskOption.sourUrl)
} else { } else {
conn.setRequestProperty( conn.setRequestProperty(

@ -136,7 +136,7 @@ internal class HttpDBlockInterceptor : ITaskInterceptor {
if (br.curProgress != blockF.length()) { if (br.curProgress != blockF.length()) {
br.curProgress = blockF.length() br.curProgress = blockF.length()
needUpdateBlockRecord.add(br) needUpdateBlockRecord.add(br)
taskOption.getOptionAdapter(HttpDOptionAdapter::class.java).putUnfinishedBlock(br) taskOption.getOptionDelegate(HttpDOptionDelegate::class.java).putUnfinishedBlock(br)
} }
// update task progress // update task progress
handler.obtainMessage(ITaskManager.STATE_UPDATE_PROGRESS, br.curProgress) handler.obtainMessage(ITaskManager.STATE_UPDATE_PROGRESS, br.curProgress)

@ -27,7 +27,7 @@ import kotlinx.coroutines.launch
**/ **/
class HttpDBlockManager(task: ITask) : DBlockManager(task) { class HttpDBlockManager(task: ITask) : DBlockManager(task) {
private val optionAdapter = private val optionAdapter =
task.getTaskOption(HttpTaskOption::class.java).getOptionAdapter(HttpDOptionAdapter::class.java) task.getTaskOption(HttpTaskOption::class.java).getOptionDelegate(HttpDOptionDelegate::class.java)
override fun start() { override fun start() {
optionAdapter.threadList.forEach { tt -> optionAdapter.threadList.forEach { tt ->

@ -20,7 +20,7 @@ import android.net.Uri
import android.os.Looper import android.os.Looper
import android.os.Process import android.os.Process
import android.text.TextUtils import android.text.TextUtils
import com.arialyy.aria.core.processor.IHttpFileLenAdapter import com.arialyy.aria.http.IHttpFileLenAdapter
import com.arialyy.aria.core.task.ITask import com.arialyy.aria.core.task.ITask
import com.arialyy.aria.core.task.ITaskInterceptor import com.arialyy.aria.core.task.ITaskInterceptor
import com.arialyy.aria.core.task.TaskChain import com.arialyy.aria.core.task.TaskChain
@ -56,8 +56,8 @@ internal class HttpDHeaderInterceptor : ITaskInterceptor {
) )
} }
private fun getOptionAdapter(): HttpDOptionAdapter { private fun getOptionAdapter(): HttpDOptionDelegate {
return taskOption.getOptionAdapter(HttpDOptionAdapter::class.java) return taskOption.getOptionDelegate(HttpDOptionDelegate::class.java)
} }
override suspend fun interceptor(chain: TaskChain): TaskResp { override suspend fun interceptor(chain: TaskChain): TaskResp {

@ -15,9 +15,9 @@
*/ */
package com.arialyy.aria.http.download package com.arialyy.aria.http.download
import com.arialyy.aria.core.processor.IHttpFileLenAdapter import com.arialyy.aria.http.IHttpFileLenAdapter
import com.arialyy.aria.core.task.IThreadTask import com.arialyy.aria.core.task.IThreadTask
import com.arialyy.aria.http.IHttpTaskOptionAdapter import com.arialyy.aria.http.IHttpTaskOptionDelegate
import com.arialyy.aria.orm.entity.BlockRecord import com.arialyy.aria.orm.entity.BlockRecord
/** /**
@ -25,7 +25,7 @@ import com.arialyy.aria.orm.entity.BlockRecord
* @Description * @Description
* @Date 2:03 PM 2023/3/6 * @Date 2:03 PM 2023/3/6
**/ **/
class HttpDOptionAdapter : IHttpTaskOptionAdapter { class HttpDOptionDelegate : IHttpTaskOptionDelegate {
var fileSizeAdapter: IHttpFileLenAdapter? = null var fileSizeAdapter: IHttpFileLenAdapter? = null
var isChunkTask = false var isChunkTask = false

@ -21,7 +21,7 @@ import com.arialyy.aria.core.command.AddCmd
import com.arialyy.aria.core.command.CancelCmd import com.arialyy.aria.core.command.CancelCmd
import com.arialyy.aria.core.command.StartCmd import com.arialyy.aria.core.command.StartCmd
import com.arialyy.aria.core.command.StopCmd import com.arialyy.aria.core.command.StopCmd
import com.arialyy.aria.core.processor.IHttpFileLenAdapter import com.arialyy.aria.http.IHttpFileLenAdapter
import com.arialyy.aria.core.task.SingleDownloadTask import com.arialyy.aria.core.task.SingleDownloadTask
import com.arialyy.aria.core.task.ITaskInterceptor import com.arialyy.aria.core.task.ITaskInterceptor
import com.arialyy.aria.core.task.TaskCachePool import com.arialyy.aria.core.task.TaskCachePool
@ -37,11 +37,11 @@ import java.net.HttpURLConnection
* @Date 12:38 PM 2023/1/22 * @Date 12:38 PM 2023/1/22
**/ **/
class HttpDStartController(target: Any, val url: String) : HttpBaseStartController(target) { class HttpDStartController(target: Any, val url: String) : HttpBaseStartController(target) {
private val taskOptionAdapter = HttpDOptionAdapter() private val taskOptionAdapter = HttpDOptionDelegate()
init { init {
httpTaskOption.sourUrl = url httpTaskOption.sourUrl = url
httpTaskOption.taskOptionAdapter = taskOptionAdapter httpTaskOption.taskOptionDelegate = taskOptionAdapter
} }
override fun setTaskInterceptor(taskInterceptor: ITaskInterceptor): HttpDStartController { override fun setTaskInterceptor(taskInterceptor: ITaskInterceptor): HttpDStartController {

@ -33,16 +33,16 @@ import timber.log.Timber
* @Description * @Description
* @Date 21:58 2023/2/20 * @Date 21:58 2023/2/20
**/ **/
internal class HttpDGroupAdapter : AbsTaskAdapter() { internal class HttpDGAdapter : AbsTaskAdapter() {
private val taskManager by lazy { private val taskManager by lazy {
val manager = HttpDGTaskManager(getTask() as HttpDGroupTask) val manager = HttpDGTaskManager(getTask() as HttpDGTask)
ThreadTaskManager2.putTaskManager(getTask().taskId, manager) ThreadTaskManager2.putTaskManager(getTask().taskId, manager)
manager manager
} }
init { init {
getTask().getTaskOption(HttpTaskOption::class.java).eventListener = getTask().getTaskOption(HttpTaskOption::class.java).eventListener =
HttpDGEventListener(getTask() as HttpDGroupTask) HttpDGEventListener(getTask() as HttpDGTask)
} }
override fun getBlockManager(): IBlockManager { override fun getBlockManager(): IBlockManager {

@ -24,7 +24,7 @@ import com.arialyy.aria.core.task.TaskResp
import com.arialyy.aria.http.HttpTaskOption import com.arialyy.aria.http.HttpTaskOption
import com.arialyy.aria.orm.entity.DEntity import com.arialyy.aria.orm.entity.DEntity
import com.arialyy.aria.orm.entity.DGEntity import com.arialyy.aria.orm.entity.DGEntity
import com.arialyy.aria.util.CheckUtil import com.arialyy.aria.util.DuaUtil
import com.arialyy.aria.util.FileUri import com.arialyy.aria.util.FileUri
import com.arialyy.aria.util.FileUtils import com.arialyy.aria.util.FileUtils
import timber.log.Timber import timber.log.Timber
@ -40,7 +40,7 @@ import java.io.File
internal class HttpDGCheckInterceptor : ITaskInterceptor { internal class HttpDGCheckInterceptor : ITaskInterceptor {
private lateinit var task: ITask private lateinit var task: ITask
private lateinit var optionAdapter: HttpDGOptionAdapter private lateinit var optionAdapter: HttpDGOptionDelegate
private lateinit var taskOption: HttpTaskOption private lateinit var taskOption: HttpTaskOption
private val dgDao by lazy { private val dgDao by lazy {
DuaContext.getServiceManager().getDbService().getDuaDb().getDGEntityDao() DuaContext.getServiceManager().getDbService().getDuaDb().getDGEntityDao()
@ -49,7 +49,7 @@ internal class HttpDGCheckInterceptor : ITaskInterceptor {
override suspend fun interceptor(chain: TaskChain): TaskResp { override suspend fun interceptor(chain: TaskChain): TaskResp {
task = chain.getTask() task = chain.getTask()
taskOption = task.getTaskOption(HttpTaskOption::class.java) taskOption = task.getTaskOption(HttpTaskOption::class.java)
optionAdapter = taskOption.getOptionAdapter(HttpDGOptionAdapter::class.java) optionAdapter = taskOption.getOptionDelegate(HttpDGOptionDelegate::class.java)
if (checkParams(taskOption.savePathDir)) { if (checkParams(taskOption.savePathDir)) {
return TaskResp(TaskResp.CODE_INTERRUPT) return TaskResp(TaskResp.CODE_INTERRUPT)
@ -140,7 +140,7 @@ internal class HttpDGCheckInterceptor : ITaskInterceptor {
} }
optionAdapter.subUrlList.forEach { optionAdapter.subUrlList.forEach {
if (!CheckUtil.checkUrl(it)) { if (!DuaUtil.checkUrl(it)) {
Timber.e("invalid url: $it") Timber.e("invalid url: $it")
return false return false
} }

@ -13,22 +13,25 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.arialyy.aria.core.command; package com.arialyy.dua.group
import com.arialyy.aria.core.download.AbsGroupTaskWrapper; import android.content.Context
import com.arialyy.aria.core.event.Event
import com.arialyy.aria.core.event.EventMsgUtil
import com.arialyy.aria.core.event.ResumeAllEvent
import com.arialyy.aria.core.inf.IComponentInit
/** /**
* Created by AriaL on 2017/6/29. * @Author laoyuyu
* 任务组开始命令该命令负责处理任务组子任务的开始\恢复等工作 * @Description
*/ * @Date 21:51 2023/2/20
final class DGSubStartCmd<T extends AbsGroupTaskWrapper> extends AbsGroupCmd<T> { **/
DGSubStartCmd(T wrapper) { class HttpDGComponent : IComponentInit {
super(wrapper); override fun init(context: Context) {
EventMsgUtil.getDefault().register(this)
} }
@Override public void executeCmd() { @Event
if (checkTask()) { fun resumeAll(resumeAllEvent: ResumeAllEvent) {
tempTask.startSubTask(childUrl);
}
} }
} }

@ -18,20 +18,19 @@ package com.arialyy.dua.group
import com.arialyy.annotations.TaskEnum import com.arialyy.annotations.TaskEnum
import com.arialyy.aria.core.DuaContext import com.arialyy.aria.core.DuaContext
import com.arialyy.aria.core.inf.IComponentLoader import com.arialyy.aria.core.inf.IComponentLoader
import com.arialyy.aria.core.inf.IDownloader
/** /**
* @Author laoyuyu * @Author laoyuyu
* @Description * @Description
* @Date 7:51 AM 2023/3/6 * @Date 7:51 AM 2023/3/6
**/ **/
class HttpDGComponentLoader : IComponentLoader { internal class HttpDGComponentLoader : IComponentLoader {
private val downloader by lazy { private val downloader by lazy {
HttpDGLoader(DuaContext.getLifeManager().getTargetByLoader(this)!!) HttpDGLoader(DuaContext.getLifeManager().getTargetByLoader(this)!!)
} }
override fun <T : IDownloader> download(): T { fun download(): HttpDGLoader {
return downloader as T return downloader
} }
override fun getTaskEnum(): TaskEnum { override fun getTaskEnum(): TaskEnum {

@ -15,19 +15,33 @@
*/ */
package com.arialyy.dua.group package com.arialyy.dua.group
import com.arialyy.aria.core.DuaContext
import com.arialyy.aria.core.inf.IEntity
import com.arialyy.aria.core.listener.AbsEventListener import com.arialyy.aria.core.listener.AbsEventListener
import com.arialyy.aria.core.listener.ISchedulers
import com.arialyy.aria.core.task.TaskCachePool
import com.arialyy.aria.orm.entity.DEntity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
/** /**
* @Author laoyuyu * @Author laoyuyu
* @Description * @Description
* @Date 9:15 PM 2023/3/6 * @Date 9:15 PM 2023/3/6
**/ **/
internal class HttpDGEventListener(task: HttpDGroupTask) : AbsEventListener(task) { internal class HttpDGEventListener(task: HttpDGTask) : AbsEventListener(task) {
override fun onComplete() { override fun onComplete() {
TODO("Not yet implemented") handleSpeed(0)
sendInState2Target(ISchedulers.COMPLETE)
saveData(IEntity.STATE_COMPLETE, task.taskState.fileSize)
} }
override fun handleCancel() { override fun handleCancel() {
TODO("Not yet implemented") DuaContext.duaScope.launch(Dispatchers.IO) {
val entity = TaskCachePool.getEntity(taskId = task.taskId)
val db = DuaContext.getServiceManager().getDbService().getDuaDb()
db.getRecordDao().deleteTaskRecord(task.taskState.taskRecord)
db.getDEntityDao().delete(entity as DEntity)
}
} }
} }

@ -15,16 +15,11 @@
*/ */
package com.arialyy.dua.group package com.arialyy.dua.group
import android.content.Context import com.arialyy.aria.core.listener.ITaskStatusListener
import com.arialyy.aria.core.inf.IComponentInit
/** /**
* @Author laoyuyu * @Author laoyuyu
* @Description * @Description
* @Date 21:51 2023/2/20 * @Date 7:26 PM 2023/3/21
**/ **/
class HttpGroupComponent : IComponentInit { class HttpDGListener : ITaskStatusListener<HttpDGTask>
override fun init(context: Context) {
}
}

@ -18,13 +18,14 @@ package com.arialyy.dua.group
import android.net.Uri import android.net.Uri
import com.arialyy.annotations.TaskEnum import com.arialyy.annotations.TaskEnum
import com.arialyy.aria.core.inf.IDownloader import com.arialyy.aria.core.inf.IDownloader
import com.arialyy.aria.http.download.HttpDStopController
/** /**
* @Author laoyuyu * @Author laoyuyu
* @Description * @Description
* @Date 7:53 AM 2023/3/6 * @Date 7:53 AM 2023/3/6
**/ **/
class HttpDGLoader(val target: Any) : IDownloader { class HttpDGLoader(private val target: Any) : IDownloader {
override fun getTaskEnum(): TaskEnum { override fun getTaskEnum(): TaskEnum {
return TaskEnum.DOWNLOAD_GROUP return TaskEnum.DOWNLOAD_GROUP
} }
@ -36,4 +37,8 @@ class HttpDGLoader(val target: Any) : IDownloader {
fun load(savePath: Uri): HttpDGStartController { fun load(savePath: Uri): HttpDGStartController {
return HttpDGStartController(target, savePath) return HttpDGStartController(target, savePath)
} }
fun load(taskId: Int): HttpDStopController {
return HttpDStopController(taskId)
}
} }

@ -15,14 +15,14 @@
*/ */
package com.arialyy.dua.group package com.arialyy.dua.group
import com.arialyy.aria.http.IHttpTaskOptionAdapter import com.arialyy.aria.http.IHttpTaskOptionDelegate
/** /**
* @Author laoyuyu * @Author laoyuyu
* @Description * @Description
* @Date 8:14 PM 2023/3/6 * @Date 8:14 PM 2023/3/6
**/ **/
internal class HttpDGOptionAdapter : IHttpTaskOptionAdapter { internal class HttpDGOptionDelegate : IHttpTaskOptionDelegate {
val subUrlList = mutableSetOf<String>() val subUrlList = mutableSetOf<String>()
val subNameList = mutableListOf<String>() val subNameList = mutableListOf<String>()

@ -16,7 +16,11 @@
package com.arialyy.dua.group package com.arialyy.dua.group
import android.net.Uri import android.net.Uri
import com.arialyy.aria.core.DuaContext
import com.arialyy.aria.core.command.AddCmd
import com.arialyy.aria.core.command.CancelCmd
import com.arialyy.aria.core.command.StartCmd import com.arialyy.aria.core.command.StartCmd
import com.arialyy.aria.core.command.StopCmd
import com.arialyy.aria.core.task.ITaskInterceptor import com.arialyy.aria.core.task.ITaskInterceptor
import com.arialyy.aria.core.task.TaskCachePool import com.arialyy.aria.core.task.TaskCachePool
import com.arialyy.aria.http.HttpBaseStartController import com.arialyy.aria.http.HttpBaseStartController
@ -30,12 +34,13 @@ import timber.log.Timber
* @Description * @Description
* @Date 7:47 PM 2023/3/6 * @Date 7:47 PM 2023/3/6
**/ **/
class HttpDGStartController(target: Any, val savePath: Uri) : HttpBaseStartController(target) { class HttpDGStartController(target: Any, private val savePath: Uri) :
HttpBaseStartController(target) {
private val optionAdapter = HttpDGOptionAdapter() private val optionAdapter = HttpDGOptionDelegate()
init { init {
httpTaskOption.taskOptionAdapter = optionAdapter httpTaskOption.taskOptionDelegate = optionAdapter
httpTaskOption.savePathDir = savePath httpTaskOption.savePathDir = savePath
} }
@ -51,6 +56,14 @@ class HttpDGStartController(target: Any, val savePath: Uri) : HttpBaseStartContr
return super.setHttpOption(httpOption) as HttpDGStartController return super.setHttpOption(httpOption) as HttpDGStartController
} }
/**
* set download listener
*/
fun setListener(listener: HttpDGListener): HttpDGStartController {
DuaContext.getLifeManager().addCustomListener(target, listener)
return this
}
/** /**
* Number of subtasks executed simultaneously * Number of subtasks executed simultaneously
* @param num max 16 * @param num max 16
@ -84,20 +97,33 @@ class HttpDGStartController(target: Any, val savePath: Uri) : HttpBaseStartContr
return this return this
} }
private fun getTask(createNewTask: Boolean = true): HttpDGroupTask { private fun getTask(createNewTask: Boolean = true): HttpDGTask? {
if (HttpUtil.checkHttpDParams(httpTaskOption)) { if (HttpUtil.checkHttpDParams(httpTaskOption)) {
throw IllegalArgumentException("invalid params") throw IllegalArgumentException("invalid params")
} }
val temp = TaskCachePool.getTaskByKey(savePath.toString()) val temp = TaskCachePool.getTaskByKey(savePath.toString())
if (temp != null) { if (temp != null) {
return temp as HttpDGroupTask return temp as HttpDGTask
}
if (!createNewTask) {
return null
} }
val task = HttpDGroupTask(httpTaskOption) val task = HttpDGTask(httpTaskOption)
task.adapter = HttpDGroupAdapter() task.adapter = HttpDGAdapter()
TaskCachePool.putTask(task) TaskCachePool.putTask(task)
return task return task
} }
fun add(): Int {
if (!FileUtils.uriEffective(savePath)) {
Timber.e("invalid savePath: $savePath")
return -1
}
val task = getTask()
val resp = AddCmd(task).executeCmd()
return if (resp.isInterrupt()) -1 else task?.taskId ?: -1
}
/** /**
* Start task * Start task
* @return taskId * @return taskId
@ -110,6 +136,28 @@ class HttpDGStartController(target: Any, val savePath: Uri) : HttpBaseStartContr
val task = getTask() val task = getTask()
val resp = StartCmd(task).executeCmd() val resp = StartCmd(task).executeCmd()
return if (resp.isInterrupt()) -1 else task.taskId return if (resp.isInterrupt()) -1 else task?.taskId ?: -1
}
fun resume(): Int {
return start()
}
fun cancel() {
val task = getTask(true)
if (task == null) {
Timber.e("not found task, savePath: $savePath")
return
}
CancelCmd(task).executeCmd()
}
fun stop() {
val task = getTask(false)
if (task == null) {
Timber.e("task not running, savePath: $savePath")
return
}
StopCmd(task).executeCmd()
} }
} }

@ -69,17 +69,17 @@ internal class HttpDGSubTaskInterceptor : ITaskInterceptor {
tp.sourUrl = it.sourceUrl tp.sourUrl = it.sourceUrl
tp.savePathDir = taskOption.savePathDir tp.savePathDir = taskOption.savePathDir
tp.threadNum = 1 tp.threadNum = 1
tp.eventListener = HttpSubListener()
val subTask = SingleDownloadTask(tp) val subTask = SingleDownloadTask(tp)
val subAdapter = HttpDTaskAdapter(true) val subAdapter = HttpDTaskAdapter(true)
subAdapter.setBlockManager(HttpSubBlockManager(subTask, chain.blockManager.handler)) subAdapter.setBlockManager(HttpSubBlockManager(subTask, chain.blockManager.handler))
subTask.adapter = subAdapter subTask.adapter = subAdapter
if (it.isComplete && checkTaskIsComplete(it)) { if (it.isComplete && checkTaskIsComplete(it)) {
(chain.getTask() as HttpDGroupTask).addIncompleteTaskList(subTask)
Timber.d("task already complete, sourUrl: ${it.sourceUrl}") Timber.d("task already complete, sourUrl: ${it.sourceUrl}")
chain.blockManager.handler.obtainMessage(ITaskManager.SUB_STATE_COMPLETE, subTask)
} else {
(chain.getTask() as HttpDGTask).addIncompleteTaskList(subTask)
} }
(chain.getTask() as HttpDGroupTask).addSubTask(subTask) (chain.getTask() as HttpDGTask).addSubTask(subTask)
} }
} }
} }

@ -28,18 +28,18 @@ import java.util.Objects;
* Created by AriaL on 2017/6/27. * Created by AriaL on 2017/6/27.
* 任务组任务 * 任务组任务
*/ */
public class HttpDGroupTask extends AbsTask { public class HttpDGTask extends AbsTask {
private final List<SingleDownloadTask> incompleteTaskList = new ArrayList<>(); private final List<SingleDownloadTask> incompleteTaskList = new ArrayList<>();
private final List<SingleDownloadTask> subTaskList = new ArrayList<>(); private final List<SingleDownloadTask> subTaskList = new ArrayList<>();
public HttpDGroupTask(ITaskOption taskOption) { public HttpDGTask(ITaskOption taskOption) {
super(taskOption); super(taskOption);
} }
public HttpDGOptionAdapter getDGOptionAdapter() { public HttpDGOptionDelegate getDGOptionAdapter() {
return getTaskOption(HttpTaskOption.class).getOptionAdapter(HttpDGOptionAdapter.class); return getTaskOption(HttpTaskOption.class).getOptionDelegate(HttpDGOptionDelegate.class);
} }
void setIncompleteTaskList(List<SingleDownloadTask> list) { void setIncompleteTaskList(List<SingleDownloadTask> list) {

@ -19,23 +19,28 @@ import android.os.Handler
import android.os.Looper import android.os.Looper
import com.arialyy.aria.core.inf.IBlockManager import com.arialyy.aria.core.inf.IBlockManager
import com.arialyy.aria.core.inf.ITaskManager import com.arialyy.aria.core.inf.ITaskManager
import com.arialyy.aria.core.inf.TaskSchedulerType
import com.arialyy.aria.core.listener.IEventListener import com.arialyy.aria.core.listener.IEventListener
import com.arialyy.aria.http.HttpTaskOption import com.arialyy.aria.http.HttpTaskOption
import com.arialyy.aria.http.download.HttpDTaskAdapter import com.arialyy.aria.http.download.HttpDTaskAdapter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope import kotlinx.coroutines.MainScope
import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber
import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.ThreadPoolExecutor import java.util.concurrent.ThreadPoolExecutor
import java.util.concurrent.TimeUnit.MILLISECONDS import java.util.concurrent.TimeUnit.MILLISECONDS
import java.util.concurrent.atomic.AtomicInteger
/** /**
* @Author laoyuyu * @Author laoyuyu
* @Description * @Description
* @Date 7:43 PM 2023/3/7 * @Date 7:43 PM 2023/3/7
**/ **/
internal class HttpDGTaskManager(val task: HttpDGroupTask) : ITaskManager, IBlockManager { internal class HttpDGTaskManager(private val task: HttpDGTask) : ITaskManager, IBlockManager {
private lateinit var looper: Looper private lateinit var looper: Looper
private lateinit var handler: Handler private lateinit var handler: Handler
private val subTaskNum = task.dgOptionAdapter.subTaskNum private val subTaskNum = task.dgOptionAdapter.subTaskNum
@ -46,20 +51,50 @@ internal class HttpDGTaskManager(val task: HttpDGroupTask) : ITaskManager, IBloc
) )
private val dispatcher = threadPool.asCoroutineDispatcher() private val dispatcher = threadPool.asCoroutineDispatcher()
private val scope = MainScope() private val scope = MainScope()
private var progress = 0L
private var lastUpdateTime = System.currentTimeMillis()
private val eventListener: IEventListener = private val eventListener: IEventListener =
task.getTaskOption(HttpTaskOption::class.java).eventListener task.getTaskOption(HttpTaskOption::class.java).eventListener
private val canceledNum = AtomicInteger(0) // 已经取消的线程的数
private val stoppedNum = AtomicInteger(0) // 已经停止的线程数
private val failedNum = AtomicInteger(0) // 失败的线程数
private val completedNum = AtomicInteger(0) // 完成的线程数
private val callback = Handler.Callback { msg -> private val callback = Handler.Callback { msg ->
when (msg.what) { when (msg.what) {
ITaskManager.SUB_STATE_STOP -> { ITaskManager.SUB_STATE_STOP -> {
stoppedNum.getAndDecrement()
if (isStopped()) {
Timber.d("isStopped")
eventListener.onStop(progress)
quitLooper()
}
} }
ITaskManager.SUB_STATE_CANCEL -> { ITaskManager.SUB_STATE_CANCEL -> {
canceledNum.getAndDecrement()
if (isCanceled()) {
Timber.d("isCanceled")
eventListener.onCancel()
quitLooper()
}
} }
ITaskManager.SUB_STATE_FAIL -> { ITaskManager.SUB_STATE_FAIL -> {
failedNum.getAndDecrement()
} }
ITaskManager.SUB_STATE_COMPLETE -> { ITaskManager.SUB_STATE_COMPLETE -> {
completedNum.getAndDecrement()
if (isCompleted()) {
Timber.d("isCompleted")
eventListener.onComplete()
}
} }
ITaskManager.SUB_STATE_RUNNING -> { ITaskManager.SUB_STATE_RUNNING -> {
val len = msg.obj as Long
progress += len
task.taskState.curProgress = progress
if (System.currentTimeMillis() - lastUpdateTime > 1000) {
eventListener.onProgress(progress)
}
} }
} }
false false
@ -90,39 +125,49 @@ internal class HttpDGTaskManager(val task: HttpDGroupTask) : ITaskManager, IBloc
} }
override fun stop() { override fun stop() {
scope.launch(Dispatchers.IO) {
task.subTaskList.forEach {
it.stop(TaskSchedulerType.TYPE_DEFAULT)
}
}
} }
override fun cancel() { override fun cancel() {
scope.launch(Dispatchers.IO) {
task.subTaskList.forEach {
it.cancel(TaskSchedulerType.TYPE_DEFAULT)
}
task.subTaskList.clear()
}
} }
override fun isCompleted(): Boolean { override fun isCompleted(): Boolean {
TODO("Not yet implemented") return completedNum.get() == task.subTaskList.size
} }
override fun getCurrentProgress(): Long { override fun getCurrentProgress(): Long {
TODO("Not yet implemented") return task.currentProgress
} }
override fun isStopped(): Boolean { override fun isStopped(): Boolean {
TODO("Not yet implemented") return stoppedNum.get() + canceledNum.get() + failedNum.get() + completedNum.get() == task.subTaskList.size
} }
override fun isCanceled(): Boolean { override fun isCanceled(): Boolean {
TODO("Not yet implemented") return canceledNum.get() == task.subTaskList.size
} }
override fun isRunning(): Boolean { override fun isRunning(): Boolean {
TODO("Not yet implemented") return scope.isActive
} }
override fun setBlockNum(blockNum: Int) { override fun setBlockNum(blockNum: Int) {
TODO("Not yet implemented") Timber.e("Group tasks do not support setting threads")
} }
override fun getHandler(): Handler = handler override fun getHandler(): Handler = handler
override fun hasFailedTask(): Boolean { override fun hasFailedTask(): Boolean {
TODO("Not yet implemented") return failedNum.get() != 0
} }
} }

@ -25,7 +25,7 @@ import com.arialyy.aria.core.task.TaskCachePool.removeTask
import com.arialyy.aria.core.task.TaskCachePool.updateState import com.arialyy.aria.core.task.TaskCachePool.updateState
import com.arialyy.aria.core.task.TaskState import com.arialyy.aria.core.task.TaskState
import com.arialyy.aria.http.HttpTaskOption import com.arialyy.aria.http.HttpTaskOption
import com.arialyy.aria.http.download.HttpDOptionAdapter import com.arialyy.aria.http.download.HttpDOptionDelegate
import com.arialyy.aria.util.BlockUtil import com.arialyy.aria.util.BlockUtil
import timber.log.Timber import timber.log.Timber
@ -76,6 +76,7 @@ internal class HttpSubBlockManager(private val task: ITask, private val groupHan
task.taskState.speed = 0 task.taskState.speed = 0
saveData(IEntity.STATE_COMPLETE) saveData(IEntity.STATE_COMPLETE)
} }
quitLooper()
} }
ITaskManager.STATE_RUNNING -> { ITaskManager.STATE_RUNNING -> {
updateProgress(msg.obj as Long) updateProgress(msg.obj as Long)
@ -126,7 +127,7 @@ internal class HttpSubBlockManager(private val task: ITask, private val groupHan
handler = Handler(looper, callback) handler = Handler(looper, callback)
// Synchronized sequential execution of all block // Synchronized sequential execution of all block
task.getTaskOption(HttpTaskOption::class.java) task.getTaskOption(HttpTaskOption::class.java)
.getOptionAdapter(HttpDOptionAdapter::class.java).threadList.forEach { tt -> .getOptionDelegate(HttpDOptionDelegate::class.java).threadList.forEach { tt ->
if (isBreak) { if (isBreak) {
Timber.d("task stopped") Timber.d("task stopped")
return return

@ -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);
}
}
}

@ -13,13 +13,11 @@
* 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.command; package com.arialyy.aria.core.command
import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.DuaContext
import com.arialyy.aria.core.inf.IEntity; import kotlinx.coroutines.Dispatchers
import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import kotlinx.coroutines.launch
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.NetUtils;
/** /**
* Created by AriaL on 2017/6/13. * Created by AriaL on 2017/6/13.
@ -28,19 +26,13 @@ import com.arialyy.aria.util.NetUtils;
* 2.如果队列执行队列已经满了则将所有任务添加到等待队列中 * 2.如果队列执行队列已经满了则将所有任务添加到等待队列中
* 3.如果队列中只有等待状态的任务如果执行队列没有满则会启动等待状态的任务如果执行队列已经满了则会将所有等待状态的任务加载到缓存队列中 * 3.如果队列中只有等待状态的任务如果执行队列没有满则会启动等待状态的任务如果执行队列已经满了则会将所有等待状态的任务加载到缓存队列中
* 4.恢复下载的任务规则是停止时间越晚的任务启动越早按照DESC来进行排序 * 4.恢复下载的任务规则是停止时间越晚的任务启动越早按照DESC来进行排序
*/ **/
final class ResumeAllCmd<T extends AbsTaskWrapper> extends AbsNormalCmd<T> { class ResumeAllCmd() : ICmd {
ResumeAllCmd(T entity, int taskType) { override fun executeCmd(): CmdResp {
super(entity, taskType); DuaContext.duaScope.launch(Dispatchers.IO) {
}
@Override public void executeCmd() {
if (!NetUtils.isConnected(AriaConfig.getInstance().getAPP())) {
ALog.w(TAG, "恢复任务失败,网络未连接");
return;
} }
new Thread(new ResumeThread(isDownloadCmd, return CmdResp(CmdResp.CODE_COMPLETE)
String.format("state!=%s", IEntity.STATE_COMPLETE))).start();
} }
} }

@ -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,12 +1,11 @@
package com.arialyy.aria.core.command; package com.arialyy.aria.core.command;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
/** /**
* Created by AriaL on 2017/6/13. * Created by AriaL on 2017/6/13.
* 停止所有任务的命令并清空所有等待队列 * 停止所有任务的命令并清空所有等待队列
*/ */
final class StopAllCmd<T extends AbsTaskWrapper> extends AbsNormalCmd<T> { final class StopAllCmd extends AbsCmd {
StopAllCmd(T entity, int taskType) { StopAllCmd(T entity, int taskType) {
super(entity, taskType); super(entity, taskType);
} }

@ -13,13 +13,11 @@
* 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.loader; package com.arialyy.aria.core.event
/** /**
* 加载器部件 * @Author laoyuyu
*/ * @Description
public interface ILoaderComponent { * @Date 8:26 PM 2023/3/21
**/
void accept(ILoaderVisitor visitor); class ResumeAllEvent
}

@ -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子任务失败次数大于等于配置的重试次数才能认为子任务停止
* 2stopNum + failNum + completeNum + cacheNum == subSize则认为组合任务停止
* 3failNum == 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所有的子任务已经停止则认为组合任务停止
* 2completeNum + 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;
}
}

@ -20,19 +20,12 @@ import com.arialyy.annotations.TaskEnum
/** /**
* @Author laoyuyu * @Author laoyuyu
* @Description * @Description
* @Date 10:44 AM 2023/1/20 * @Date 8:09 PM 2023/3/21
**/ **/
interface IComponentLoader { interface IComponentLoader {
companion object { companion object {
val proxyMethods = arrayOf("download", "upload") val proxyMethods = arrayOf("download", "upload")
} }
// fun upload()
fun <T : IDownloader> download(): T
// fun <T : IUploader> upload(): T
fun getTaskEnum(): TaskEnum fun getTaskEnum(): TaskEnum
} }

@ -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);
} //}

@ -16,6 +16,7 @@
package com.arialyy.aria.core.provider package com.arialyy.aria.core.provider
import android.content.Context import android.content.Context
import androidx.annotation.Keep
import androidx.startup.Initializer import androidx.startup.Initializer
import com.arialyy.aria.core.DuaContext import com.arialyy.aria.core.DuaContext
import com.arialyy.aria.core.inf.IComponentInit import com.arialyy.aria.core.inf.IComponentInit
@ -24,6 +25,7 @@ import timber.log.Timber
import timber.log.Timber.DebugTree import timber.log.Timber.DebugTree
import java.util.ServiceLoader import java.util.ServiceLoader
@Keep
class DuaStartupProvider : Initializer<Unit> { class DuaStartupProvider : Initializer<Unit> {
override fun create(context: Context) { override fun create(context: Context) {

@ -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;
}
}

@ -15,6 +15,8 @@
*/ */
package com.arialyy.aria.util package com.arialyy.aria.util
import android.text.TextUtils
import timber.log.Timber
import java.security.MessageDigest import java.security.MessageDigest
/** /**
@ -23,6 +25,27 @@ import java.security.MessageDigest
* @Date 21:38 2023/3/11 * @Date 21:38 2023/3/11
**/ **/
object DuaUtil { object DuaUtil {
/**
* 检测url是否合法
*
* @return `true` 合法`false` 非法
*/
fun checkUrl(url: String): Boolean {
if (TextUtils.isEmpty(url)) {
Timber.e("url不能为null")
return false
}
if (!url.startsWith("http") && !url.startsWith("ftp") && !url.startsWith("sftp")) {
Timber.e("url【$url】错误")
return false
}
val index = url.indexOf("://")
if (index == -1) {
Timber.e("url【$url】不合法")
}
return true
}
fun getMD5Hash(str: String): String { fun getMD5Hash(str: String): String {
val md = MessageDigest.getInstance("MD5") val md = MessageDigest.getInstance("MD5")
val messageDigest = md.digest(str.toByteArray()) val messageDigest = md.digest(str.toByteArray())
@ -33,5 +56,4 @@ object DuaUtil {
} }
return hashText return hashText
} }
} }

@ -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);
}

@ -35,7 +35,7 @@ import com.arialyy.aria.core.common.AbsEntity;
import com.arialyy.aria.core.common.HttpOption; import com.arialyy.aria.core.common.HttpOption;
import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.listener.ISchedulers; import com.arialyy.aria.core.listener.ISchedulers;
import com.arialyy.aria.core.processor.IHttpFileLenAdapter; import com.arialyy.aria.http.IHttpFileLenAdapter;
import com.arialyy.aria.core.task.SingleDownloadTask; import com.arialyy.aria.core.task.SingleDownloadTask;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.CommonUtil;

@ -25,7 +25,7 @@ import com.arialyy.aria.core.common.AbsEntity;
import com.arialyy.aria.core.common.HttpOption; import com.arialyy.aria.core.common.HttpOption;
import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.download.DownloadGroupEntity;
import com.arialyy.aria.core.processor.IHttpFileLenAdapter; import com.arialyy.aria.http.IHttpFileLenAdapter;
import com.arialyy.aria.core.task.DownloadGroupTask; import com.arialyy.aria.core.task.DownloadGroupTask;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import com.arialyy.frame.util.show.T; import com.arialyy.frame.util.show.T;

Loading…
Cancel
Save