diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java index 5382cd4f..cc71525b 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java @@ -36,6 +36,8 @@ import com.arialyy.aria.core.download.target.HttpNormalTarget; import com.arialyy.aria.core.event.EventMsgUtil; import com.arialyy.aria.core.inf.AbsReceiver; import com.arialyy.aria.core.inf.ReceiverType; +import com.arialyy.aria.core.queue.DGroupTaskQueue; +import com.arialyy.aria.core.queue.DTaskQueue; import com.arialyy.aria.core.scheduler.TaskSchedulers; import com.arialyy.aria.core.task.ITask; import com.arialyy.aria.orm.DbEntity; @@ -302,6 +304,24 @@ public class DownloadReceiver extends AbsReceiver { return DbDataHelper.getDGEntityByHash(url); } + /** + * 获取执行中的任务 + * + * @return 没有执行中的任务,返回null + */ + public List getDRunningTask() { + return DTaskQueue.getInstance().getRunningTask(DownloadEntity.class); + } + + /** + * 获取执行中的任务 + * + * @return 没有执行中的任务,返回null + */ + public List getDGRunningTask() { + return DGroupTaskQueue.getInstance().getRunningTask(DownloadGroupEntity.class); + } + /** * 下载任务是否存在 * diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupNormalTarget.java index 4d710cc6..06e9b8ed 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupNormalTarget.java @@ -17,8 +17,10 @@ package com.arialyy.aria.core.download.target; import com.arialyy.aria.core.common.AbsNormalTarget; import com.arialyy.aria.core.common.HttpOption; +import com.arialyy.aria.core.download.DGTaskWrapper; import com.arialyy.aria.core.manager.SubTaskManager; import com.arialyy.aria.core.wrapper.ITaskWrapper; +import com.arialyy.aria.util.ALog; import java.util.List; /** @@ -101,6 +103,37 @@ public class GroupNormalTarget extends AbsNormalTarget { return mConfigHandler.setSubFileName(subTaskFileName); } + /** + * 如果无法获取到组合任务到总长度,请调用该方法, + * 请注意: + * 1、如果组合任务到子任务数过多,请不要使用该标志位,否则Aria将需要消耗大量的时间获取组合任务的总长度,直到获取完成组合任务总长度后才会执行下载。 + * 2、如果你的知道组合任务的总长度,请使用{@link #setFileSize(long)}设置组合任务的长度。 + * 3、由于网络或其它原因的存在,这种方式获取的组合任务大小有可能是不准确的。 + */ + public GroupNormalTarget unknownSize() { + ((DGTaskWrapper) getTaskWrapper()).setUnknownSize(true); + return this; + } + + /** + * 任务组总任务大小,任务组是一个抽象的概念,没有真实的数据实体,任务组的大小是Aria动态获取子任务大小相加而得到的, + * 如果你知道当前任务组总大小,你也可以调用该方法给任务组设置大小 + * + * 为了更好的用户体验,组合任务最好设置文件大小,默认需要强制设置文件大小。如果无法获取到总长度,请调用{@link #unknownSize()} + * + * @param fileSize 任务组总大小 + */ + public GroupNormalTarget setFileSize(long fileSize) { + if (fileSize <= 0) { + ALog.e(TAG, "文件大小不能小于 0"); + return this; + } + if (getEntity().getFileSize() <= 1 || getEntity().getFileSize() != fileSize) { + getEntity().setFileSize(fileSize); + } + return this; + } + @Override public boolean isRunning() { return mConfigHandler.isRunning(); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java b/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java index 203ee0a5..695c7bf4 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java @@ -16,6 +16,8 @@ package com.arialyy.aria.core.queue; +import android.text.TextUtils; +import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.inf.TaskSchedulerType; import com.arialyy.aria.core.manager.TaskWrapperManager; @@ -31,6 +33,8 @@ import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.aria.core.task.UploadTask; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.ALog; +import java.util.ArrayList; +import java.util.List; /** * Created by lyy on 2017/2/23. 任务队列 @@ -64,7 +68,33 @@ public abstract class AbsTaskQueue List getRunningTask(Class type) { + List exeTask = mExecutePool.getAllTask(); + List cacheTask = mCachePool.getAllTask(); + List entities = new ArrayList<>(); + if (exeTask != null && !exeTask.isEmpty()) { + for (TASK task : exeTask) { + entities.add((T) task.getTaskWrapper().getEntity()); + } + } + if (cacheTask != null && !cacheTask.isEmpty()) { + for (TASK task : cacheTask) { + entities.add((T) task.getTaskWrapper().getEntity()); + } + } + return entities.isEmpty() ? null : entities; + } + @Override public boolean taskIsRunning(String key) { + if (TextUtils.isEmpty(key)) { + ALog.w(TAG, "key为空,无法确认任务是否执行"); + return false; + } TASK task = mExecutePool.getTask(key); if (task == null && ThreadTaskManager.getInstance().taskIsRunning(key)) { ThreadTaskManager.getInstance().removeTaskThread(key); diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/DTaskQueue.java b/Aria/src/main/java/com/arialyy/aria/core/queue/DTaskQueue.java index d275bbc5..499a37dc 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/DTaskQueue.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/DTaskQueue.java @@ -18,6 +18,7 @@ package com.arialyy.aria.core.queue; import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.download.DTaskWrapper; +import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.event.DMaxNumEvent; import com.arialyy.aria.core.event.Event; import com.arialyy.aria.core.event.EventMsgUtil; diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java index 7b5aea7b..3eaa62ae 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java @@ -28,6 +28,7 @@ import com.arialyy.aria.core.common.ProxyHelper; import com.arialyy.aria.core.event.EventMsgUtil; import com.arialyy.aria.core.inf.AbsReceiver; import com.arialyy.aria.core.inf.ReceiverType; +import com.arialyy.aria.core.queue.UTaskQueue; import com.arialyy.aria.core.scheduler.TaskSchedulers; import com.arialyy.aria.core.task.ITask; import com.arialyy.aria.core.upload.target.FtpBuilderTarget; @@ -230,6 +231,15 @@ public class UploadReceiver extends AbsReceiver { ITask.UPLOAD)); } + /** + * 获取执行中的任务 + * + * @return 没有执行中的任务,返回null + */ + public List getURunningTask() { + return UTaskQueue.getInstance().getRunningTask(UploadEntity.class); + } + /** * 删除所有任务 * diff --git a/DEV_LOG.md b/DEV_LOG.md index e94c8782..fac48057 100644 --- a/DEV_LOG.md +++ b/DEV_LOG.md @@ -5,6 +5,8 @@ - 添加ftp服务器标志 https://github.com/AriaLyy/Aria/issues/580 - 重构loader模块,让loader模块的代码更加清晰,去除一些不必要的线程创建 - 修复ftp上传完成后,删除服务器端的文件,无法重新下载的问题 + - 增加获取执行中的任务api,详情见:https://aria.laoyuyu.me/aria_doc/api/task_list.html + - 增加获取剩余时间的api,详情见:https://aria.laoyuyu.me/aria_doc/start/task_explain.html + v_3.8.1 (2019/12/22) - 修复一个表创建失败的问题 https://github.com/AriaLyy/Aria/issues/570 - 修复一个非分块模式下导致下载失败的问题 https://github.com/AriaLyy/Aria/issues/571 diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDLoaderUtil.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDLoaderUtil.java index 969de722..160057f5 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDLoaderUtil.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDLoaderUtil.java @@ -42,7 +42,7 @@ public final class FtpDLoaderUtil extends AbsNormalLoaderUtil { public LoaderStructure BuildLoaderStructure() { LoaderStructure structure = new LoaderStructure(); - structure.addComponent(new FtpDRecordHandler(getTaskWrapper())) + structure.addComponent(new FtpDRecordHandler((DTaskWrapper) getTaskWrapper())) .addComponent(new NormalThreadStateManager(getListener())) .addComponent(new FtpDFileInfoTask((DTaskWrapper) getTaskWrapper())) .addComponent(new FtpDTTBuilder(getTaskWrapper())); diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoTask.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoTask.java index 6069f5ee..58ecaef4 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoTask.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoTask.java @@ -60,10 +60,7 @@ final class FtpUFileInfoTask extends AbsFtpInfoTask return; } - //为了防止编码错乱,需要使用原始字符串 - if (files.length == 0) { - handleFile(getRemotePath(), null); - } + handleFile(getRemotePath(), files.length == 0 ? null : files[0]); int reply = client.getReplyCode(); if (!FTPReply.isPositiveCompletion(reply)) { //服务器上没有该文件路径,表示该任务为新的上传任务 diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java index 8d05431a..c948a6be 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java @@ -36,7 +36,7 @@ import java.util.concurrent.TimeUnit; */ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { private String dir, remotePath; - private boolean storeFail = false; + private boolean storeSuccess = false; private ScheduledThreadPoolExecutor timer; private FTPClient client = null; private boolean isTimeOut = true; @@ -77,7 +77,11 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { file.seek(getThreadRecord().startLocation); } boolean complete = upload(file); - if (!complete || getThreadTask().isBreak()) { + if (getThreadTask().isBreak()) { + return; + } + if (!complete){ + fail(new AriaIOException(TAG, "ftp文件上传失败"), false); return; } ALog.i(TAG, @@ -158,12 +162,11 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { private boolean upload(final BufferedRandomAccessFile bis) throws IOException { fa = new FtpFISAdapter(bis); - storeFail = false; + storeSuccess = false; startTimer(); - final long fileSize = getThreadConfig().tempFile.length(); try { ALog.d(TAG, String.format("remotePath: %s", remotePath)); - client.storeFile(remotePath, fa, new OnFtpInputStreamListener() { + storeSuccess = client.storeFile(remotePath, fa, new OnFtpInputStreamListener() { boolean isStoped = false; @Override public void onFtpInputStream(FTPClient client, long totalBytesTransferred, @@ -181,7 +184,7 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { progress(bytesTransferred); } catch (IOException e) { e.printStackTrace(); - storeFail = true; + storeSuccess = false; } } }); @@ -192,15 +195,11 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { if (e.getMessage().contains("AriaIOException caught while copying")) { e.printStackTrace(); } else { - fail(new AriaIOException(TAG, msg, e), !storeFail); + fail(new AriaIOException(TAG, msg, e), !storeSuccess); } return false; } finally { fa.close(); - closeClient(client); - } - if (storeFail) { - return false; } int reply = client.getReplyCode(); if (!FTPReply.isPositiveCompletion(reply)) { @@ -212,6 +211,6 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { closeClient(client); return false; } - return true; + return storeSuccess; } } diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/LiveRecordHandler.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/LiveRecordHandler.java new file mode 100644 index 00000000..206c2a97 --- /dev/null +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/LiveRecordHandler.java @@ -0,0 +1,99 @@ +/* + * 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.m3u8.live; + +import com.arialyy.aria.core.TaskRecord; +import com.arialyy.aria.core.ThreadRecord; +import com.arialyy.aria.core.common.RecordHandler; +import com.arialyy.aria.core.loader.IRecordHandler; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.m3u8.M3U8TaskOption; +import com.arialyy.aria.util.RecordUtil; +import java.util.ArrayList; + +/** + * 直播m3u8文件处理器 + */ +final class LiveRecordHandler extends RecordHandler { + private M3U8TaskOption mOption; + + LiveRecordHandler(AbsTaskWrapper wrapper) { + super(wrapper); + } + + public void setOption(M3U8TaskOption option) { + mOption = option; + } + + @Override public void onPre() { + super.onPre(); + RecordUtil.delTaskRecord(getEntity().getFilePath(), IRecordHandler.TYPE_DOWNLOAD); + } + + /** + * @deprecated 直播文件不需要处理任务记录 + */ + @Deprecated + @Override public void handlerTaskRecord(TaskRecord record) { + if (record.threadRecords == null) { + record.threadRecords = new ArrayList<>(); + } + } + + /** + * @deprecated 交由{@link #createThreadRecord(TaskRecord, String, int)} 处理 + */ + @Override + @Deprecated + public ThreadRecord createThreadRecord(TaskRecord record, int threadId, long startL, long endL) { + return null; + } + + /** + * 创建线程记录 + * + * @param taskRecord 任务记录 + * @param tsUrl ts下载地址 + * @param threadId 线程id + */ + ThreadRecord createThreadRecord(TaskRecord taskRecord, String tsUrl, int threadId) { + ThreadRecord tr = new ThreadRecord(); + tr.taskKey = taskRecord.filePath; + tr.isComplete = false; + tr.tsUrl = tsUrl; + tr.threadType = getEntity().getTaskType(); + tr.threadId = threadId; + tr.startLocation = 0; + taskRecord.threadRecords.add(tr); + 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; + record.isBlock = true; + record.taskType = getEntity().getTaskType(); + record.bandWidth = mOption.getBandWidth(); + return record; + } + + @Override public int initTaskThreadNum() { + return 1; + } +} diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/LiveStateManager.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/LiveStateManager.java index ab7901c5..03f28116 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/LiveStateManager.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/LiveStateManager.java @@ -15,6 +15,7 @@ */ package com.arialyy.aria.m3u8.live; +import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -80,7 +81,11 @@ final class LiveStateManager implements IThreadStateManager { msg.getData().getString(ISchedulers.DATA_M3U8_PEER_PATH), peerIndex); break; case STATE_RUNNING: - mProgress += (long) msg.obj; + Bundle b = msg.getData(); + if (b != null) { + long len = b.getLong(IThreadStateManager.DATA_ADD_LEN, 0); + mProgress += len; + } break; case STATE_FAIL: mLoader.notifyLock(false, peerIndex); diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java index 0bd13449..4768faca 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java @@ -68,11 +68,9 @@ final class M3U8LiveLoader extends BaseM3U8Loader { private Condition mCondition = LOCK.newCondition(); private LinkedBlockingQueue mPeerQueue = new LinkedBlockingQueue<>(); private ExtInfo mCurExtInfo; - private LiveStateManager mManager; private M3U8InfoTask mInfoTask; private ScheduledThreadPoolExecutor mTimer; private List mPeerUrls = new ArrayList<>(); - private Looper mLooper; M3U8LiveLoader(DTaskWrapper wrapper, M3U8Listener listener) { super(wrapper, listener); @@ -94,8 +92,22 @@ final class M3U8LiveLoader extends BaseM3U8Loader { if (isBreak()) { return; } - mLooper = looper; + + // 处理记录 + getRecordHandler().setOption(mM3U8Option); + mRecord = getRecordHandler().getRecord(0); + + // 初始化状态管理器 + getStateManager().setLooper(mRecord, looper); + getStateManager().setLoader(this); + mStateHandler = new Handler(looper, getStateManager().getHandlerCallback()); + + // 循环获取直播文件列表 startLoaderLiveInfo(); + + // 启动定时器 + startTimer(); + new Thread(new Runnable() { @Override public void run() { String cacheDir = getCacheDir(); @@ -127,6 +139,14 @@ final class M3U8LiveLoader extends BaseM3U8Loader { }).start(); } + @Override protected LiveStateManager getStateManager() { + return (LiveStateManager) super.getStateManager(); + } + + private LiveRecordHandler getRecordHandler() { + return (LiveRecordHandler) mRecordHandler; + } + @Override public long getFileSize() { return mTempFile.length(); } @@ -165,20 +185,14 @@ final class M3U8LiveLoader extends BaseM3U8Loader { * 配置config */ private ThreadTask createThreadTask(String cacheDir, int indexId, String tsUrl) { - ThreadRecord record = new ThreadRecord(); - record.taskKey = mRecord.filePath; - record.isComplete = false; - record.tsUrl = tsUrl; - record.threadType = getEntity().getTaskType(); - record.threadId = indexId; - mRecord.threadRecords.add(record); + ThreadRecord tr = getRecordHandler().createThreadRecord(mRecord, tsUrl, indexId); SubThreadConfig config = new SubThreadConfig(); config.url = tsUrl; config.tempFile = new File(getTsFilePath(cacheDir, indexId)); config.isBlock = mRecord.isBlock; config.taskWrapper = mTaskWrapper; - config.record = record; + config.record = tr; config.stateHandler = mStateHandler; config.peerIndex = indexId; config.threadType = SubThreadConfig.getThreadType(ITaskWrapper.M3U8_LIVE); @@ -219,15 +233,10 @@ final class M3U8LiveLoader extends BaseM3U8Loader { if (isSuccess) { // 合并成功,删除缓存文件 for (String pp : partPath) { - File f = new File(pp); - if (f.exists()) { - f.delete(); - } + FileUtil.deleteFile(pp); } File cDir = new File(cacheDir); - if (cDir.exists()) { - cDir.delete(); - } + FileUtil.deleteDir(cDir); return true; } else { ALog.e(TAG, "合并失败"); @@ -323,10 +332,7 @@ final class M3U8LiveLoader extends BaseM3U8Loader { * 需要在{@link #addComponent(IRecordHandler)} 后调用 */ @Override public void addComponent(IThreadStateManager threadState) { - mManager = (LiveStateManager) threadState; - mManager.setLooper(mRecordHandler.getRecord(0), mLooper); - mManager.setLoader(this); - mStateHandler = new Handler(mLooper, mManager.getHandlerCallback()); + mStateManager = threadState; } /** @@ -344,7 +350,7 @@ final class M3U8LiveLoader extends BaseM3U8Loader { if (mInfoTask == null) { throw new NullPointerException(("文件信息组件为空")); } - if (mManager == null) { + if (mStateManager == null) { throw new NullPointerException("任务状态管理组件为空"); } } diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java index eb7327a5..a447be99 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java @@ -23,9 +23,7 @@ import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.http.HttpTaskOption; import com.arialyy.aria.m3u8.M3U8InfoTask; import com.arialyy.aria.m3u8.M3U8Listener; -import com.arialyy.aria.m3u8.M3U8RecordHandler; import com.arialyy.aria.m3u8.M3U8TaskOption; -import com.arialyy.aria.util.CommonUtil; /** * M3U8直播文件下载工具,对于直播来说,需要定时更新m3u8文件 @@ -37,7 +35,6 @@ import com.arialyy.aria.util.CommonUtil; * 5、不处理直播切片下载失败的状态 */ public class M3U8LiveUtil extends AbsNormalLoaderUtil { - private final String TAG = CommonUtil.getClassName(getClass()); public M3U8LiveUtil(AbsTaskWrapper wrapper, IEventListener listener) { super(wrapper, listener); @@ -57,7 +54,7 @@ public class M3U8LiveUtil extends AbsNormalLoaderUtil { @Override public LoaderStructure BuildLoaderStructure() { LoaderStructure structure = new LoaderStructure(); - structure.addComponent(new M3U8RecordHandler(getTaskWrapper())) + structure.addComponent(new LiveRecordHandler(getTaskWrapper())) .addComponent(new M3U8InfoTask(getTaskWrapper())) .addComponent(new LiveStateManager(getTaskWrapper(), getListener())); structure.accept(getLoader()); diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java index d3f46333..832ecb53 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java @@ -73,7 +73,6 @@ final class M3U8VodLoader extends BaseM3U8Loader { private Condition mJumpCondition = JUMP_LOCK.newCondition(); private SparseArray mBeforePeer = new SparseArray<>(); private SparseArray mAfterPeer = new SparseArray<>(); - private VodStateManager mManager; private PeerIndexEvent mCurrentEvent; private String mCacheDir; private int aIndex = 0, bIndex = 0; @@ -136,11 +135,6 @@ final class M3U8VodLoader extends BaseM3U8Loader { } } - @Override protected void onPostPre() { - super.onPostPre(); - initData(); - } - @Override public boolean isBreak() { return super.isBreak() || isDestroy; } @@ -154,6 +148,22 @@ final class M3U8VodLoader extends BaseM3U8Loader { } private void startThreadTask() { + // 处理任务记录 + ((VodRecordHandler) mRecordHandler).setOption(mM3U8Option); + mRecord = mRecordHandler.getRecord(0); + + // 处理任务管理器 + mStateHandler = new Handler(mLooper, getStateManager().getHandlerCallback()); + getStateManager().setVodLoader(this); + getStateManager().setLooper(mRecord, mLooper); + + // 初始化ts数据 + initData(); + + // 启动定时器 + startTimer(); + + // 启动线程开始下载ts切片 Thread th = new Thread(new Runnable() { @Override public void run() { while (!isBreak()) { @@ -258,7 +268,7 @@ final class M3U8VodLoader extends BaseM3U8Loader { mCompleteNum++; } } - mManager.updateStateCount(); + getStateManager().updateStateCount(); if (mCompleteNum <= 0) { getListener().onStart(0); } else { @@ -416,7 +426,7 @@ final class M3U8VodLoader extends BaseM3U8Loader { String.format("beforeSize = %s, afterSize = %s, mCompleteNum = %s", mBeforePeer.size(), mAfterPeer.size(), mCompleteNum)); ALog.i(TAG, String.format("完成处理数据的操作,将优先下载【%s】之后的切片", mCurrentEvent.peerIndex)); - mManager.updateStateCount(); + getStateManager().updateStateCount(); try { JUMP_LOCK.lock(); @@ -487,7 +497,6 @@ final class M3U8VodLoader extends BaseM3U8Loader { @Override public void addComponent(IRecordHandler recordHandler) { mRecordHandler = recordHandler; - mRecord = mRecordHandler.getRecord(0); } @Override public void addComponent(IInfoTask infoTask) { @@ -543,10 +552,7 @@ final class M3U8VodLoader extends BaseM3U8Loader { * 需要在 {@link #addComponent(IRecordHandler)}后调用 */ @Override public void addComponent(IThreadStateManager threadState) { - mManager = (VodStateManager) threadState; - mStateHandler = new Handler(mLooper, mManager.getHandlerCallback()); - mManager.setVodLoader(this); - mManager.setLooper(mRecord, mLooper); + mStateManager = threadState; } /** @@ -557,6 +563,11 @@ final class M3U8VodLoader extends BaseM3U8Loader { } + @Override + protected VodStateManager getStateManager() { + return (VodStateManager) mStateManager; + } + @Override protected void checkComponent() { if (mRecordHandler == null) { throw new NullPointerException("任务记录组件为空"); @@ -564,7 +575,7 @@ final class M3U8VodLoader extends BaseM3U8Loader { if (mInfoTask == null) { throw new NullPointerException(("文件信息组件为空")); } - if (mManager == null) { + if (getStateManager() == null) { throw new NullPointerException("任务状态管理组件为空"); } } diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodUtil.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodUtil.java index 86b97bd6..b02b57a1 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodUtil.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodUtil.java @@ -24,7 +24,6 @@ import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.http.HttpTaskOption; import com.arialyy.aria.m3u8.M3U8InfoTask; import com.arialyy.aria.m3u8.M3U8Listener; -import com.arialyy.aria.m3u8.M3U8RecordHandler; import com.arialyy.aria.m3u8.M3U8TaskOption; /** @@ -54,7 +53,7 @@ public final class M3U8VodUtil extends AbsNormalLoaderUtil { @Override public LoaderStructure BuildLoaderStructure() { LoaderStructure structure = new LoaderStructure(); - structure.addComponent(new M3U8RecordHandler(getTaskWrapper())) + structure.addComponent(new VodRecordHandler(getTaskWrapper())) .addComponent(new M3U8InfoTask(getTaskWrapper())) .addComponent(new VodStateManager(getTaskWrapper(), (M3U8Listener) getListener())); structure.accept(getLoader()); diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordHandler.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/VodRecordHandler.java similarity index 88% rename from M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordHandler.java rename to M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/VodRecordHandler.java index 507dcb05..4ba82c57 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordHandler.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/VodRecordHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.m3u8; +package com.arialyy.aria.m3u8.vod; import com.arialyy.aria.core.TaskRecord; import com.arialyy.aria.core.ThreadRecord; @@ -21,11 +21,12 @@ import com.arialyy.aria.core.common.RecordHandler; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.M3U8Entity; -import com.arialyy.aria.core.loader.IRecordHandler; import com.arialyy.aria.core.wrapper.ITaskWrapper; +import com.arialyy.aria.m3u8.BaseM3U8Loader; +import com.arialyy.aria.m3u8.M3U8InfoTask; +import com.arialyy.aria.m3u8.M3U8TaskOption; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.FileUtil; -import com.arialyy.aria.util.RecordUtil; import java.io.File; import java.util.ArrayList; @@ -33,19 +34,15 @@ import java.util.ArrayList; * @Author lyy * @Date 2019-09-24 */ -public final class M3U8RecordHandler extends RecordHandler { +final class VodRecordHandler extends RecordHandler { private M3U8TaskOption mOption; - public M3U8RecordHandler(DTaskWrapper wrapper) { + VodRecordHandler(DTaskWrapper wrapper) { super(wrapper); - mOption = (M3U8TaskOption) wrapper.getM3u8Option(); } - @Override public void onPre() { - super.onPre(); - if (getWrapper().getRequestType() == ITaskWrapper.M3U8_LIVE) { - RecordUtil.delTaskRecord(getEntity().getFilePath(), IRecordHandler.TYPE_DOWNLOAD); - } + public void setOption(M3U8TaskOption option) { + mOption = option; } /** diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/VodStateManager.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/VodStateManager.java index 2b880862..425ba1a7 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/VodStateManager.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/VodStateManager.java @@ -52,7 +52,6 @@ public final class VodStateManager implements IThreadStateManager { private int cancelNum = 0; // 已经取消的线程的数 private int stopNum = 0; // 已经停止的线程数 private int failNum = 0; // 失败的线程数 - private long percent; //当前总进度,百分比进度 private long progress; private TaskRecord taskRecord; // 任务记录 private Looper looper; @@ -164,7 +163,11 @@ public final class VodStateManager implements IThreadStateManager { } break; case STATE_RUNNING: - progress += (long) msg.obj; + Bundle b = msg.getData(); + if (b != null) { + long len = b.getLong(IThreadStateManager.DATA_ADD_LEN, 0); + progress += len; + } break; } return true; @@ -226,7 +229,6 @@ public final class VodStateManager implements IThreadStateManager { int percent = completeNum * 100 / taskRecord.threadRecords.size(); getEntity().setPercent(percent); getEntity().update(); - this.percent = percent; } @Override public boolean isFail() { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsEntity.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsEntity.java index 02eb8105..4df5008b 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsEntity.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsEntity.java @@ -41,6 +41,11 @@ public abstract class AbsEntity extends DbEntity implements IEntity, Parcelable, */ @Ignore private int failNum = 0; + /** + * 剩余时间,单位:s + */ + @Ignore private int timeLeft = Integer.MAX_VALUE; + /** * 扩展字段 */ @@ -81,6 +86,18 @@ public abstract class AbsEntity extends DbEntity implements IEntity, Parcelable, */ private long stopTime = 0; + /** + * 获取剩余时间,单位:s + * 如果是m3u8任务,无法获取剩余时间;m2u8任务如果需要获取剩余时间,请设置文件长度{@link #setFileSize(long)} + */ + public int getTimeLeft() { + return timeLeft; + } + + public void setTimeLeft(int timeLeft) { + this.timeLeft = timeLeft; + } + public boolean isComplete() { return isComplete; } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java index 9fe5c43a..9942ff89 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java @@ -156,10 +156,18 @@ public class RecordHelper { : mTaskRecord.filePath); ThreadRecord tr = mTaskRecord.threadRecords.get(0); if (!file.exists()) { - ALog.w(TAG, String.format("文件【%s】不存在,任务将重新开始", file.getPath())); - tr.startLocation = 0; - tr.isComplete = false; - tr.endLocation = mWrapper.getEntity().getFileSize(); + // 目标文件 + File targetFile = new File(mTaskRecord.filePath); + // 处理组合任务其中一个子任务完成的情况 + if (tr.isComplete && targetFile.exists() && targetFile.length() == mWrapper.getEntity() + .getFileSize()) { + tr.isComplete = true; + } else { + ALog.w(TAG, String.format("文件【%s】不存在,任务将重新开始", file.getPath())); + tr.startLocation = 0; + tr.isComplete = false; + tr.endLocation = mWrapper.getEntity().getFileSize(); + } } else if (file.length() > mWrapper.getEntity().getFileSize()) { ALog.i(TAG, String.format("文件【%s】错误,任务重新开始", file.getPath())); FileUtil.deleteFile(file); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java index e7846b1b..e1b6b11d 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java @@ -46,7 +46,7 @@ public class SubThreadConfig { public int peerIndex; // 线程任务类型 public int threadType = TYPE_HTTP; - // 更新间隔,单位:毫秒 + // 进度更新间隔,单位:毫秒 public long updateInterval = 1000; /** diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoader.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoader.java index 3969971a..4dbb02c1 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoader.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoader.java @@ -272,8 +272,8 @@ public abstract class AbsGroupLoader implements ILoaderVisitor, ILoader { mListener.onComplete(); return; } - startTimer(); handlerTask(looper); + startTimer(); Looper.loop(); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java index 6f370cf2..6e333d2a 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java @@ -140,6 +140,13 @@ public abstract class BaseListener } @Override public void stop(int type) { + mUtil = createUtil(); + if (mUtil == null){ + ALog.e(TAG, "任务工具创建失败"); + return; + } isStop = true; mSchedulerType = type; getUtil().stop(); @@ -226,6 +255,11 @@ public abstract class AbsTask } @Override public void cancel(int type) { + mUtil = createUtil(); + if (mUtil == null){ + ALog.e(TAG, "任务工具创建失败"); + return; + } isCancel = true; mSchedulerType = type; getUtil().cancel(); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java index a9463d26..01f15423 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java @@ -67,8 +67,8 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { private long mRangeProgress, mLastRangeProgress; private IThreadTaskAdapter mAdapter; private ThreadRecord mRecord; - private String mThreadNmae; - private long updateInterval = 1000; // 更新间隔 + private String mThreadName; + private long updateInterval; // 更新间隔 private Thread mConfigThread = new Thread(new Runnable() { @Override public void run() { @@ -243,8 +243,8 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { } @Override public String getThreadName() { - return mThreadNmae == null ? CommonUtil.getThreadName(getConfig().url, getThreadId()) - : mThreadNmae; + return mThreadName == null ? (mThreadName = + CommonUtil.getThreadName(getConfig().url, getThreadId())) : mThreadName; } /** @@ -287,7 +287,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { bundle.putLong(IThreadStateManager.DATA_THREAD_LOCATION, mRangeProgress); msg.what = state; int reqType = getConfig().threadType; - if (reqType == ITaskWrapper.M3U8_VOD || reqType == ITaskWrapper.M3U8_LIVE) { + if (reqType == SubThreadConfig.TYPE_M3U8_PEER) { sendM3U8Info(state, msg); } @@ -299,10 +299,10 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { } private void sendM3U8Info(int state, Message msg) { + Bundle bundle = msg.getData(); if (state != IThreadStateManager.STATE_UPDATE_PROGRESS) { msg.obj = this; } - Bundle bundle = msg.getData(); if ((state == IThreadStateManager.STATE_COMPLETE || state == IThreadStateManager.STATE_FAIL)) { bundle.putString(ISchedulers.DATA_M3U8_URL, getConfig().url); bundle.putString(ISchedulers.DATA_M3U8_PEER_PATH, getConfig().tempFile.getPath()); @@ -313,6 +313,8 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { @Override public synchronized void updateCompleteState() { ALog.i(TAG, String.format("任务【%s】线程__%s__完成", getTaskWrapper().getKey(), mRecord.threadId)); writeConfig(true, mRecord.endLocation); + // 进度发送不是实时的,发送完成任务前,需要更新一次进度 + sendRunningState(); updateState(IThreadStateManager.STATE_COMPLETE, null); } @@ -334,17 +336,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { } // 不需要太频繁发送,以减少消息队列的压力 if (System.currentTimeMillis() - mLastSendProgressTime > updateInterval) { - Message msg = mStateHandler.obtainMessage(); - Bundle b = msg.getData(); - if (b == null) { - b = new Bundle(); - msg.setData(b); - } - b.putString(IThreadStateManager.DATA_THREAD_NAME, getThreadName()); - b.putLong(IThreadStateManager.DATA_ADD_LEN, mRangeProgress - mLastRangeProgress); - msg.what = IThreadStateManager.STATE_RUNNING; - msg.obj = mRangeProgress; - msg.sendToTarget(); + sendRunningState(); mLastRangeProgress = mRangeProgress; mLastSendProgressTime = System.currentTimeMillis(); } @@ -358,6 +350,23 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { } } + /** + * 发送执行中的数据 + */ + private void sendRunningState() { + Message msg = mStateHandler.obtainMessage(); + Bundle b = msg.getData(); + if (b == null) { + b = new Bundle(); + msg.setData(b); + } + b.putString(IThreadStateManager.DATA_THREAD_NAME, getThreadName()); + b.putLong(IThreadStateManager.DATA_ADD_LEN, mRangeProgress - mLastRangeProgress); + msg.what = IThreadStateManager.STATE_RUNNING; + msg.obj = mRangeProgress; + msg.sendToTarget(); + } + @Override public long getThreadProgress() { return mRangeProgress; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java index bbb0f4d6..5abf3cd7 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java @@ -48,6 +48,7 @@ import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; @@ -794,6 +795,39 @@ public class CommonUtil { return result4.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "tb"; } + /** + * 转换时间 + * 时间<1 小时,显示分秒,显示样式 00:20 + * 时间 ≥1 小时,显示时分秒,显示样式 01:11:12 + * 时间 ≥1 天,显示天时分,显示样式 1d 01:11 + * 时间 ≥7 天,显示样式 ∞ + * + * @param seconds 单位为s的时间 + */ + public static String formatTime(int seconds) { + String standardTime; + if (seconds <= 0) { + standardTime = "00:00"; + } else if (seconds < 60) { + standardTime = String.format(Locale.getDefault(), "00:%02d", seconds % 60); + } else if (seconds < 3600) { + standardTime = String.format(Locale.getDefault(), "%02d:%02d", seconds / 60, seconds % 60); + } else if (seconds < 86400) { + standardTime = + String.format(Locale.getDefault(), "%02d:%02d:%02d", seconds / 3600, seconds % 3600 / 60, + seconds % 60); + } else if (seconds < 604800) { + standardTime = + String.format(Locale.getDefault(), "%dd %02d:%02d", seconds / 86400, + seconds % 86400 / 3600, + seconds % 3600); + } else { + standardTime = "∞"; + } + + return standardTime; + } + /** * 通过文件名获取下载配置文件路径 * diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java index 1bc3b215..30fb69fd 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java @@ -128,6 +128,7 @@ public class ComponentUtil { break; } if (className == null) { + ALog.e(TAG, "不识别的类名:" + className); return null; } T util = null; diff --git a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java index ba3e6cd8..e844ab84 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java @@ -204,6 +204,7 @@ public class SingleTaskActivity extends BaseActivity { getBinding().setProgress(task.getPercent()); } getBinding().setSpeed(task.getConvertSpeed()); + getBinding().setTimeLeft(task.getConvertTimeLeft()); } } diff --git a/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java b/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java index 48d9d47b..4a0cc8ad 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java @@ -155,10 +155,11 @@ public class DownloadGroupActivity extends BaseActivity + @@ -40,6 +44,7 @@ bind:progress="@{progress}" bind:speed="@{speed}" bind:stateStr="@{stateStr}" + bind:timeLeft="@{timeLeft}" /> +