Merge remote-tracking branch 'origin/v4' into v4

# Conflicts:
#	PublicComponent/src/main/java/com/arialyy/aria/util/FileUri.kt
v4
laoyuyu 2 years ago
commit f0c63d72b3
  1. 33
      Http/src/main/java/com/arialyy/aria/http/download/HttpDEventListener.kt
  2. 6
      Http/src/main/java/com/arialyy/aria/http/download/HttpDStartController.kt
  3. 18
      Http/src/main/java/com/arialyy/aria/http/download/HttpDTaskUtil.kt
  4. 249
      Http/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java
  5. 9
      Http/src/main/java/com/arialyy/aria/http/download/TimerInterceptor.kt
  6. 4
      M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8Listener.java
  7. 2
      PublicComponent/src/main/java/com/arialyy/aria/core/inf/ITaskOption.java
  8. 48
      PublicComponent/src/main/java/com/arialyy/aria/core/listener/AbsEventListener.java
  9. 2
      PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseDListener.java
  10. 2
      PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseUListener.java
  11. 2
      PublicComponent/src/main/java/com/arialyy/aria/core/listener/DownloadGroupListener.java
  12. 3
      PublicComponent/src/main/java/com/arialyy/aria/core/listener/IEventListener.java
  13. 265
      PublicComponent/src/main/java/com/arialyy/aria/core/manager/ThreadTaskManager.java
  14. 2
      PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsTask.java
  15. 5
      PublicComponent/src/main/java/com/arialyy/aria/core/task/DBlockManager.kt
  16. 1
      PublicComponent/src/main/java/com/arialyy/aria/core/task/DownloadTask.java
  17. 4
      PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask2.kt
  18. 82
      PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTaskManager2.kt
  19. 6
      PublicComponent/src/main/java/com/arialyy/aria/orm/dao/DEntityDao.kt
  20. 17
      PublicComponent/src/main/java/com/arialyy/aria/util/FileUri.kt
  21. 18
      PublicComponent/src/main/java/com/arialyy/aria/util/FileUtils.kt
  22. 12
      Queue/src/main/java/com/arialyy/aria/queue/AbsTaskQueue.java

@ -0,0 +1,33 @@
/*
* 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.http.download
import com.arialyy.aria.core.listener.AbsEventListener
import com.arialyy.aria.core.task.DownloadTask
import com.arialyy.aria.util.FileUtils
class HttpDEventListener(task: DownloadTask) : AbsEventListener(task) {
override fun handleCancel() {
}
override fun handleComplete() {
}
}

@ -123,8 +123,8 @@ class HttpDStartController(target: Any, val url: String) : HttpBaseController(ta
*/ */
private suspend fun findDEntityBySavePath(option: HttpDTaskOption): DEntity { private suspend fun findDEntityBySavePath(option: HttpDTaskOption): DEntity {
val savePath = option.savePathUri val savePath = option.savePathUri
val dao = DuaContext.getServiceManager().getDbService().getDuaDb()?.getDEntityDao() val dao = DuaContext.getServiceManager().getDbService().getDuaDb().getDEntityDao()
val de = dao?.getDEntityBySavePath(savePath.toString()) val de = dao.getDEntityBySavePath(savePath.toString())
if (de != null) { if (de != null) {
return de return de
} }
@ -132,7 +132,7 @@ class HttpDStartController(target: Any, val url: String) : HttpBaseController(ta
sourceUrl = option.sourUrl!!, sourceUrl = option.sourUrl!!,
savePath = savePath!!, savePath = savePath!!,
) )
dao?.insert(newDe) dao.insert(newDe)
return newDe return newDe
} }

@ -20,7 +20,9 @@ import com.arialyy.aria.core.DuaContext
import com.arialyy.aria.core.inf.IBlockManager import com.arialyy.aria.core.inf.IBlockManager
import com.arialyy.aria.core.task.AbsTaskUtil import com.arialyy.aria.core.task.AbsTaskUtil
import com.arialyy.aria.core.task.BlockManager import com.arialyy.aria.core.task.BlockManager
import com.arialyy.aria.core.task.DownloadTask
import com.arialyy.aria.core.task.TaskResp import com.arialyy.aria.core.task.TaskResp
import com.arialyy.aria.core.task.ThreadTaskManager2
import com.arialyy.aria.exception.AriaException import com.arialyy.aria.exception.AriaException
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -33,6 +35,12 @@ import kotlinx.coroutines.launch
internal class HttpDTaskUtil : AbsTaskUtil() { internal class HttpDTaskUtil : AbsTaskUtil() {
private var blockManager: BlockManager? = null private var blockManager: BlockManager? = null
init {
getTask().getTaskOption(HttpDTaskOption::class.java).eventListener =
HttpDEventListener(getTask() as DownloadTask)
}
override fun getBlockManager(): IBlockManager { override fun getBlockManager(): IBlockManager {
if (blockManager == null) { if (blockManager == null) {
blockManager = BlockManager(getTask()) blockManager = BlockManager(getTask())
@ -45,11 +53,15 @@ internal class HttpDTaskUtil : AbsTaskUtil() {
} }
override fun cancel() { override fun cancel() {
TODO("Not yet implemented") DuaContext.duaScope.launch(Dispatchers.IO) {
ThreadTaskManager2.stopThreadTask(getTask().taskId, true)
}
} }
override fun stop() { override fun stop() {
blockManager?.stop() DuaContext.duaScope.launch(Dispatchers.IO) {
ThreadTaskManager2.stopThreadTask(getTask().taskId)
}
} }
override fun start() { override fun start() {
@ -67,7 +79,7 @@ internal class HttpDTaskUtil : AbsTaskUtil() {
addCoreInterceptor(HttpBlockThreadInterceptor()) addCoreInterceptor(HttpBlockThreadInterceptor())
val resp = interceptor() val resp = interceptor()
if (resp == null || resp.code != TaskResp.CODE_SUCCESS) { if (resp == null || resp.code != TaskResp.CODE_SUCCESS) {
getTask().getTaskOption(HttpDTaskOption::class.java).taskListener.onFail( getTask().getTaskOption(HttpDTaskOption::class.java).eventListener.onFail(
false, false,
AriaException("start task fail, task interrupt, code: ${resp?.code ?: TaskResp.CODE_INTERRUPT}") AriaException("start task fail, task interrupt, code: ${resp?.code ?: TaskResp.CODE_INTERRUPT}")
) )

@ -1,249 +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.http.download;
import com.arialyy.aria.core.common.RequestEnum;
import com.arialyy.aria.core.task.AbsThreadTaskAdapter;
import com.arialyy.aria.core.task.ThreadConfig;
import com.arialyy.aria.exception.AriaHTTPException;
import com.arialyy.aria.http.ConnectionHelp;
import com.arialyy.aria.http.HttpOption;
import com.arialyy.aria.http.request.IRequest;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.BufferedRandomAccessFile;
import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.util.Map;
import java.util.Set;
import timber.log.Timber;
/**
* Created by lyy on 2017/1/18. 下载线程
*/
final class HttpDThreadTaskAdapter extends AbsThreadTaskAdapter {
HttpDThreadTaskAdapter(ThreadConfig threadConfig) {
super(threadConfig);
}
private HttpDTaskOption getTaskOption() {
return (HttpDTaskOption) getThreadConfig().getOption();
}
@Override protected void handlerThreadTask() {
HttpURLConnection conn = null;
BufferedInputStream is = null;
BufferedRandomAccessFile file = null;
try {
HttpDTaskOption taskOption = getTaskOption();
HttpOption option = taskOption.getHttpOption();
conn = IRequest.Companion.getRequest(option).getDConnection(taskOption.getSourUrl(), option);
if (!taskOption.isSupportResume()) {
Timber.w("this task not support resume, url: %s", taskOption.getSourUrl());
}else {
conn.setRequestProperty("Range",
String.format("bytes=%s-%s", getThreadConfig().getBlockRecord().getStartLocation(),
(getThreadConfig().getBlockRecord().getEndLocation() - 1)));
}
ConnectionHelp.setConnectParam(mTaskOption, conn);
conn.setConnectTimeout(getTaskConfig().getConnectTimeOut());
conn.setReadTimeout(getTaskConfig().getIOTimeOut()); //设置读取流的等待时间,必须设置该参数
if (mTaskOption.isChunked()) {
conn.setDoInput(true);
conn.setChunkedStreamingMode(0);
}
conn.connect();
is = new BufferedInputStream(ConnectionHelp.convertInputStream(conn));
if (mTaskOption.isChunked()) {
readChunked(is);
} else if (getThreadConfig().isBlock) {
readDynamicFile(is);
} else {
//创建可设置位置的文件
file =
new BufferedRandomAccessFile(getThreadConfig().tempFile, "rwd",
getTaskConfig().getBuffSize());
//设置每条线程写入文件的位置
if (getThreadRecord().startLocation > 0) {
file.seek(getThreadRecord().startLocation);
}
readNormal(is, file);
handleComplete();
}
} catch (MalformedURLException e) {
fail(new AriaHTTPException(String.format("任务【%s】下载失败,filePath: %s, url: %s", getFileName(),
getEntity().getFilePath(), getEntity().getUrl()), e), false);
} catch (IOException e) {
fail(new AriaHTTPException(String.format("任务【%s】下载失败,filePath: %s, url: %s", getFileName(),
getEntity().getFilePath(), getEntity().getUrl()), e), true);
} catch (ArrayIndexOutOfBoundsException e) {
fail(new AriaHTTPException(String.format("任务【%s】下载失败,filePath: %s, url: %s", getFileName(),
getEntity().getFilePath(), getEntity().getUrl()), e), false);
} catch (Exception e) {
fail(new AriaHTTPException(String.format("任务【%s】下载失败,filePath: %s, url: %s", getFileName(),
getEntity().getFilePath(), getEntity().getUrl()), e), false);
} finally {
try {
if (file != null) {
file.close();
}
if (is != null) {
is.close();
}
if (conn != null) {
conn.getInputStream().close();
conn.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 读取chunked数据
*/
private void readChunked(InputStream is) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(getThreadConfig().tempFile, true);
byte[] buffer = new byte[getTaskConfig().getBuffSize()];
int len;
while (getThreadTask().isLive() && (len = is.read(buffer)) != -1) {
if (getThreadTask().isBreak()) {
break;
}
if (mSpeedBandUtil != null) {
mSpeedBandUtil.limitNextBytes(len);
}
fos.write(buffer, 0, len);
progress(len);
}
handleComplete();
} catch (IOException e) {
fail(new AriaHTTPException(
String.format("文件下载失败,savePath: %s, url: %s", getEntity().getFilePath(),
getThreadConfig().url), e), true);
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 动态长度文件读取方式
*/
private void readDynamicFile(InputStream is) {
FileOutputStream fos = null;
FileChannel foc = null;
ReadableByteChannel fic = null;
try {
int len;
fos = new FileOutputStream(getThreadConfig().tempFile, true);
foc = fos.getChannel();
fic = Channels.newChannel(is);
ByteBuffer bf = ByteBuffer.allocate(getTaskConfig().getBuffSize());
//如果要通过 Future 的 cancel 方法取消正在运行的任务,那么该任务必定是可以 对线程中断做出响应 的任务。
while (getThreadTask().isLive() && (len = fic.read(bf)) != -1) {
if (getThreadTask().isBreak()) {
break;
}
if (mSpeedBandUtil != null) {
mSpeedBandUtil.limitNextBytes(len);
}
if (getRangeProgress() + len >= getThreadRecord().endLocation) {
len = (int) (getThreadRecord().endLocation - getRangeProgress());
bf.flip();
fos.write(bf.array(), 0, len);
bf.compact();
progress(len);
break;
} else {
bf.flip();
foc.write(bf);
bf.compact();
progress(len);
}
}
handleComplete();
} catch (IOException e) {
fail(new AriaHTTPException(
String.format("文件下载失败,savePath: %s, url: %s", getEntity().getFilePath(),
getThreadConfig().url), e), true);
} finally {
try {
if (fos != null) {
fos.flush();
fos.close();
}
if (foc != null) {
foc.close();
}
if (fic != null) {
fic.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 读取普通的文件流
*/
private void readNormal(InputStream is, BufferedRandomAccessFile file)
throws IOException {
byte[] buffer = new byte[getTaskConfig().getBuffSize()];
int len;
while (getThreadTask().isLive() && (len = is.read(buffer)) != -1) {
if (getThreadTask().isBreak()) {
break;
}
if (mSpeedBandUtil != null) {
mSpeedBandUtil.limitNextBytes(len);
}
file.write(buffer, 0, len);
progress(len);
}
}
@Override public void cancel() {
}
@Override public void stop() {
}
}

@ -15,9 +15,8 @@
*/ */
package com.arialyy.aria.http.download package com.arialyy.aria.http.download
import com.arialyy.aria.core.inf.IBlockManager
import com.arialyy.aria.core.inf.ITaskOption import com.arialyy.aria.core.inf.ITaskOption
import com.arialyy.aria.core.manager.ThreadTaskManager import com.arialyy.aria.core.task.ThreadTaskManager2
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
@ -61,12 +60,12 @@ open class TimerInterceptor : ITaskInterceptor {
|| blockManager.hasFailedBlock() || blockManager.hasFailedBlock()
|| !isRunning(chain.getTask()) || !isRunning(chain.getTask())
) { ) {
ThreadTaskManager.getInstance().removeTaskThread(chain.getTask().taskId) ThreadTaskManager2.stopThreadTask(chain.getTask().taskId)
closeTimer() closeTimer()
return return
} }
if (chain.getTask().taskState.curProgress >= 0) { if (chain.getTask().taskState.curProgress >= 0) {
chain.getTask().getTaskOption(ITaskOption::class.java).taskListener.onProgress( chain.getTask().getTaskOption(ITaskOption::class.java).eventListener.onProgress(
blockManager.currentProgress blockManager.currentProgress
) )
return return
@ -86,6 +85,6 @@ open class TimerInterceptor : ITaskInterceptor {
} }
@Synchronized fun isRunning(task: ITask): Boolean { @Synchronized fun isRunning(task: ITask): Boolean {
return ThreadTaskManager.getInstance().taskIsRunning(task.taskId) return ThreadTaskManager2.taskIsRunning(task.taskId)
} }
} }

@ -19,7 +19,7 @@ import android.os.Bundle;
import android.os.Message; import android.os.Message;
import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.core.inf.TaskSchedulerType; import com.arialyy.aria.core.inf.TaskSchedulerType;
import com.arialyy.aria.core.listener.BaseListener; import com.arialyy.aria.core.listener.AbsEventListener;
import com.arialyy.aria.core.listener.IDLoadListener; import com.arialyy.aria.core.listener.IDLoadListener;
import com.arialyy.aria.core.listener.ISchedulers; import com.arialyy.aria.core.listener.ISchedulers;
import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.aria.core.task.DownloadTask;
@ -29,7 +29,7 @@ import com.arialyy.aria.util.DeleteM3u8Record;
/** /**
* 下载监听类 * 下载监听类
*/ */
public final class M3U8Listener extends BaseListener implements IDLoadListener { public final class M3U8Listener extends AbsEventListener implements IDLoadListener {
@Override @Override
public void onPostPre(long fileSize) { public void onPostPre(long fileSize) {

@ -23,6 +23,6 @@ import com.arialyy.aria.core.listener.IEventListener;
*/ */
public abstract class ITaskOption { public abstract class ITaskOption {
public IEventListener taskListener; public IEventListener eventListener;
public int threadNum; public int threadNum;
} }

@ -20,37 +20,41 @@ import com.arialyy.aria.core.AriaConfig;
import com.arialyy.aria.core.DuaContext; import com.arialyy.aria.core.DuaContext;
import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.core.inf.TaskSchedulerType; import com.arialyy.aria.core.inf.TaskSchedulerType;
import com.arialyy.aria.core.task.AbsTask;
import com.arialyy.aria.core.task.ITask; import com.arialyy.aria.core.task.ITask;
import com.arialyy.aria.core.task.TaskCachePool;
import com.arialyy.aria.core.task.TaskState; import com.arialyy.aria.core.task.TaskState;
import com.arialyy.aria.core.wrapper.ITaskWrapper;
import com.arialyy.aria.exception.AriaException; import com.arialyy.aria.exception.AriaException;
import com.arialyy.aria.core.task.TaskCachePool;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.ErrorHelp; import com.arialyy.aria.util.ErrorHelp;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import timber.log.Timber; import timber.log.Timber;
public abstract class BaseListener implements IEventListener { public abstract class AbsEventListener implements IEventListener {
static final int RUN_SAVE_INTERVAL = 5 * 1000; //5s保存一次下载中的进度 static final int RUN_SAVE_INTERVAL = 5 * 1000; //5s保存一次下载中的进度
protected Handler outHandler; protected Handler outHandler;
private long mLastLen; //上一次发送长度 private long mLastLen; //上一次发送长度
private boolean isFirst = true; private boolean isFirst = true;
private ITask mTask; private final ITask mTask;
long mLastSaveTime; long mLastSaveTime;
private long mUpdateInterval; private final long mUpdateInterval;
/**
* 处理任务取消
*/
protected abstract void handleCancel();
protected abstract void handleComplete();
@Override public IEventListener setParams(ITask task) { protected AbsEventListener(ITask task) {
this.outHandler = DuaContext.INSTANCE.getServiceManager().getSchedulerHandler(); this.outHandler = DuaContext.INSTANCE.getServiceManager().getSchedulerHandler();
mTask = new WeakReference<>(task).get(); mTask = new WeakReference<>(task).get();
mUpdateInterval = AriaConfig.getInstance().getCConfig().getUpdateInterval(); mUpdateInterval = AriaConfig.getInstance().getCConfig().getUpdateInterval();
mLastLen = task.getTaskState().getCurProgress(); mLastLen = task.getTaskState().getCurProgress();
mLastSaveTime = System.currentTimeMillis(); mLastSaveTime = System.currentTimeMillis();
return this;
} }
protected <TASK extends AbsTask> TASK getTask(Class<TASK> clazz) { protected ITask getTask() {
return (TASK) mTask; return mTask;
} }
@Override public void onPre() { @Override public void onPre() {
@ -93,7 +97,7 @@ public abstract class BaseListener implements IEventListener {
} }
@Override public void onComplete() { @Override public void onComplete() {
saveData(IEntity.STATE_COMPLETE, mEntity.getFileSize()); saveData(IEntity.STATE_COMPLETE, mTask.getTaskState().getFileSize());
handleSpeed(0); handleSpeed(0);
sendInState2Target(ISchedulers.COMPLETE); sendInState2Target(ISchedulers.COMPLETE);
} }
@ -128,30 +132,8 @@ public abstract class BaseListener implements IEventListener {
speed = speed * 1000 / mUpdateInterval; speed = speed * 1000 / mUpdateInterval;
} }
mTask.getTaskState().setSpeed(speed); mTask.getTaskState().setSpeed(speed);
int taskType = mTaskWrapper.getRequestType();
if (taskType != ITaskWrapper.M3U8_VOD && taskType != ITaskWrapper.M3U8_LIVE) {
mEntity.setPercent((int) (mEntity.getFileSize() <= 0 ? 0
: mEntity.getCurrentProgress() * 100 / mEntity.getFileSize()));
}
} }
/**
* 处理任务完成后的情况
*/
private void handleComplete() {
mEntity.setComplete(true);
mEntity.setCompleteTime(System.currentTimeMillis());
mEntity.setCurrentProgress(mEntity.getFileSize());
mEntity.setPercent(100);
handleSpeed(0);
}
/**
* 处理任务取消
*/
protected abstract void handleCancel();
/** /**
* 将任务状态发送给下载器 * 将任务状态发送给下载器
* *

@ -24,7 +24,7 @@ import com.arialyy.aria.util.DeleteDRecord;
/** /**
* 下载监听类 * 下载监听类
*/ */
public class BaseDListener extends BaseListener implements IDLoadListener { public class BaseDListener extends AbsEventListener implements IDLoadListener {
@Override @Override
public void onPostPre(long fileSize) { public void onPostPre(long fileSize) {

@ -23,7 +23,7 @@ import com.arialyy.aria.util.DeleteURecord;
/** /**
* 下载监听类 * 下载监听类
*/ */
public class BaseUListener extends BaseListener implements IUploadListener { public class BaseUListener extends AbsEventListener implements IUploadListener {
@Override protected void handleCancel() { @Override protected void handleCancel() {
int sType = getTask(UploadTask.class).getSchedulerType(); int sType = getTask(UploadTask.class).getSchedulerType();

@ -34,7 +34,7 @@ import static com.arialyy.aria.core.task.AbsTask.ERROR_INFO_KEY;
/** /**
* Created by Aria.Lao on 2017/7/20. 任务组下载事件 * Created by Aria.Lao on 2017/7/20. 任务组下载事件
*/ */
public class DownloadGroupListener extends BaseListener implements IDGroupListener { public class DownloadGroupListener extends AbsEventListener implements IDGroupListener {
private GroupSendParams<DownloadGroupTask, DownloadEntity> mSeedEntity; private GroupSendParams<DownloadGroupTask, DownloadEntity> mSeedEntity;
@Override public IEventListener setParams(AbsTask task, Handler outHandler) { @Override public IEventListener setParams(AbsTask task, Handler outHandler) {

@ -15,7 +15,6 @@
*/ */
package com.arialyy.aria.core.listener; package com.arialyy.aria.core.listener;
import com.arialyy.aria.core.task.ITask;
import com.arialyy.aria.exception.AriaException; import com.arialyy.aria.exception.AriaException;
/** /**
@ -24,8 +23,6 @@ import com.arialyy.aria.exception.AriaException;
*/ */
public interface IEventListener { public interface IEventListener {
IEventListener setParams(ITask task);
/** /**
* 预处理有时有些地址链接比较慢这时可以先在这个地方出来一些界面上的UI如按钮的状态 * 预处理有时有些地址链接比较慢这时可以先在这个地方出来一些界面上的UI如按钮的状态
*/ */

@ -1,265 +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.manager;
import android.text.TextUtils;
import com.arialyy.aria.core.task.ITask;
import com.arialyy.aria.core.task.IThreadTask;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/**
* 线程任务管理器
*/
@Deprecated
public class ThreadTaskManager1 {
private final String TAG = CommonUtil.getClassName(this);
private static volatile ThreadTaskManager INSTANCE = null;
private static final int CORE_POOL_NUM = 20;
private static final ReentrantLock LOCK = new ReentrantLock();
private final ThreadPoolExecutor mExePool;
private final Map<Integer, Set<FutureContainer>> mThreadTasks = new ConcurrentHashMap<>();
public static synchronized ThreadTaskManager getInstance() {
if (INSTANCE == null) {
INSTANCE = new ThreadTaskManager();
}
return INSTANCE;
}
private ThreadTaskManager() {
mExePool = new ThreadPoolExecutor(CORE_POOL_NUM, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
mExePool.allowsCoreThreadTimeOut();
}
/**
* 删除所有线程任务
*/
public void removeAllThreadTask() {
if (mThreadTasks.isEmpty()) {
return;
}
try {
LOCK.tryLock(2, TimeUnit.SECONDS);
for (Set<FutureContainer> threads : mThreadTasks.values()) {
for (FutureContainer container : threads) {
if (container.future.isDone() || container.future.isCancelled()) {
continue;
}
container.threadTask.destroy();
}
threads.clear();
}
mThreadTasks.clear();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
LOCK.unlock();
}
}
/**
* 启动线程任务
*
* @param taskId {@link ITask#getTaskId()}
* @param threadTask 线程任务{@link IThreadTask}
*/
public void startThread(Integer taskId, IThreadTask threadTask) {
try {
LOCK.tryLock(2, TimeUnit.SECONDS);
if (mExePool.isShutdown()) {
ALog.e(TAG, "线程池已经关闭");
return;
}
Set<FutureContainer> temp = mThreadTasks.get(taskId);
if (temp == null) {
temp = new HashSet<>();
mThreadTasks.put(taskId, temp);
}
FutureContainer container = new FutureContainer();
container.threadTask = threadTask;
container.future = mExePool.submit(threadTask);
temp.add(container);
} catch (Exception e) {
e.printStackTrace();
} finally {
LOCK.unlock();
}
}
/**
* 任务是否在执行
*
* @return {@code true} 任务正在运行
*/
public boolean taskIsRunning(Integer taskId) {
return mThreadTasks.get(taskId) != null;
}
/**
* 停止任务的所有线程
*/
public void removeTaskThread(Integer taskId) {
try {
LOCK.tryLock(2, TimeUnit.SECONDS);
if (mExePool.isShutdown()) {
ALog.e(TAG, "线程池已经关闭");
return;
}
Set<FutureContainer> temp = mThreadTasks.get(taskId);
if (temp != null && temp.size() > 0) {
for (FutureContainer container : temp) {
if (container.future.isDone() || container.future.isCancelled()) {
continue;
}
container.threadTask.destroy();
}
temp.clear();
mThreadTasks.remove(taskId);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
LOCK.unlock();
}
}
/**
* 根据线程名删除任务的中的线程
*
* @param threadName 线程名
* @return true 删除线程成功false 删除线程失败
*/
public boolean removeSingleTaskThread(Integer taskId, String threadName) {
try {
LOCK.tryLock(2, TimeUnit.SECONDS);
if (mExePool.isShutdown()) {
ALog.e(TAG, "线程池已经关闭");
return false;
}
if (TextUtils.isEmpty(threadName)) {
ALog.e(TAG, "线程名为空");
return false;
}
Set<FutureContainer> temp = mThreadTasks.get(taskId);
if (temp != null && temp.size() > 0) {
FutureContainer tempC = null;
for (FutureContainer container : temp) {
if (container.threadTask.getThreadName().equals(threadName)) {
tempC = container;
break;
}
}
if (tempC != null) {
tempC.threadTask.destroy();
temp.remove(tempC);
return true;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
LOCK.unlock();
}
return false;
}
/**
* 删除单个线程任务
*
* @param task 线程任务
*/
public boolean removeSingleTaskThread(Integer taskId, IThreadTask task) {
try {
LOCK.tryLock(2, TimeUnit.SECONDS);
if (mExePool.isShutdown()) {
ALog.e(TAG, "线程池已经关闭");
return false;
}
if (task == null) {
ALog.e(TAG, "线程任务为空");
return false;
}
Set<FutureContainer> temp = mThreadTasks.get(taskId);
if (temp != null && temp.size() > 0) {
FutureContainer tempC = null;
for (FutureContainer container : temp) {
if (container.threadTask == task) {
tempC = container;
break;
}
}
if (tempC != null) {
task.destroy();
temp.remove(tempC);
return true;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
LOCK.unlock();
}
return false;
}
/**
* 重试线程任务
*
* @param task 线程任务
*/
public void retryThread(IThreadTask task) {
try {
LOCK.tryLock(2, TimeUnit.SECONDS);
if (mExePool.isShutdown()) {
ALog.e(TAG, "线程池已经关闭");
return;
}
try {
if (task == null || task.isDestroy()) {
ALog.e(TAG, "线程为空或线程已经中断");
return;
}
} catch (Exception e) {
ALog.e(TAG, "", e);
return;
}
mExePool.submit(task);
} catch (Exception e) {
e.printStackTrace();
} finally {
LOCK.unlock();
}
}
private static class FutureContainer {
Future future;
IThreadTask threadTask;
}
}

@ -45,7 +45,7 @@ public abstract class AbsTask implements ITask {
mUtil = util; mUtil = util;
taskId = TaskStatePool.INSTANCE.buildTaskId$PublicComponent_debug(); taskId = TaskStatePool.INSTANCE.buildTaskId$PublicComponent_debug();
TaskStatePool.INSTANCE.putTaskState(getTaskId(), mTaskState); TaskStatePool.INSTANCE.putTaskState(getTaskId(), mTaskState);
util.init(this, taskOption.taskListener); util.init(this, taskOption.eventListener);
} }
@Override public void setState(int state) { @Override public void setState(int state) {

@ -30,7 +30,6 @@ import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.ThreadFactory
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 import java.util.concurrent.atomic.AtomicInteger
@ -55,19 +54,21 @@ class BlockManager(task: ITask) : IBlockManager {
private lateinit var handler: Handler private lateinit var handler: Handler
private var blockNum: Int = 1 private var blockNum: Int = 1
private var eventListener: IEventListener = private var eventListener: IEventListener =
task.getTaskOption(ITaskOption::class.java).taskListener task.getTaskOption(ITaskOption::class.java).eventListener
private val callback = Callback { msg -> private val callback = Callback { msg ->
when (msg.what) { when (msg.what) {
IBlockManager.STATE_STOP -> { IBlockManager.STATE_STOP -> {
stoppedNum.getAndIncrement() stoppedNum.getAndIncrement()
if (isStopped) { if (isStopped) {
eventListener.onStop(currentProgress)
quitLooper() quitLooper()
} }
} }
IBlockManager.STATE_CANCEL -> { IBlockManager.STATE_CANCEL -> {
canceledNum.getAndIncrement() canceledNum.getAndIncrement()
if (isCanceled) { if (isCanceled) {
eventListener.onCancel()
quitLooper() quitLooper()
} }
} }

@ -29,7 +29,6 @@ public class DownloadTask extends AbsTask {
public DownloadTask(DTaskOption taskOption, ITaskUtil util) { public DownloadTask(DTaskOption taskOption, ITaskUtil util) {
super(taskOption, util); super(taskOption, util);
taskOption.taskListener.setParams(this);
} }
public Uri getSavePath() { public Uri getSavePath() {

@ -51,13 +51,13 @@ class ThreadTask2(
} }
override fun cancel() { override fun cancel() {
adapter.cancel() adapter.breakTask()
isCanceled = true isCanceled = true
handler.obtainMessage(IBlockManager.STATE_CANCEL) handler.obtainMessage(IBlockManager.STATE_CANCEL)
} }
override fun stop() { override fun stop() {
adapter.stop() adapter.breakTask()
isStopped = true isStopped = true
handler.obtainMessage(IBlockManager.STATE_STOP) handler.obtainMessage(IBlockManager.STATE_STOP)
} }

@ -0,0 +1,82 @@
/*
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.arialyy.aria.core.task
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.TimeUnit.SECONDS
import java.util.concurrent.locks.ReentrantLock
object ThreadTaskManager2 {
private val mThreadTasks: ConcurrentHashMap<Int, MutableSet<IThreadTask>> = ConcurrentHashMap()
private val LOCK = ReentrantLock()
/**
* 任务是否在执行
*
* @return `true` 任务正在运行
*/
fun taskIsRunning(taskId: Int): Boolean {
return mThreadTasks[taskId] != null
}
/**
* stop thread task
* @param isRemoveTask if true, remove task and block
*/
fun stopThreadTask(taskId: Int, isRemoveTask: Boolean = false) {
try {
LOCK.tryLock(2, SECONDS)
val threadTaskList: MutableSet<IThreadTask>? = mThreadTasks[taskId]
threadTaskList?.forEach {
if (isRemoveTask) {
it.cancel()
return@forEach
}
it.stop()
}
mThreadTasks.remove(taskId)
} catch (e: Exception) {
e.printStackTrace()
} finally {
LOCK.unlock()
}
}
/**
* 删除所有线程任务
*/
fun removeAllThreadTask() {
if (mThreadTasks.isEmpty()) {
return
}
try {
LOCK.tryLock(2, SECONDS)
for (threads in mThreadTasks.values) {
for (tt in threads) {
tt.stop()
}
threads.clear()
}
mThreadTasks.clear()
} catch (e: InterruptedException) {
e.printStackTrace()
} finally {
LOCK.unlock()
}
}
}

@ -36,13 +36,13 @@ interface DEntityDao {
suspend fun getDEntityList(): List<DEntity> suspend fun getDEntityList(): List<DEntity>
@Query("SELECT * FROM DEntity WHERE :savePath=savePath") @Query("SELECT * FROM DEntity WHERE :savePath=savePath")
suspend fun getDEntityBySavePath(savePath: String): DEntity suspend fun getDEntityBySavePath(savePath: String): DEntity?
@Query("SELECT * FROM DEntity WHERE :dId=dId") @Query("SELECT * FROM DEntity WHERE :dId=dId")
suspend fun getDEntityById(did: String): DEntity suspend fun getDEntityById(did: String): DEntity?
@Query("SELECT * FROM DEntity WHERE :sourceUrl=sourceUrl") @Query("SELECT * FROM DEntity WHERE :sourceUrl=sourceUrl")
suspend fun getDEntityBySource(sourceUrl: String): DEntity suspend fun getDEntityBySource(sourceUrl: String): DEntity?
@Insert @Insert
suspend fun insert(dEntity: DEntity) suspend fun insert(dEntity: DEntity)

@ -114,14 +114,14 @@ object FileUri {
return uri?.use { return uri?.use {
Timber.i( Timber.i(
"FileUri getPathByUri -> " + "FileUri getPathByUri -> " +
"Uri: " + uri + "Uri: " + uri +
", Authority: " + uri.authority + ", Authority: " + uri.authority +
", Fragment: " + uri.fragment + ", Fragment: " + uri.fragment +
", Port: " + uri.port + ", Port: " + uri.port +
", Query: " + uri.query + ", Query: " + uri.query +
", Scheme: " + uri.scheme + ", Scheme: " + uri.scheme +
", Host: " + uri.host + ", Host: " + uri.host +
", Segments: " + uri.pathSegments.toString() ", Segments: " + uri.pathSegments.toString()
) )
// 以 file:// 开头的使用第三方应用打开 (open with third-party applications starting with file://) // 以 file:// 开头的使用第三方应用打开 (open with third-party applications starting with file://)
@ -384,4 +384,5 @@ object FileUri {
private fun isHuaWeiUri(uri: Uri?): Boolean { private fun isHuaWeiUri(uri: Uri?): Boolean {
return "com.huawei.hidisk.fileprovider".equals(uri?.authority, true) return "com.huawei.hidisk.fileprovider".equals(uri?.authority, true)
} }
} }

@ -20,7 +20,9 @@ import android.database.Cursor
import android.net.Uri import android.net.Uri
import android.provider.OpenableColumns import android.provider.OpenableColumns
import com.arialyy.aria.core.DuaContext import com.arialyy.aria.core.DuaContext
import com.arialyy.aria.orm.entity.BlockRecord
import timber.log.Timber import timber.log.Timber
import java.io.File
import java.io.InputStream import java.io.InputStream
import java.util.Locale import java.util.Locale
@ -32,6 +34,22 @@ object FileUtils {
RegexOption.IGNORE_CASE RegexOption.IGNORE_CASE
) )
fun mergeBlock(blockRecordList: List<BlockRecord>, targetPath: Uri): Boolean {
if (blockRecordList.isEmpty()) {
Timber.e("block record list empty")
return false
}
if (!uriEffective(targetPath)) {
Timber.e("invalid uri: $targetPath")
return false
}
val fileList = arrayListOf<File>()
blockRecordList.forEach {
fileList.add(File(it.blockPath))
}
return FileUtil.mergeFile()
}
/** /**
* check if url is correct * check if url is correct
*/ */

@ -20,7 +20,7 @@ import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.core.inf.IPool; import com.arialyy.aria.core.inf.IPool;
import com.arialyy.aria.core.inf.ITaskQueue; import com.arialyy.aria.core.inf.ITaskQueue;
import com.arialyy.aria.core.inf.TaskSchedulerType; import com.arialyy.aria.core.inf.TaskSchedulerType;
import com.arialyy.aria.core.manager.ThreadTaskManager; import com.arialyy.aria.core.task.ThreadTaskManager2;
import com.arialyy.aria.core.task.ITask; import com.arialyy.aria.core.task.ITask;
import timber.log.Timber; import timber.log.Timber;
@ -72,8 +72,8 @@ public abstract class AbsTaskQueue<TASK extends ITask> implements ITaskQueue<TAS
return true; return true;
} }
TASK task = getExePool().getTask(taskId); TASK task = getExePool().getTask(taskId);
if (task == null && ThreadTaskManager.getInstance().taskIsRunning(taskId)) { if (task == null && ThreadTaskManager2.INSTANCE.taskIsRunning(taskId)) {
ThreadTaskManager.getInstance().removeTaskThread(taskId); ThreadTaskManager2.INSTANCE.stopThreadTask(taskId);
} }
return task != null && task.isRunning() && taskExists(taskId); return task != null && task.isRunning() && taskExists(taskId);
} }
@ -133,7 +133,7 @@ public abstract class AbsTaskQueue<TASK extends ITask> implements ITaskQueue<TAS
task.stop(TaskSchedulerType.TYPE_STOP_NOT_NEXT); task.stop(TaskSchedulerType.TYPE_STOP_NOT_NEXT);
} }
} }
ThreadTaskManager.getInstance().removeAllThreadTask(); ThreadTaskManager2.INSTANCE.removeAllThreadTask();
getCachePool().clear(); getCachePool().clear();
} }
@ -167,8 +167,8 @@ public abstract class AbsTaskQueue<TASK extends ITask> implements ITaskQueue<TAS
if (taskIsRunning(task.getTaskId())) { if (taskIsRunning(task.getTaskId())) {
getCachePool().removeTask(task.getTaskId()); getCachePool().removeTask(task.getTaskId());
getExePool().removeTask(task.getTaskId()); getExePool().removeTask(task.getTaskId());
if (ThreadTaskManager.getInstance().taskIsRunning(task.getTaskId())) { if (ThreadTaskManager2.INSTANCE.taskIsRunning(task.getTaskId())) {
ThreadTaskManager.getInstance().removeTaskThread(task.getTaskId()); ThreadTaskManager2.INSTANCE.stopThreadTask(task.getTaskId());
} }
} }
break; break;

Loading…
Cancel
Save