loader 重构
This commit is contained in:
@@ -45,6 +45,8 @@ public abstract class RecordHandler implements IRecordHandler {
|
||||
private TaskRecord mTaskRecord;
|
||||
private AbsTaskWrapper mTaskWrapper;
|
||||
private AbsNormalEntity mEntity;
|
||||
protected String mFilePath;
|
||||
protected long mFileSize;
|
||||
|
||||
public RecordHandler(AbsTaskWrapper wrapper) {
|
||||
mTaskWrapper = wrapper;
|
||||
@@ -72,7 +74,8 @@ public abstract class RecordHandler implements IRecordHandler {
|
||||
* @return 任务记录
|
||||
*/
|
||||
@Override
|
||||
public TaskRecord getRecord() {
|
||||
public TaskRecord getRecord(long fileSize) {
|
||||
mFileSize = fileSize;
|
||||
mConfigFile = new File(CommonUtil.getFileConfigPath(false, mEntity.getFileName()));
|
||||
if (mConfigFile.exists()) {
|
||||
convertDb();
|
||||
@@ -80,22 +83,7 @@ public abstract class RecordHandler implements IRecordHandler {
|
||||
onPre();
|
||||
mTaskRecord = DbDataHelper.getTaskRecord(getFilePath(), mEntity.getTaskType());
|
||||
if (mTaskRecord == null) {
|
||||
if (!new File(getFilePath()).exists()) {
|
||||
FileUtil.createFile(getFilePath());
|
||||
}
|
||||
initRecord(true);
|
||||
} else {
|
||||
File file = new File(mTaskRecord.filePath);
|
||||
if (!file.exists()) {
|
||||
ALog.w(TAG, String.format("文件【%s】不存在,重新分配线程区间", mTaskRecord.filePath));
|
||||
DbEntity.deleteData(ThreadRecord.class, "taskKey=?", mTaskRecord.filePath);
|
||||
mTaskRecord.threadRecords.clear();
|
||||
mTaskRecord.threadNum = initTaskThreadNum();
|
||||
initRecord(false);
|
||||
} else if (mTaskRecord.threadRecords == null || mTaskRecord.threadRecords.isEmpty()) {
|
||||
mTaskRecord.threadNum = initTaskThreadNum();
|
||||
initRecord(false);
|
||||
}
|
||||
}
|
||||
handlerTaskRecord(mTaskRecord);
|
||||
}
|
||||
@@ -172,7 +160,7 @@ public abstract class RecordHandler implements IRecordHandler {
|
||||
if (requestType == ITaskWrapper.M3U8_LIVE) {
|
||||
return;
|
||||
}
|
||||
long blockSize = mEntity.getFileSize() / mTaskRecord.threadNum;
|
||||
long blockSize = getFileSize() / mTaskRecord.threadNum;
|
||||
// 处理线程区间记录
|
||||
for (int i = 0; i < mTaskRecord.threadNum; i++) {
|
||||
long startL = i * blockSize, endL = (i + 1) * blockSize;
|
||||
@@ -193,6 +181,10 @@ public abstract class RecordHandler implements IRecordHandler {
|
||||
ALog.d(TAG, String.format("保存记录,线程记录数:%s", mTaskRecord.threadRecords.size()));
|
||||
}
|
||||
|
||||
protected long getFileSize() {
|
||||
return mFileSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务路径
|
||||
*
|
||||
|
||||
@@ -149,28 +149,29 @@ public class RecordHelper {
|
||||
* 处理单线程的任务的记录
|
||||
*/
|
||||
public void handleSingleThreadRecord() {
|
||||
File file = new File(mTaskRecord.filePath);
|
||||
// mTaskRecord.isBlock是为了兼容以前的文件格式
|
||||
File file = new File(
|
||||
mTaskRecord.isBlock ? String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, 0)
|
||||
: 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();
|
||||
} else if (mTaskRecord.isBlock) {
|
||||
if (file.length() > mWrapper.getEntity().getFileSize()) {
|
||||
ALog.i(TAG, String.format("文件【%s】错误,任务重新开始", file.getPath()));
|
||||
FileUtil.deleteFile(file);
|
||||
tr.startLocation = 0;
|
||||
} else if (file.length() > mWrapper.getEntity().getFileSize()) {
|
||||
ALog.i(TAG, String.format("文件【%s】错误,任务重新开始", file.getPath()));
|
||||
FileUtil.deleteFile(file);
|
||||
tr.startLocation = 0;
|
||||
tr.isComplete = false;
|
||||
tr.endLocation = mWrapper.getEntity().getFileSize();
|
||||
} else if (file.length() == mWrapper.getEntity().getFileSize()) {
|
||||
tr.isComplete = true;
|
||||
} else {
|
||||
if (file.length() != tr.startLocation) {
|
||||
ALog.i(TAG, String.format("修正【%s】的进度记录为:%s", file.getPath(), file.length()));
|
||||
tr.startLocation = file.length();
|
||||
tr.isComplete = false;
|
||||
tr.endLocation = mWrapper.getEntity().getFileSize();
|
||||
} else if (file.length() == mWrapper.getEntity().getFileSize()) {
|
||||
tr.isComplete = true;
|
||||
} else {
|
||||
if (file.length() != tr.startLocation) {
|
||||
ALog.i(TAG, String.format("修正【%s】的进度记录为:%s", file.getPath(), file.length()));
|
||||
tr.startLocation = file.length();
|
||||
tr.isComplete = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
mWrapper.setNewTask(false);
|
||||
|
||||
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
* 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.IThreadStateManager;
|
||||
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.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;
|
||||
protected 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, AbsSubDLoadUtil> 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 AbsSubDLoadUtil createSubLoader(DTaskWrapper wrapper, boolean needGetFileInfo);
|
||||
|
||||
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()) {
|
||||
File subFile = new File(wrapper.getEntity().getFilePath());
|
||||
if (wrapper.getEntity().getState() == IEntity.STATE_COMPLETE
|
||||
&& subFile.exists()
|
||||
&& subFile.length() == wrapper.getEntity().getFileSize()) {
|
||||
mState.updateCompleteNum();
|
||||
mCurrentLocation += wrapper.getEntity().getFileSize();
|
||||
} else {
|
||||
if (!subFile.exists()) {
|
||||
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));
|
||||
}
|
||||
|
||||
@Override public String getKey() {
|
||||
return mGTWrapper.getKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动子任务下载
|
||||
*
|
||||
* @param url 子任务下载地址
|
||||
*/
|
||||
void startSubTask(String url) {
|
||||
if (!checkSubTask(url, "开始")) {
|
||||
return;
|
||||
}
|
||||
if (!mState.isRunning) {
|
||||
startTimer();
|
||||
}
|
||||
AbsSubDLoadUtil d = getDownloader(url, false);
|
||||
if (d != null && !d.isRunning()) {
|
||||
mSubQueue.startTask(d);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止子任务下载
|
||||
*
|
||||
* @param url 子任务下载地址
|
||||
*/
|
||||
void stopSubTask(String url) {
|
||||
if (!checkSubTask(url, "停止")) {
|
||||
return;
|
||||
}
|
||||
AbsSubDLoadUtil 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 AbsSubDLoadUtil getDownloader(String url, boolean needGetFileInfo) {
|
||||
AbsSubDLoadUtil d = mExeLoader.get(url);
|
||||
if (d == null) {
|
||||
return createSubLoader(mCache.get(url), needGetFileInfo);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
@Override public boolean isRunning() {
|
||||
return mState != null && mState.isRunning;
|
||||
}
|
||||
|
||||
@Override public void cancel() {
|
||||
isCancel = true;
|
||||
closeTimer();
|
||||
onPreCancel();
|
||||
|
||||
mSubQueue.removeAllTask();
|
||||
mListener.onCancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* onCancel前的操作
|
||||
*/
|
||||
protected void onPreCancel() {
|
||||
|
||||
}
|
||||
|
||||
@Override public void stop() {
|
||||
isStop = true;
|
||||
closeTimer();
|
||||
if (onPreStop()) {
|
||||
return;
|
||||
}
|
||||
mSubQueue.stopAllTask();
|
||||
}
|
||||
|
||||
/**
|
||||
* onStop前的操作
|
||||
*
|
||||
* @return 返回{@code true},直接回调{@link IDGroupListener#onStop(long)}
|
||||
*/
|
||||
protected boolean onPreStop() {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public void run() {
|
||||
checkComponent();
|
||||
if (isStop || isCancel) {
|
||||
closeTimer();
|
||||
return;
|
||||
}
|
||||
startRunningFlow();
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始进度流程
|
||||
*/
|
||||
private void startRunningFlow() {
|
||||
closeTimer();
|
||||
Looper.prepare();
|
||||
Looper looper = Looper.myLooper();
|
||||
initState(looper);
|
||||
getState().setSubSize(getWrapper().getSubTaskWrapper().size());
|
||||
if (getState().getCompleteNum() == getState().getSubSize()) {
|
||||
mListener.onComplete();
|
||||
return;
|
||||
}
|
||||
|
||||
mListener.onPostPre(mGTWrapper.getEntity().getFileSize());
|
||||
if (mCurrentLocation > 0) {
|
||||
mListener.onResume(mCurrentLocation);
|
||||
} else {
|
||||
mListener.onStart(mCurrentLocation);
|
||||
}
|
||||
startTimer();
|
||||
handlerTask(looper);
|
||||
Looper.loop();
|
||||
}
|
||||
|
||||
private synchronized void startTimer() {
|
||||
mState.isRunning = true;
|
||||
mTimer = new ScheduledThreadPoolExecutor(1);
|
||||
mTimer.scheduleWithFixedDelay(new Runnable() {
|
||||
@Override public void run() {
|
||||
if (!mState.isRunning) {
|
||||
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(AbsSubDLoadUtil 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();
|
||||
}
|
||||
}
|
||||
|
||||
@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(IThreadStateManager threadState) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查组件: {@link #mInfoTask}
|
||||
*/
|
||||
private void checkComponent() {
|
||||
if (mInfoTask == null) {
|
||||
throw new NullPointerException(("文件信息组件为空"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,284 +15,90 @@
|
||||
*/
|
||||
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.IUtil;
|
||||
import com.arialyy.aria.core.listener.IDGroupListener;
|
||||
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;
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Created by AriaL on 2017/6/30.
|
||||
* 任务组核心逻辑
|
||||
*/
|
||||
public abstract class AbsGroupLoaderUtil implements IUtil, Runnable {
|
||||
protected final String TAG = CommonUtil.getClassName(getClass());
|
||||
public abstract class AbsGroupLoaderUtil implements IUtil {
|
||||
|
||||
private long mCurrentLocation = 0;
|
||||
protected IDGroupListener mListener;
|
||||
private ScheduledThreadPoolExecutor mTimer;
|
||||
private long mUpdateInterval;
|
||||
protected String TAG = CommonUtil.getClassName(getClass());
|
||||
private IEventListener mListener;
|
||||
protected AbsGroupLoader mLoader;
|
||||
private AbsTaskWrapper mTaskWrapper;
|
||||
private boolean isStop = false, isCancel = false;
|
||||
private Handler mScheduler;
|
||||
private SimpleSubQueue mSubQueue = SimpleSubQueue.newInstance();
|
||||
private Map<String, AbsSubDLoadUtil> mExeLoader = new WeakHashMap<>();
|
||||
private Map<String, DTaskWrapper> mCache = new WeakHashMap<>();
|
||||
private DGTaskWrapper mGTWrapper;
|
||||
private GroupRunState mState;
|
||||
|
||||
protected AbsGroupLoaderUtil(AbsTaskWrapper groupWrapper, IEventListener listener) {
|
||||
mListener = (IDGroupListener) listener;
|
||||
mGTWrapper = (DGTaskWrapper) groupWrapper;
|
||||
mUpdateInterval = Configuration.getInstance().downloadCfg.getUpdateInterval();
|
||||
initState();
|
||||
protected AbsGroupLoaderUtil(AbsTaskWrapper wrapper, IEventListener listener) {
|
||||
mTaskWrapper = wrapper;
|
||||
mListener = listener;
|
||||
mLoader = getLoader();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建子任务加载器工具
|
||||
*
|
||||
* @param needGetFileInfo {@code true} 需要获取文件信息。{@code false} 不需要获取文件信息
|
||||
*/
|
||||
protected abstract AbsSubDLoadUtil createSubLoader(DTaskWrapper wrapper, boolean needGetFileInfo);
|
||||
protected abstract AbsGroupLoader getLoader();
|
||||
|
||||
protected DGTaskWrapper getWrapper() {
|
||||
return mGTWrapper;
|
||||
protected abstract LoaderStructure buildLoaderStructure();
|
||||
|
||||
public IEventListener getListener() {
|
||||
return mListener;
|
||||
}
|
||||
|
||||
protected GroupRunState getState() {
|
||||
return mState;
|
||||
}
|
||||
|
||||
public Handler getScheduler() {
|
||||
return mScheduler;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化组合任务状态
|
||||
*/
|
||||
private void initState() {
|
||||
mState = new GroupRunState(getWrapper().getKey(), mListener, mSubQueue);
|
||||
for (DTaskWrapper wrapper : mGTWrapper.getSubTaskWrapper()) {
|
||||
File subFile = new File(wrapper.getEntity().getFilePath());
|
||||
if (wrapper.getEntity().getState() == IEntity.STATE_COMPLETE
|
||||
&& subFile.exists()
|
||||
&& subFile.length() == wrapper.getEntity().getFileSize()) {
|
||||
mState.updateCompleteNum();
|
||||
mCurrentLocation += wrapper.getEntity().getFileSize();
|
||||
} else {
|
||||
if (!subFile.exists()) {
|
||||
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.getMainLooper(), SimpleSchedulers.newInstance(mState));
|
||||
public AbsTaskWrapper getTaskWrapper() {
|
||||
return mTaskWrapper;
|
||||
}
|
||||
|
||||
@Override public String getKey() {
|
||||
return mGTWrapper.getKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动子任务下载
|
||||
*
|
||||
* @param url 子任务下载地址
|
||||
*/
|
||||
public void startSubTask(String url) {
|
||||
if (!checkSubTask(url, "开始")) return;
|
||||
if (!mState.isRunning) {
|
||||
startTimer();
|
||||
}
|
||||
AbsSubDLoadUtil d = getDownloader(url);
|
||||
if (d != null && !d.isRunning()) {
|
||||
mSubQueue.startTask(d);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止子任务下载
|
||||
*
|
||||
* @param url 子任务下载地址
|
||||
*/
|
||||
public void stopSubTask(String url) {
|
||||
if (!checkSubTask(url, "停止")) return;
|
||||
AbsSubDLoadUtil d = getDownloader(url);
|
||||
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 AbsSubDLoadUtil getDownloader(String url) {
|
||||
AbsSubDLoadUtil d = mExeLoader.get(url);
|
||||
if (d == null) {
|
||||
return createSubLoader(mCache.get(url), true);
|
||||
}
|
||||
return d;
|
||||
return mTaskWrapper.getKey();
|
||||
}
|
||||
|
||||
@Override public long getFileSize() {
|
||||
return mGTWrapper.getEntity().getFileSize();
|
||||
return mTaskWrapper.getEntity().getFileSize();
|
||||
}
|
||||
|
||||
@Override public long getCurrentLocation() {
|
||||
return mCurrentLocation;
|
||||
return mLoader.getCurrentProgress();
|
||||
}
|
||||
|
||||
@Override public boolean isRunning() {
|
||||
return mState.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;
|
||||
closeTimer();
|
||||
onPreCancel();
|
||||
|
||||
mSubQueue.removeAllTask();
|
||||
mListener.onCancel();
|
||||
mLoader.cancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* onCancel前的操作
|
||||
* 停止下载
|
||||
*/
|
||||
public void onPreCancel() {
|
||||
|
||||
}
|
||||
|
||||
@Override public void stop() {
|
||||
isStop = true;
|
||||
closeTimer();
|
||||
if (onPreStop()) {
|
||||
return;
|
||||
}
|
||||
mSubQueue.stopAllTask();
|
||||
}
|
||||
|
||||
/**
|
||||
* onStop前的操作
|
||||
*
|
||||
* @return 返回{@code true},直接回调{@link IDGroupListener#onStop(long)}
|
||||
*/
|
||||
protected boolean onPreStop() {
|
||||
|
||||
return false;
|
||||
mLoader.stop();
|
||||
}
|
||||
|
||||
@Override public void start() {
|
||||
new Thread(this).start();
|
||||
}
|
||||
|
||||
@Override public void run() {
|
||||
if (isStop || isCancel) {
|
||||
closeTimer();
|
||||
ALog.w(TAG, "启动组合任务失败,任务已停止或已取消");
|
||||
return;
|
||||
}
|
||||
if (onStart()) {
|
||||
startRunningFlow();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理启动前的检查:获取组合任务大小
|
||||
*
|
||||
* @return {@code false} 将不再走后续流程,任务介绍
|
||||
*/
|
||||
protected boolean onStart() {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
synchronized void closeTimer() {
|
||||
if (mTimer != null && !mTimer.isShutdown()) {
|
||||
mTimer.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始进度流程
|
||||
*/
|
||||
private void startRunningFlow() {
|
||||
closeTimer();
|
||||
mListener.onPostPre(mGTWrapper.getEntity().getFileSize());
|
||||
if (mCurrentLocation > 0) {
|
||||
mListener.onResume(mCurrentLocation);
|
||||
} else {
|
||||
mListener.onStart(mCurrentLocation);
|
||||
}
|
||||
startTimer();
|
||||
}
|
||||
|
||||
private synchronized void startTimer() {
|
||||
mState.isRunning = true;
|
||||
mTimer = new ScheduledThreadPoolExecutor(1);
|
||||
mTimer.scheduleWithFixedDelay(new Runnable() {
|
||||
@Override public void run() {
|
||||
if (!mState.isRunning) {
|
||||
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(AbsSubDLoadUtil loader) {
|
||||
mExeLoader.put(loader.getKey(), loader);
|
||||
mSubQueue.startTask(loader);
|
||||
buildLoaderStructure();
|
||||
new Thread(mLoader).start();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,37 +20,42 @@ import com.arialyy.aria.core.download.DTaskWrapper;
|
||||
import com.arialyy.aria.core.download.DownloadEntity;
|
||||
import com.arialyy.aria.core.inf.IUtil;
|
||||
import com.arialyy.aria.core.listener.ISchedulers;
|
||||
import com.arialyy.aria.core.loader.NormalLoader;
|
||||
import com.arialyy.aria.core.loader.LoaderStructure;
|
||||
import com.arialyy.aria.core.loader.SubLoader;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
|
||||
/**
|
||||
* 子任务下载器,负责创建
|
||||
* 子任务下载器工具,需要在线程池中执行
|
||||
*/
|
||||
public abstract class AbsSubDLoadUtil implements IUtil {
|
||||
public abstract class AbsSubDLoadUtil implements IUtil, Runnable {
|
||||
protected final String TAG = CommonUtil.getClassName(getClass());
|
||||
|
||||
private NormalLoader mDLoader;
|
||||
protected SubLoader mDLoader;
|
||||
private DTaskWrapper mWrapper;
|
||||
private Handler mSchedulers;
|
||||
private ChildDLoadListener mListener;
|
||||
private boolean needGetInfo;
|
||||
private boolean isStop = false, isCancel = false;
|
||||
|
||||
/**
|
||||
* @param schedulers 调度器
|
||||
* @param needGetInfo {@code true} 需要获取文件信息。{@code false} 不需要获取文件信息
|
||||
*/
|
||||
protected AbsSubDLoadUtil(Handler schedulers, DTaskWrapper taskWrapper, boolean needGetInfo) {
|
||||
protected AbsSubDLoadUtil(DTaskWrapper taskWrapper, Handler schedulers, boolean needGetInfo) {
|
||||
mWrapper = taskWrapper;
|
||||
mSchedulers = schedulers;
|
||||
this.needGetInfo = needGetInfo;
|
||||
mListener = new ChildDLoadListener(mSchedulers, AbsSubDLoadUtil.this);
|
||||
mDLoader = createLoader(mListener, taskWrapper);
|
||||
mDLoader = getLoader();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建加载器
|
||||
*/
|
||||
protected abstract NormalLoader createLoader(ChildDLoadListener listener, DTaskWrapper wrapper);
|
||||
protected abstract SubLoader getLoader();
|
||||
|
||||
protected abstract LoaderStructure buildLoaderStructure();
|
||||
|
||||
protected boolean isNeedGetInfo() {
|
||||
return needGetInfo;
|
||||
@@ -72,6 +77,27 @@ public abstract class AbsSubDLoadUtil implements IUtil {
|
||||
return mWrapper.getEntity();
|
||||
}
|
||||
|
||||
public ChildDLoadListener getListener() {
|
||||
return mListener;
|
||||
}
|
||||
|
||||
@Override public void run() {
|
||||
if (isStop || isCancel) {
|
||||
return;
|
||||
}
|
||||
mListener.onPre();
|
||||
buildLoaderStructure();
|
||||
mDLoader.run();
|
||||
}
|
||||
|
||||
/**
|
||||
* 请在线程池中使用
|
||||
*/
|
||||
@Deprecated
|
||||
@Override public void start() {
|
||||
throw new AssertionError("请在线程池中使用");
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新开始任务
|
||||
*/
|
||||
@@ -81,16 +107,24 @@ public abstract class AbsSubDLoadUtil implements IUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public NormalLoader getDownloader() {
|
||||
public SubLoader getDownloader() {
|
||||
return mDLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 子任务不实现这个
|
||||
*/
|
||||
@Deprecated
|
||||
@Override public long getFileSize() {
|
||||
return mDLoader == null ? -1 : mDLoader.getFileSize();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 子任务不实现这个
|
||||
*/
|
||||
@Deprecated
|
||||
@Override public long getCurrentLocation() {
|
||||
return mDLoader == null ? -1 : mDLoader.getCurrentLocation();
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override public boolean isRunning() {
|
||||
@@ -98,6 +132,11 @@ public abstract class AbsSubDLoadUtil implements IUtil {
|
||||
}
|
||||
|
||||
@Override public void cancel() {
|
||||
if (isCancel) {
|
||||
ALog.w(TAG, "子任务已取消");
|
||||
return;
|
||||
}
|
||||
isCancel = true;
|
||||
if (mDLoader != null && isRunning()) {
|
||||
mDLoader.cancel();
|
||||
} else {
|
||||
@@ -106,6 +145,11 @@ public abstract class AbsSubDLoadUtil implements IUtil {
|
||||
}
|
||||
|
||||
@Override public void stop() {
|
||||
if (isStop) {
|
||||
ALog.w(TAG, "任务已停止");
|
||||
return;
|
||||
}
|
||||
isStop = true;
|
||||
if (mDLoader != null && isRunning()) {
|
||||
mDLoader.stop();
|
||||
} else {
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
*/
|
||||
package com.arialyy.aria.core.group;
|
||||
|
||||
import com.arialyy.aria.core.loader.AbsLoader;
|
||||
import com.arialyy.aria.core.loader.AbsNormalLoader;
|
||||
import com.arialyy.aria.core.inf.IUtil;
|
||||
import com.arialyy.aria.core.config.DGroupConfig;
|
||||
|
||||
/**
|
||||
* 组合任务子任务队列
|
||||
*
|
||||
* @param <Fileer> {@link AbsLoader}下载器
|
||||
* @param <Fileer> {@link AbsNormalLoader}下载器
|
||||
*/
|
||||
interface ISubQueue<Fileer extends IUtil> {
|
||||
|
||||
|
||||
@@ -16,11 +16,12 @@
|
||||
|
||||
package com.arialyy.aria.core.group;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import com.arialyy.aria.core.AriaConfig;
|
||||
import com.arialyy.aria.core.common.AbsEntity;
|
||||
import com.arialyy.aria.core.config.Configuration;
|
||||
import com.arialyy.aria.core.listener.ISchedulers;
|
||||
import com.arialyy.aria.core.inf.IThreadStateManager;
|
||||
import com.arialyy.aria.core.manager.ThreadTaskManager;
|
||||
import com.arialyy.aria.exception.TaskException;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
@@ -32,7 +33,7 @@ import java.util.concurrent.TimeUnit;
|
||||
* 组合任务子任务调度器,用于调度任务的开始、停止、失败、完成等情况
|
||||
* 该调度器生命周期和{@link AbsGroupLoaderUtil}生命周期一致
|
||||
*/
|
||||
class SimpleSchedulers implements ISchedulers {
|
||||
class SimpleSchedulers implements Handler.Callback {
|
||||
private static final String TAG = "SimpleSchedulers";
|
||||
private SimpleSubQueue mQueue;
|
||||
private GroupRunState mGState;
|
||||
@@ -48,9 +49,16 @@ class SimpleSchedulers implements ISchedulers {
|
||||
}
|
||||
|
||||
@Override public boolean handleMessage(Message msg) {
|
||||
AbsSubDLoadUtil loader = (AbsSubDLoadUtil) msg.obj;
|
||||
// todo key 应该从bundle中获取
|
||||
String key = (String) msg.obj;
|
||||
AbsSubDLoadUtil loader = mQueue.getLoaderUtil(key);
|
||||
if (loader == null) {
|
||||
ALog.e(TAG, "子任务loder不存在,key:" + key);
|
||||
return true;
|
||||
}
|
||||
// todo 处理的是子任务的线程,需要删除 ThreadTaskManager.removeSingleTaskThread 删除线程任务
|
||||
switch (msg.what) {
|
||||
case RUNNING:
|
||||
case IThreadStateManager.STATE_RUNNING:
|
||||
mGState.listener.onSubRunning(loader.getEntity());
|
||||
break;
|
||||
case PRE:
|
||||
@@ -60,13 +68,13 @@ class SimpleSchedulers implements ISchedulers {
|
||||
case START:
|
||||
mGState.listener.onSubStart(loader.getEntity());
|
||||
break;
|
||||
case STOP:
|
||||
case IThreadStateManager.STATE_STOP:
|
||||
handleStop(loader);
|
||||
break;
|
||||
case COMPLETE:
|
||||
case IThreadStateManager.STATE_COMPLETE:
|
||||
handleComplete(loader);
|
||||
break;
|
||||
case FAIL:
|
||||
case IThreadStateManager.STATE_FAIL:
|
||||
handleFail(loader);
|
||||
break;
|
||||
}
|
||||
@@ -128,9 +136,9 @@ class SimpleSchedulers implements ISchedulers {
|
||||
* 1、所有的子任务已经停止,则认为组合任务停止
|
||||
* 2、completeNum + failNum + stopNum = subSize,则认为组合任务停止
|
||||
*/
|
||||
private synchronized void handleStop(AbsSubDLoadUtil loader) {
|
||||
mGState.listener.onSubStop(loader.getEntity());
|
||||
mGState.countStopNum(loader.getKey());
|
||||
private synchronized void handleStop(AbsSubDLoadUtil loadUtil) {
|
||||
mGState.listener.onSubStop(loadUtil.getEntity());
|
||||
mGState.countStopNum(loadUtil.getKey());
|
||||
if (mGState.getStopNum() == mGState.getSubSize()
|
||||
|| mGState.getStopNum()
|
||||
+ mGState.getCompleteNum()
|
||||
|
||||
@@ -61,6 +61,14 @@ class SimpleSubQueue implements ISubQueue<AbsSubDLoadUtil> {
|
||||
return mExec;
|
||||
}
|
||||
|
||||
AbsSubDLoadUtil getLoaderUtil(String key) {
|
||||
AbsSubDLoadUtil sub = mExec.get(key);
|
||||
if (sub != null) {
|
||||
return sub;
|
||||
}
|
||||
return mCache.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存队列大小
|
||||
*/
|
||||
@@ -81,7 +89,7 @@ class SimpleSubQueue implements ISubQueue<AbsSubDLoadUtil> {
|
||||
mCache.remove(fileer.getKey());
|
||||
mExec.put(fileer.getKey(), fileer);
|
||||
ALog.d(TAG, String.format("开始执行子任务:%s", fileer.getEntity().getFileName()));
|
||||
fileer.start();
|
||||
fileer.run();
|
||||
} else {
|
||||
ALog.d(TAG, String.format("执行队列已满,任务进入缓存器中,key: %s", fileer.getKey()));
|
||||
addTask(fileer);
|
||||
|
||||
@@ -23,13 +23,15 @@ import com.arialyy.aria.core.loader.ILoaderComponent;
|
||||
/**
|
||||
* 线程任务状态
|
||||
*/
|
||||
public interface IThreadState extends ILoaderComponent {
|
||||
public interface IThreadStateManager extends ILoaderComponent {
|
||||
int STATE_STOP = 0x01;
|
||||
int STATE_FAIL = 0x02;
|
||||
int STATE_CANCEL = 0x03;
|
||||
int STATE_COMPLETE = 0x04;
|
||||
int STATE_RUNNING = 0x05;
|
||||
int STATE_UPDATE_PROGRESS = 0x06;
|
||||
int STATE_PRE = 0x07;
|
||||
int STATE_START = 0x08;
|
||||
String KEY_RETRY = "KEY_RETRY";
|
||||
String KEY_ERROR_INFO = "KEY_ERROR_INFO";
|
||||
|
||||
@@ -17,7 +17,7 @@ package com.arialyy.aria.core.loader;
|
||||
|
||||
import android.os.Looper;
|
||||
import com.arialyy.aria.core.TaskRecord;
|
||||
import com.arialyy.aria.core.inf.IThreadState;
|
||||
import com.arialyy.aria.core.inf.IThreadStateManager;
|
||||
import com.arialyy.aria.core.listener.IEventListener;
|
||||
import com.arialyy.aria.core.manager.ThreadTaskManager;
|
||||
import com.arialyy.aria.core.task.IThreadTask;
|
||||
@@ -39,9 +39,10 @@ import java.util.concurrent.TimeUnit;
|
||||
* 3、创建文件信息获取器,获取文件信息,根据文件信息执行任务
|
||||
* 4、创建线程任务执行下载、上传操作
|
||||
*/
|
||||
public abstract class AbsLoader implements ILoaderVisitor, ILoader {
|
||||
protected final String TAG;
|
||||
protected IEventListener mListener;
|
||||
public abstract class AbsNormalLoader implements ILoaderVisitor, ILoader {
|
||||
protected final String TAG = CommonUtil.getClassName(getClass());
|
||||
;
|
||||
private IEventListener mListener;
|
||||
protected AbsTaskWrapper mTaskWrapper;
|
||||
protected File mTempFile;
|
||||
|
||||
@@ -53,32 +54,34 @@ public abstract class AbsLoader implements ILoaderVisitor, ILoader {
|
||||
*/
|
||||
private long mUpdateInterval = 1000;
|
||||
protected TaskRecord mRecord;
|
||||
private boolean isCancel = false, isStop = false;
|
||||
protected boolean isCancel = false, isStop = false;
|
||||
private boolean isRuning = false;
|
||||
private Looper mLooper;
|
||||
|
||||
protected IRecordHandler mRecordHandler;
|
||||
protected IThreadState mStateManager;
|
||||
protected IThreadStateManager mStateManager;
|
||||
protected IInfoTask mInfoTask;
|
||||
protected IThreadTaskBuilder mTTBuilder;
|
||||
|
||||
protected AbsLoader(AbsTaskWrapper wrapper, IEventListener listener) {
|
||||
protected AbsNormalLoader(AbsTaskWrapper wrapper, IEventListener listener) {
|
||||
mListener = listener;
|
||||
mTaskWrapper = wrapper;
|
||||
TAG = CommonUtil.getClassName(getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动线程任务
|
||||
*/
|
||||
protected abstract void handleTask();
|
||||
protected abstract void handleTask(Looper looper);
|
||||
|
||||
/**
|
||||
* 获取文件长度
|
||||
*/
|
||||
public abstract long getFileSize();
|
||||
|
||||
public IThreadState getStateManager() {
|
||||
protected IEventListener getListener() {
|
||||
return mListener;
|
||||
}
|
||||
|
||||
protected IThreadStateManager getStateManager() {
|
||||
return mStateManager;
|
||||
}
|
||||
|
||||
@@ -119,22 +122,16 @@ public abstract class AbsLoader implements ILoaderVisitor, ILoader {
|
||||
if (isBreak()) {
|
||||
return;
|
||||
}
|
||||
Looper.prepare();
|
||||
Looper looper = Looper.myLooper();
|
||||
isRuning = true;
|
||||
resetState();
|
||||
onPostPre();
|
||||
handleTask();
|
||||
startTimer();
|
||||
handleTask(looper);
|
||||
Looper.loop();
|
||||
}
|
||||
|
||||
@Override public Looper getLooper() {
|
||||
if (mLooper == null) {
|
||||
Looper.prepare();
|
||||
mLooper = Looper.myLooper();
|
||||
}
|
||||
return mLooper;
|
||||
}
|
||||
|
||||
/**
|
||||
* 预处理完成
|
||||
*/
|
||||
@@ -201,13 +198,14 @@ public abstract class AbsLoader implements ILoaderVisitor, ILoader {
|
||||
mUpdateInterval = interval;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isRunning() {
|
||||
boolean b = ThreadTaskManager.getInstance().taskIsRunning(mTaskWrapper.getKey());
|
||||
//ALog.d(TAG, "isRunning = " + b);
|
||||
return b && isRuning;
|
||||
}
|
||||
|
||||
final public synchronized void cancel() {
|
||||
@Override final public synchronized void cancel() {
|
||||
if (isCancel) {
|
||||
ALog.d(TAG, String.format("任务【%s】正在删除,删除任务失败", mTaskWrapper.getKey()));
|
||||
return;
|
||||
@@ -300,7 +298,7 @@ public abstract class AbsLoader implements ILoaderVisitor, ILoader {
|
||||
/**
|
||||
* 检查组件: {@link #mRecordHandler}、{@link #mInfoTask}、{@link #mStateManager}、{@link #mTTBuilder}
|
||||
*/
|
||||
private void checkComponent() {
|
||||
protected void checkComponent() {
|
||||
if (mRecordHandler == null) {
|
||||
throw new NullPointerException("任务记录组件为空");
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import com.arialyy.aria.core.inf.IUtil;
|
||||
import com.arialyy.aria.core.listener.IEventListener;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.exception.BaseException;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
|
||||
/**
|
||||
@@ -29,7 +30,7 @@ import com.arialyy.aria.util.CommonUtil;
|
||||
public abstract class AbsNormalLoaderUtil implements IUtil {
|
||||
protected String TAG = CommonUtil.getClassName(getClass());
|
||||
private IEventListener mListener;
|
||||
protected AbsLoader mLoader;
|
||||
protected AbsNormalLoader mLoader;
|
||||
private AbsTaskWrapper mTaskWrapper;
|
||||
private boolean isStop = false, isCancel = false;
|
||||
|
||||
@@ -42,12 +43,12 @@ public abstract class AbsNormalLoaderUtil implements IUtil {
|
||||
/**
|
||||
* 获取加载器
|
||||
*/
|
||||
public abstract AbsLoader getLoader();
|
||||
public abstract AbsNormalLoader getLoader();
|
||||
|
||||
/**
|
||||
* 获取构造器
|
||||
*/
|
||||
public abstract LoaderStructure getLoaderStructure();
|
||||
public abstract LoaderStructure BuildLoaderStructure();
|
||||
|
||||
@Override public String getKey() {
|
||||
return mTaskWrapper.getKey();
|
||||
@@ -99,6 +100,7 @@ public abstract class AbsNormalLoaderUtil implements IUtil {
|
||||
*/
|
||||
@Override public void start() {
|
||||
if (isStop || isCancel) {
|
||||
ALog.w(TAG, "启动任务失败,任务已停止或已取消");
|
||||
return;
|
||||
}
|
||||
mListener.onPre();
|
||||
@@ -112,7 +114,7 @@ public abstract class AbsNormalLoaderUtil implements IUtil {
|
||||
// mDownloader.create();
|
||||
//}
|
||||
|
||||
getLoaderStructure();
|
||||
BuildLoaderStructure();
|
||||
new Thread(mLoader).start();
|
||||
|
||||
onStart();
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package com.arialyy.aria.core.loader;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
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.IThreadState;
|
||||
import com.arialyy.aria.core.inf.IThreadStateManager;
|
||||
import com.arialyy.aria.core.task.IThreadTask;
|
||||
import com.arialyy.aria.core.task.IThreadTaskAdapter;
|
||||
import com.arialyy.aria.core.task.ThreadTask;
|
||||
@@ -36,7 +35,7 @@ public abstract class AbsNormalTTBuilder implements IThreadTaskBuilder {
|
||||
mTempFile = new File(((AbsNormalEntity) wrapper.getEntity()).getFilePath());
|
||||
}
|
||||
|
||||
protected File getTempFile(){
|
||||
protected File getTempFile() {
|
||||
return mTempFile;
|
||||
}
|
||||
|
||||
@@ -111,7 +110,7 @@ public abstract class AbsNormalTTBuilder implements IThreadTaskBuilder {
|
||||
long fileLength = getEntity().getFileSize();
|
||||
long blockSize = fileLength / mTotalThreadNum;
|
||||
long currentProgress = 0;
|
||||
List<IThreadTask> threadTasks = new ArrayList<>();
|
||||
List<IThreadTask> threadTasks = new ArrayList<>(mTotalThreadNum);
|
||||
|
||||
mRecord.fileLength = fileLength;
|
||||
if (mWrapper.isNewTask() && !handleNewTask(mRecord, mTotalThreadNum)) {
|
||||
@@ -132,7 +131,7 @@ public abstract class AbsNormalTTBuilder implements IThreadTaskBuilder {
|
||||
if (tr.isComplete) {//该线程已经完成
|
||||
currentProgress += endL - startL;
|
||||
ALog.d(TAG, String.format("任务【%s】线程__%s__已完成", mWrapper.getKey(), i));
|
||||
mStateHandler.obtainMessage(IThreadState.STATE_COMPLETE).sendToTarget();
|
||||
mStateHandler.obtainMessage(IThreadStateManager.STATE_COMPLETE).sendToTarget();
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -162,15 +161,14 @@ public abstract class AbsNormalTTBuilder implements IThreadTaskBuilder {
|
||||
private List<IThreadTask> handleTask() {
|
||||
if (mWrapper.isSupportBP()) {
|
||||
return handleBreakpoint();
|
||||
}else {
|
||||
} else {
|
||||
return handleNoSupportBP();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public List<IThreadTask> buildThreadTask(TaskRecord record, Looper looper,
|
||||
IThreadState stateManager) {
|
||||
@Override public List<IThreadTask> buildThreadTask(TaskRecord record, Handler stateHandler) {
|
||||
mRecord = record;
|
||||
mStateHandler = new Handler(looper, stateManager.getHandlerCallback());
|
||||
mStateHandler = stateHandler;
|
||||
mTotalThreadNum = mRecord.threadNum;
|
||||
return handleTask();
|
||||
}
|
||||
|
||||
@@ -15,12 +15,19 @@
|
||||
*/
|
||||
package com.arialyy.aria.core.loader;
|
||||
|
||||
import android.os.Looper;
|
||||
|
||||
public interface ILoader extends Runnable{
|
||||
public interface ILoader extends Runnable {
|
||||
|
||||
//void start();
|
||||
|
||||
/**
|
||||
* 任务是否在执行
|
||||
*
|
||||
* @return true 任务执行中
|
||||
*/
|
||||
boolean isRunning();
|
||||
|
||||
void cancel();
|
||||
|
||||
void stop();
|
||||
|
||||
/**
|
||||
@@ -33,6 +40,4 @@ public interface ILoader extends Runnable{
|
||||
String getKey();
|
||||
|
||||
long getCurrentProgress();
|
||||
|
||||
Looper getLooper();
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package com.arialyy.aria.core.loader;
|
||||
|
||||
import com.arialyy.aria.core.inf.IThreadState;
|
||||
import com.arialyy.aria.core.inf.IThreadStateManager;
|
||||
|
||||
/**
|
||||
* 加载器访问者
|
||||
@@ -35,7 +35,7 @@ public interface ILoaderVisitor {
|
||||
/**
|
||||
* 线程状态
|
||||
*/
|
||||
void addComponent(IThreadState threadState);
|
||||
void addComponent(IThreadStateManager threadState);
|
||||
|
||||
/**
|
||||
* 构造线程任务
|
||||
|
||||
@@ -44,7 +44,7 @@ public interface IRecordHandler extends ILoaderComponent {
|
||||
/**
|
||||
* 获取任务记录
|
||||
*/
|
||||
TaskRecord getRecord();
|
||||
TaskRecord getRecord(long fileSize);
|
||||
|
||||
/**
|
||||
* 记录处理前的操作,可用来删除任务记录
|
||||
|
||||
@@ -15,9 +15,8 @@
|
||||
*/
|
||||
package com.arialyy.aria.core.loader;
|
||||
|
||||
import android.os.Looper;
|
||||
import android.os.Handler;
|
||||
import com.arialyy.aria.core.TaskRecord;
|
||||
import com.arialyy.aria.core.inf.IThreadState;
|
||||
import com.arialyy.aria.core.task.IThreadTask;
|
||||
import java.util.List;
|
||||
|
||||
@@ -29,10 +28,10 @@ public interface IThreadTaskBuilder extends ILoaderComponent {
|
||||
/**
|
||||
* 构造线程任务
|
||||
*/
|
||||
List<IThreadTask> buildThreadTask(TaskRecord record, Looper looper, IThreadState stateManager);
|
||||
List<IThreadTask> buildThreadTask(TaskRecord record, Handler stateHandler);
|
||||
|
||||
/**
|
||||
* 获取创建的线程任务数,需要先调用{@link #buildThreadTask(TaskRecord, Looper, IThreadState)}方法才能获取创建的线程任务数
|
||||
* 获取创建的线程任务数,需要先调用{@link #buildThreadTask(TaskRecord, Handler)}方法才能获取创建的线程任务数
|
||||
*/
|
||||
int getCreatedThreadNum();
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package com.arialyy.aria.core.loader;
|
||||
|
||||
import com.arialyy.aria.core.inf.IThreadState;
|
||||
import com.arialyy.aria.core.inf.IThreadStateManager;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -33,7 +33,7 @@ public class LoaderStructure {
|
||||
* 将组件加入到集合,必须添加以下集合:
|
||||
* 1 {@link IRecordHandler}
|
||||
* 2 {@link IInfoTask}
|
||||
* 3 {@link IThreadState}
|
||||
* 3 {@link IThreadStateManager}
|
||||
* 4 {@link IThreadTaskBuilder}
|
||||
*
|
||||
* @param component 待添加的组件
|
||||
|
||||
@@ -15,25 +15,28 @@
|
||||
*/
|
||||
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.IThreadState;
|
||||
import com.arialyy.aria.core.inf.IThreadStateManager;
|
||||
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.BaseException;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* 单文件
|
||||
*/
|
||||
public class NormalLoader extends AbsLoader {
|
||||
private int mStartThreadNum; //启动的线程数
|
||||
public class NormalLoader extends AbsNormalLoader {
|
||||
private int startThreadNum; //启动的线程数
|
||||
private boolean isComplete = false;
|
||||
private Looper looper;
|
||||
|
||||
public NormalLoader(AbsTaskWrapper wrapper, IEventListener listener) {
|
||||
super(wrapper, listener);
|
||||
@@ -57,8 +60,8 @@ public class NormalLoader extends AbsLoader {
|
||||
*/
|
||||
protected void setMaxSpeed(int maxSpeed) {
|
||||
for (IThreadTask threadTask : getTaskList()) {
|
||||
if (threadTask != null && mStartThreadNum > 0) {
|
||||
threadTask.setMaxSpeed(maxSpeed / mStartThreadNum);
|
||||
if (threadTask != null && startThreadNum > 0) {
|
||||
threadTask.setMaxSpeed(maxSpeed / startThreadNum);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,8 +73,8 @@ public class NormalLoader extends AbsLoader {
|
||||
|
||||
@Override protected void onPostPre() {
|
||||
super.onPostPre();
|
||||
if (mListener instanceof IDLoadListener) {
|
||||
((IDLoadListener) mListener).onPostPre(getEntity().getFileSize());
|
||||
if (getListener() instanceof IDLoadListener) {
|
||||
((IDLoadListener) getListener()).onPostPre(getEntity().getFileSize());
|
||||
}
|
||||
File file = new File(getEntity().getFilePath());
|
||||
if (file.getParentFile() != null && !file.getParentFile().exists()) {
|
||||
@@ -79,35 +82,38 @@ public class NormalLoader extends AbsLoader {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果使用"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 ? "成功" : "失败"));
|
||||
}
|
||||
}
|
||||
///**
|
||||
// * 如果使用"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 ? "成功" : "失败"));
|
||||
// }
|
||||
//}
|
||||
|
||||
/**
|
||||
* 启动单线程任务
|
||||
*/
|
||||
@Override
|
||||
public void handleTask() {
|
||||
if (isBreak()) {
|
||||
public void handleTask(Looper looper) {
|
||||
if (isBreak() || isComplete) {
|
||||
return;
|
||||
}
|
||||
mStateManager.setLooper(mRecord, getLooper());
|
||||
this.looper = looper;
|
||||
mInfoTask.run();
|
||||
}
|
||||
|
||||
private void startThreadTask() {
|
||||
getTaskList().addAll(mTTBuilder.buildThreadTask(mRecord, getLooper(), mStateManager));
|
||||
mStartThreadNum = mTTBuilder.getCreatedThreadNum();
|
||||
protected void startThreadTask() {
|
||||
mRecord = mRecordHandler.getRecord(getFileSize());
|
||||
mStateManager.setLooper(mRecord, looper);
|
||||
getTaskList().addAll(mTTBuilder.buildThreadTask(mRecord,
|
||||
new Handler(looper, mStateManager.getHandlerCallback())));
|
||||
startThreadNum = mTTBuilder.getCreatedThreadNum();
|
||||
if (mStateManager.getCurrentProgress() > 0) {
|
||||
mListener.onResume(mStateManager.getCurrentProgress());
|
||||
getListener().onResume(mStateManager.getCurrentProgress());
|
||||
} else {
|
||||
mListener.onStart(mStateManager.getCurrentProgress());
|
||||
getListener().onStart(mStateManager.getCurrentProgress());
|
||||
}
|
||||
|
||||
for (IThreadTask threadTask : getTaskList()) {
|
||||
@@ -121,10 +127,10 @@ public class NormalLoader extends AbsLoader {
|
||||
|
||||
@Override public void addComponent(IRecordHandler recordHandler) {
|
||||
mRecordHandler = recordHandler;
|
||||
mRecord = mRecordHandler.getRecord();
|
||||
if (recordHandler.checkTaskCompleted()) {
|
||||
mRecord.deleteData();
|
||||
mListener.onComplete();
|
||||
isComplete = true;
|
||||
getListener().onComplete();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,12 +142,12 @@ public class NormalLoader extends AbsLoader {
|
||||
}
|
||||
|
||||
@Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) {
|
||||
mListener.onFail(needRetry, e);
|
||||
getListener().onFail(needRetry, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override public void addComponent(IThreadState threadState) {
|
||||
@Override public void addComponent(IThreadStateManager threadState) {
|
||||
mStateManager = threadState;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import com.arialyy.aria.core.TaskRecord;
|
||||
import com.arialyy.aria.core.inf.IThreadState;
|
||||
import com.arialyy.aria.core.inf.IThreadStateManager;
|
||||
import com.arialyy.aria.core.listener.IEventListener;
|
||||
import com.arialyy.aria.exception.BaseException;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
@@ -32,7 +32,7 @@ import java.util.List;
|
||||
/**
|
||||
* 线程任务管理器,用于处理多线程下载时任务的状态回调
|
||||
*/
|
||||
public class ThreadStateManager implements IThreadState {
|
||||
public class NormalThreadStateManager implements IThreadStateManager {
|
||||
private final String TAG = "ThreadTaskStateManager";
|
||||
|
||||
/**
|
||||
@@ -51,7 +51,7 @@ public class ThreadStateManager implements IThreadState {
|
||||
/**
|
||||
* @param listener 任务事件
|
||||
*/
|
||||
public ThreadStateManager(IEventListener listener) {
|
||||
public NormalThreadStateManager(IEventListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@@ -220,6 +220,11 @@ public class ThreadStateManager implements IThreadState {
|
||||
* @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));
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* 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.common.AbsEntity;
|
||||
import com.arialyy.aria.core.common.CompleteInfo;
|
||||
import com.arialyy.aria.core.inf.IThreadStateManager;
|
||||
import com.arialyy.aria.core.listener.ISchedulers;
|
||||
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.BaseException;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
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 IThreadTask threadTask;
|
||||
|
||||
public SubLoader(AbsTaskWrapper wrapper, Handler schedulers) {
|
||||
this.wrapper = wrapper;
|
||||
this.schedulers = schedulers;
|
||||
}
|
||||
|
||||
private void handlerTask() {
|
||||
List<IThreadTask> task =
|
||||
ttBuild.buildThreadTask(recordHandler.getRecord(wrapper.getEntity().getFileSize()),
|
||||
schedulers);
|
||||
if (task == null || task.isEmpty()) {
|
||||
ALog.e(TAG, "创建子任务的线程任务失败,key:" + getKey());
|
||||
//schedulers.obtainMessage(ISchedulers.FAIL, SubLoader.this).sendToTarget();
|
||||
return;
|
||||
}
|
||||
threadTask = task.get(0);
|
||||
try {
|
||||
ThreadTaskManager.getInstance().startThread(getKey(), threadTask);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void setNeedGetInfo(boolean needGetInfo) {
|
||||
this.needGetInfo = needGetInfo;
|
||||
}
|
||||
|
||||
public void retryTask() {
|
||||
try {
|
||||
if (threadTask != null) {
|
||||
threadTask.call();
|
||||
} else {
|
||||
ALog.e(TAG, "子任务的线程任务为空");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void stop() {
|
||||
if (isStop) {
|
||||
ALog.w(TAG, "子任务已停止");
|
||||
return;
|
||||
}
|
||||
isStop = true;
|
||||
threadTask.stop();
|
||||
}
|
||||
|
||||
@Override public boolean isRunning() {
|
||||
return !threadTask.isBreak();
|
||||
}
|
||||
|
||||
@Override public void cancel() {
|
||||
if (isCancel) {
|
||||
ALog.w(TAG, "子任务已取消");
|
||||
return;
|
||||
}
|
||||
isCancel = true;
|
||||
threadTask.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;
|
||||
}
|
||||
|
||||
@Override public String getKey() {
|
||||
return wrapper.getKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 子任务不需要实现这个
|
||||
*/
|
||||
@Deprecated
|
||||
@Override public long getCurrentProgress() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@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, BaseException e, boolean needRetry) {
|
||||
schedulers.obtainMessage(ISchedulers.FAIL, SubLoader.this).sendToTarget();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 子任务不需要实现这个
|
||||
*/
|
||||
@Override public void addComponent(IThreadStateManager threadState) {
|
||||
// 子任务不需要实现这个
|
||||
}
|
||||
|
||||
@Override public void addComponent(IThreadTaskBuilder builder) {
|
||||
ttBuild = builder;
|
||||
}
|
||||
|
||||
@Override public void run() {
|
||||
checkComponent();
|
||||
if (isBreak()) {
|
||||
return;
|
||||
}
|
||||
if (needGetInfo) {
|
||||
infoTask.run();
|
||||
} else {
|
||||
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,4 +1,3 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
||||
*
|
||||
@@ -16,14 +15,24 @@
|
||||
*/
|
||||
package com.arialyy.aria.core.loader;
|
||||
|
||||
/**
|
||||
* 用于初始化一些常用的常量
|
||||
*/
|
||||
public final class ConstantIntercept implements ILoaderInterceptor {
|
||||
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 ILoader intercept(Chain chain) {
|
||||
@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,48 +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 com.arialyy.aria.core.common.AbsNormalEntity;
|
||||
import com.arialyy.aria.core.loader.ILoaderAdapter;
|
||||
import com.arialyy.aria.core.wrapper.ITaskWrapper;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* 单文件任务适配器
|
||||
*
|
||||
* @Author lyy
|
||||
* @Date 2019-09-19
|
||||
*/
|
||||
public abstract class AbsNormalLoaderAdapter implements ILoaderAdapter {
|
||||
protected String TAG = CommonUtil.getClassName(getClass());
|
||||
|
||||
private ITaskWrapper mWrapper;
|
||||
private File mTempFile;
|
||||
|
||||
public AbsNormalLoaderAdapter(ITaskWrapper wrapper) {
|
||||
mWrapper = wrapper;
|
||||
mTempFile = new File(((AbsNormalEntity) wrapper.getEntity()).getFilePath());
|
||||
}
|
||||
|
||||
public ITaskWrapper getWrapper() {
|
||||
return mWrapper;
|
||||
}
|
||||
|
||||
public File getTempFile() {
|
||||
return mTempFile;
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
package com.arialyy.aria.core.task;
|
||||
|
||||
import android.os.Bundle;
|
||||
import com.arialyy.aria.core.inf.IThreadState;
|
||||
import com.arialyy.aria.core.inf.IThreadStateManager;
|
||||
import com.arialyy.aria.exception.BaseException;
|
||||
|
||||
/**
|
||||
@@ -30,7 +30,7 @@ public interface IThreadTaskObserver {
|
||||
/**
|
||||
* 更新所有状态
|
||||
*
|
||||
* @param state state {@link IThreadState#STATE_STOP}..
|
||||
* @param state state {@link IThreadStateManager#STATE_STOP}..
|
||||
*/
|
||||
void updateState(int state, Bundle bundle);
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import com.arialyy.aria.core.AriaConfig;
|
||||
import com.arialyy.aria.core.ThreadRecord;
|
||||
import com.arialyy.aria.core.common.SubThreadConfig;
|
||||
import com.arialyy.aria.core.inf.IEntity;
|
||||
import com.arialyy.aria.core.inf.IThreadState;
|
||||
import com.arialyy.aria.core.inf.IThreadStateManager;
|
||||
import com.arialyy.aria.core.listener.ISchedulers;
|
||||
import com.arialyy.aria.core.manager.ThreadTaskManager;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
@@ -168,7 +168,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver {
|
||||
taskBreak = true;
|
||||
if (mTaskWrapper.isSupportBP()) {
|
||||
final long currentTemp = mRangeProgress;
|
||||
updateState(IThreadState.STATE_STOP, null);
|
||||
updateState(IThreadStateManager.STATE_STOP, null);
|
||||
ALog.d(TAG, String.format("任务【%s】thread__%s__中断【停止位置:%s】", getFileName(),
|
||||
mRecord.threadId, currentTemp));
|
||||
writeConfig(false, currentTemp);
|
||||
@@ -238,7 +238,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver {
|
||||
@Override
|
||||
public void stop() {
|
||||
isStop = true;
|
||||
updateState(IThreadState.STATE_STOP, null);
|
||||
updateState(IThreadStateManager.STATE_STOP, null);
|
||||
if (mTaskWrapper.getRequestType() == ITaskWrapper.M3U8_VOD) {
|
||||
writeConfig(false, getConfig().tempFile.length());
|
||||
ALog.i(TAG, String.format("任务【%s】已停止", getFileName()));
|
||||
@@ -258,30 +258,22 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver {
|
||||
/**
|
||||
* 发送状态给状态处理器
|
||||
*
|
||||
* @param state {@link IThreadState#STATE_STOP}..
|
||||
* @param state {@link IThreadStateManager#STATE_STOP}..
|
||||
* @param bundle 而外数据
|
||||
*/
|
||||
@Override
|
||||
public synchronized void updateState(int state, Bundle bundle) {
|
||||
Message msg = mStateHandler.obtainMessage();
|
||||
msg.what = state;
|
||||
if (state != IThreadState.STATE_UPDATE_PROGRESS) {
|
||||
msg.obj = this;
|
||||
}
|
||||
|
||||
if ((state == IThreadState.STATE_COMPLETE || state == IThreadState.STATE_FAIL)
|
||||
&& (mTaskWrapper.getRequestType() == AbsTaskWrapper.M3U8_VOD
|
||||
|| mTaskWrapper.getRequestType() == AbsTaskWrapper.M3U8_LIVE)) {
|
||||
if (bundle == null) {
|
||||
bundle = new Bundle();
|
||||
}
|
||||
bundle.putString(ISchedulers.DATA_M3U8_URL, getConfig().url);
|
||||
bundle.putString(ISchedulers.DATA_M3U8_PEER_PATH, getConfig().tempFile.getPath());
|
||||
bundle.putInt(ISchedulers.DATA_M3U8_PEER_INDEX, getConfig().peerIndex);
|
||||
}
|
||||
msg.what = state;
|
||||
if (bundle != null) {
|
||||
msg.setData(bundle);
|
||||
}
|
||||
int reqType = mTaskWrapper.getRequestType();
|
||||
if (reqType == ITaskWrapper.M3U8_VOD || reqType == ITaskWrapper.M3U8_LIVE) {
|
||||
sendM3U8Info(state, msg);
|
||||
}
|
||||
|
||||
Thread loopThread = mStateHandler.getLooper().getThread();
|
||||
if (!loopThread.isAlive() || loopThread.isInterrupted()) {
|
||||
return;
|
||||
@@ -289,10 +281,25 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver {
|
||||
msg.sendToTarget();
|
||||
}
|
||||
|
||||
private void sendM3U8Info(int state, Message msg) {
|
||||
if (state != IThreadStateManager.STATE_UPDATE_PROGRESS) {
|
||||
msg.obj = this;
|
||||
}
|
||||
Bundle bundle = msg.getData();
|
||||
if ((state == IThreadStateManager.STATE_COMPLETE || state == IThreadStateManager.STATE_FAIL)) {
|
||||
if (bundle == null) {
|
||||
bundle = new Bundle();
|
||||
}
|
||||
bundle.putString(ISchedulers.DATA_M3U8_URL, getConfig().url);
|
||||
bundle.putString(ISchedulers.DATA_M3U8_PEER_PATH, getConfig().tempFile.getPath());
|
||||
bundle.putInt(ISchedulers.DATA_M3U8_PEER_INDEX, getConfig().peerIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public synchronized void updateCompleteState() {
|
||||
ALog.i(TAG, String.format("任务【%s】线程__%s__下载完毕", getTaskWrapper().getKey(), mRecord.threadId));
|
||||
writeConfig(true, mRecord.endLocation);
|
||||
updateState(IThreadState.STATE_COMPLETE, null);
|
||||
updateState(IThreadStateManager.STATE_COMPLETE, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -311,7 +318,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver {
|
||||
if (!loopThread.isAlive() || loopThread.isInterrupted()) {
|
||||
return;
|
||||
}
|
||||
mStateHandler.obtainMessage(IThreadState.STATE_RUNNING, len).sendToTarget();
|
||||
mStateHandler.obtainMessage(IThreadStateManager.STATE_RUNNING, len).sendToTarget();
|
||||
if (System.currentTimeMillis() - mLastSaveTime > 5000
|
||||
&& mRangeProgress < mRecord.endLocation) {
|
||||
mLastSaveTime = System.currentTimeMillis();
|
||||
@@ -332,7 +339,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver {
|
||||
@Override
|
||||
public void cancel() {
|
||||
isCancel = true;
|
||||
updateState(IThreadState.STATE_CANCEL, null);
|
||||
updateState(IThreadStateManager.STATE_CANCEL, null);
|
||||
ALog.d(TAG,
|
||||
String.format("任务【%s】thread__%s__取消", getFileName(), mRecord.threadId));
|
||||
}
|
||||
@@ -396,8 +403,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver {
|
||||
return;
|
||||
}
|
||||
if (mFailTimes < RETRY_NUM && needRetry && (NetUtils.isConnected(
|
||||
AriaConfig.getInstance().getAPP())
|
||||
|| isNotNetRetry) && !isBreak()) {
|
||||
AriaConfig.getInstance().getAPP()) || isNotNetRetry) && !isBreak()) {
|
||||
ALog.w(TAG, String.format("分块【%s】正在重试", getFileName()));
|
||||
mFailTimes++;
|
||||
handleBlockRecord();
|
||||
@@ -435,7 +441,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver {
|
||||
} else if (blockFileLen < mRecord.blockLen) {
|
||||
mRecord.startLocation = mRecord.endLocation - mRecord.blockLen + blockFileLen;
|
||||
mRecord.isComplete = false;
|
||||
updateState(IThreadState.STATE_UPDATE_PROGRESS, null);
|
||||
updateState(IThreadStateManager.STATE_UPDATE_PROGRESS, null);
|
||||
ALog.i(TAG,
|
||||
String.format("修正分块【%s】记录,开始位置:%s,结束位置:%s", temp.getName(), mRecord.startLocation,
|
||||
mRecord.endLocation));
|
||||
@@ -453,12 +459,12 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver {
|
||||
*/
|
||||
private void sendFailMsg(BaseException e, boolean needRetry) {
|
||||
Bundle b = new Bundle();
|
||||
b.putBoolean(IThreadState.KEY_RETRY, needRetry);
|
||||
b.putBoolean(IThreadStateManager.KEY_RETRY, needRetry);
|
||||
if (e != null) {
|
||||
b.putSerializable(IThreadState.KEY_ERROR_INFO, e);
|
||||
updateState(IThreadState.STATE_FAIL, b);
|
||||
b.putSerializable(IThreadStateManager.KEY_ERROR_INFO, e);
|
||||
updateState(IThreadStateManager.STATE_FAIL, b);
|
||||
} else {
|
||||
updateState(IThreadState.STATE_FAIL, b);
|
||||
updateState(IThreadStateManager.STATE_FAIL, b);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ class DBConfig {
|
||||
static boolean DEBUG = false;
|
||||
static Map<String, Class<? extends DbEntity>> mapping = new LinkedHashMap<>();
|
||||
static String DB_NAME;
|
||||
static int VERSION = 57;
|
||||
static int VERSION = 58;
|
||||
|
||||
/**
|
||||
* 是否将数据库保存在Sd卡,{@code true} 是
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.arialyy.aria.util.ALog;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -163,7 +164,49 @@ final class SqlHelper extends SQLiteOpenHelper {
|
||||
for (String tableName : tables) {
|
||||
Class<? extends DbEntity> clazz = DBConfig.mapping.get(tableName);
|
||||
if (SqlUtil.tableExists(db, clazz)) {
|
||||
//修改表名为中介表名
|
||||
// ----------- 1、获取旧表字段、新表字段
|
||||
Cursor columnC =
|
||||
db.rawQuery(String.format("PRAGMA table_info(%s)", tableName), null);
|
||||
|
||||
// 获取新表的所有字段名称
|
||||
List<String> newTabColumns = SqlUtil.getColumns(clazz);
|
||||
// 获取旧表的所有字段名称
|
||||
List<String> oldTabColumns = new ArrayList<>();
|
||||
|
||||
while (columnC.moveToNext()) {
|
||||
String columnName = columnC.getString(columnC.getColumnIndex("name"));
|
||||
oldTabColumns.add(columnName);
|
||||
}
|
||||
columnC.close();
|
||||
|
||||
// ----------- 2、为防止字段增加失败的情况,先给旧表增加字段
|
||||
List<String> newAddColum = getNewColumn(newTabColumns, oldTabColumns);
|
||||
// 删除重命名的字段
|
||||
Map<String, String > modifyMap = null;
|
||||
if (modifyColumns != null){
|
||||
modifyMap = modifyColumns.get(tableName);
|
||||
if (modifyMap != null){
|
||||
Iterator<String> it = newAddColum.iterator();
|
||||
while (it.hasNext()){
|
||||
String s = it.next();
|
||||
if (modifyMap.get(s) != null){
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 给旧表增加字段,防止新增字段失败
|
||||
if (newAddColum.size() > 0){
|
||||
String sql = "ALTER TABLE %s ADD COLUMN %s %s";
|
||||
for (String nc : newAddColum){
|
||||
String temp = String.format(sql, tableName, nc, SqlUtil.getColumnTypeByFieldName(clazz, nc));
|
||||
ALog.d(TAG, "添加表字段的sql:" + temp);
|
||||
db.execSQL(temp);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------- 3、将旧表备份下,并创建新表
|
||||
String alertSql = String.format("ALTER TABLE %s RENAME TO %s_temp", tableName, tableName);
|
||||
db.execSQL(alertSql);
|
||||
|
||||
@@ -176,28 +219,15 @@ final class SqlHelper extends SQLiteOpenHelper {
|
||||
long count = cursor.getLong(0);
|
||||
cursor.close();
|
||||
|
||||
// 复制数据
|
||||
// ----------- 4、将旧表数据复制到新表
|
||||
if (count > 0) {
|
||||
Cursor columnC =
|
||||
db.rawQuery(String.format("PRAGMA table_info(%s_temp)", tableName), null);
|
||||
|
||||
// 获取新表的所有字段名称
|
||||
List<String> newTabColumns = SqlUtil.getColumns(clazz);
|
||||
// 获取旧表的所有字段名称
|
||||
List<String> oldTabColumns = new ArrayList<>();
|
||||
|
||||
while (columnC.moveToNext()) {
|
||||
String columnName = columnC.getString(columnC.getColumnIndex("name"));
|
||||
oldTabColumns.add(columnName);
|
||||
}
|
||||
columnC.close();
|
||||
|
||||
// 旧表需要删除的字段,删除旧表有而新表没的字段
|
||||
List<String> diffTab = getDiffColumn(newTabColumns, oldTabColumns);
|
||||
StringBuilder params = new StringBuilder();
|
||||
|
||||
// 需要修改的列名映射表
|
||||
Map<String, String> modifyMap = null;
|
||||
//Map<String, String> modifyMap = null;
|
||||
if (modifyColumns != null) {
|
||||
modifyMap = modifyColumns.get(tableName);
|
||||
}
|
||||
@@ -227,11 +257,11 @@ final class SqlHelper extends SQLiteOpenHelper {
|
||||
String insertSql =
|
||||
String.format("INSERT INTO %s (%s) SELECT %s FROM %s_temp", tableName, newParamStr,
|
||||
oldParamStr, tableName);
|
||||
ALog.d(TAG, "insertSql = " + insertSql);
|
||||
ALog.d(TAG, "恢复数据的sql:" + insertSql);
|
||||
|
||||
db.execSQL(insertSql);
|
||||
}
|
||||
//删除中介表
|
||||
// ----------- 5、删除备份的表
|
||||
SqlUtil.dropTable(db, tableName + "_temp");
|
||||
} else {
|
||||
SqlUtil.createTable(db, clazz);
|
||||
@@ -258,6 +288,19 @@ final class SqlHelper extends SQLiteOpenHelper {
|
||||
return temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取新增字段
|
||||
*
|
||||
* @param newTab 新表字段
|
||||
* @param oldTab 就表字段
|
||||
* @return 新表有而旧表没的字段
|
||||
*/
|
||||
private List<String> getNewColumn(List<String> newTab, List<String> oldTab) {
|
||||
List<String> temp = new ArrayList<>(newTab);
|
||||
temp.removeAll(oldTab);
|
||||
return temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给TaskRecord 增加任务类型
|
||||
*/
|
||||
|
||||
@@ -240,28 +240,13 @@ final class SqlUtil {
|
||||
continue;
|
||||
}
|
||||
Class<?> type = field.getType();
|
||||
sb.append(field.getName());
|
||||
if (type == String.class || type.isEnum()) {
|
||||
sb.append(" VARCHAR");
|
||||
} else if (type == int.class || type == Integer.class) {
|
||||
sb.append(" INTEGER");
|
||||
} else if (type == float.class || type == Float.class) {
|
||||
sb.append(" FLOAT");
|
||||
} else if (type == double.class || type == Double.class) {
|
||||
sb.append(" DOUBLE");
|
||||
} else if (type == long.class || type == Long.class) {
|
||||
sb.append(" BIGINT");
|
||||
} else if (type == boolean.class || type == Boolean.class) {
|
||||
sb.append(" BOOLEAN");
|
||||
} else if (type == java.util.Date.class || type == java.sql.Date.class) {
|
||||
sb.append(" DATA");
|
||||
} else if (type == byte.class || type == Byte.class) {
|
||||
sb.append(" BLOB");
|
||||
} else if (type == Map.class || type == List.class) {
|
||||
sb.append(" TEXT");
|
||||
} else {
|
||||
String columnType = getColumnType(type);
|
||||
if (columnType == null) {
|
||||
continue;
|
||||
}
|
||||
sb.append(field.getName());
|
||||
sb.append(" ").append(columnType);
|
||||
|
||||
if (SqlUtil.isPrimary(field)) {
|
||||
Primary pk = field.getAnnotation(Primary.class);
|
||||
sb.append(" PRIMARY KEY");
|
||||
@@ -315,10 +300,51 @@ final class SqlUtil {
|
||||
|
||||
String str = sb.toString();
|
||||
str = str.substring(0, str.length() - 1) + ");";
|
||||
ALog.d(TAG, "创建表的sql:" + str);
|
||||
db.execSQL(str);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字段名获取字段类型
|
||||
*/
|
||||
static String getColumnTypeByFieldName(Class tabClass, String fieldName) {
|
||||
List<Field> fields = CommonUtil.getAllFields(tabClass);
|
||||
for (Field field : fields) {
|
||||
if (field.getName().equals(fieldName)) {
|
||||
return getColumnType(field.getType());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字段类型
|
||||
*/
|
||||
static String getColumnType(Class fieldtype) {
|
||||
if (fieldtype == String.class || fieldtype.isEnum()) {
|
||||
return "VARCHAR";
|
||||
} else if (fieldtype == int.class || fieldtype == Integer.class) {
|
||||
return "INTEGER";
|
||||
} else if (fieldtype == float.class || fieldtype == Float.class) {
|
||||
return "FLOAT";
|
||||
} else if (fieldtype == double.class || fieldtype == Double.class) {
|
||||
return "DOUBLE";
|
||||
} else if (fieldtype == long.class || fieldtype == Long.class) {
|
||||
return "BIGINT";
|
||||
} else if (fieldtype == boolean.class || fieldtype == Boolean.class) {
|
||||
return "BOOLEAN";
|
||||
} else if (fieldtype == java.util.Date.class || fieldtype == java.sql.Date.class) {
|
||||
return "DATA";
|
||||
} else if (fieldtype == byte.class || fieldtype == Byte.class) {
|
||||
return "BLOB";
|
||||
} else if (fieldtype == Map.class || fieldtype == List.class) {
|
||||
return "TEXT";
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* URL编码字符串
|
||||
*
|
||||
|
||||
@@ -121,10 +121,10 @@ public class ComponentUtil {
|
||||
className = "com.arialyy.aria.http.upload.HttpULoaderUtil";
|
||||
break;
|
||||
case ITaskWrapper.D_FTP_DIR:
|
||||
className = "com.arialyy.aria.ftp.download.FtpDirDLoaderUtil";
|
||||
className = "com.arialyy.aria.ftp.download.FtpDGLoaderUtil";
|
||||
break;
|
||||
case ITaskWrapper.DG_HTTP:
|
||||
className = "com.arialyy.aria.http.download.DGroupLoaderUtil";
|
||||
className = "com.arialyy.aria.http.download.HttpDGLoaderUtil";
|
||||
break;
|
||||
}
|
||||
if (className == null) {
|
||||
|
||||
@@ -375,76 +375,25 @@ public class FileUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查SD内存空间是否充足
|
||||
* 检查内存空间是否充足
|
||||
*
|
||||
* @param filePath 文件保存路径
|
||||
* @param fileSize 文件大小
|
||||
* @return {@code false} 内存空间不足,{@code true}内存空间足够
|
||||
* @param path 文件路径
|
||||
* @param fileSize 下载的文件的大小
|
||||
* @return true 空间足够
|
||||
*/
|
||||
public static boolean checkSDMemorySpace(String filePath, long fileSize) {
|
||||
List<String> dirs = FileUtil.getSDPathList(AriaConfig.getInstance().getAPP());
|
||||
if (dirs == null || dirs.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
for (String path : dirs) {
|
||||
if (filePath.contains(path)) {
|
||||
if (fileSize > 0 && fileSize > getAvailableExternalMemorySize(path)) {
|
||||
return false;
|
||||
}
|
||||
public static boolean checkMemorySpace(String path, long fileSize) {
|
||||
File temp = new File(path);
|
||||
if (!temp.exists()){
|
||||
if (!temp.getParentFile().exists()){
|
||||
FileUtil.createDir(temp.getParentFile().getPath());
|
||||
}
|
||||
path = temp.getParentFile().getPath();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* sdcard 可用大小
|
||||
*
|
||||
* @param sdcardPath sdcard 根路径
|
||||
* @return 单位为:byte
|
||||
*/
|
||||
public static long getAvailableExternalMemorySize(String sdcardPath) {
|
||||
StatFs stat = new StatFs(sdcardPath);
|
||||
StatFs stat = new StatFs(path);
|
||||
long blockSize = stat.getBlockSize();
|
||||
long availableBlocks = stat.getAvailableBlocks();
|
||||
return availableBlocks * blockSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* sdcard 总大小
|
||||
*
|
||||
* @param sdcardPath sdcard 根路径
|
||||
* @return 单位为:byte
|
||||
*/
|
||||
public static long getTotalExternalMemorySize(String sdcardPath) {
|
||||
StatFs stat = new StatFs(sdcardPath);
|
||||
long blockSize = stat.getBlockSize();
|
||||
long totalBlocks = stat.getBlockCount();
|
||||
return totalBlocks * blockSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取SD卡目录列表
|
||||
*/
|
||||
public static List<String> getSDPathList(Context context) {
|
||||
List<String> paths = null;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
try {
|
||||
paths = getVolumeList(context);
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
List<String> mounts = readMountsFile();
|
||||
List<String> volds = readVoldFile();
|
||||
paths = compareMountsWithVold(mounts, volds);
|
||||
}
|
||||
return paths;
|
||||
return fileSize <= availableBlocks * blockSize;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -495,11 +444,83 @@ public class FileUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查SD内存空间是否充足
|
||||
*
|
||||
* @param filePath 文件保存路径
|
||||
* @param fileSize 文件大小
|
||||
* @return {@code false} 内存空间不足,{@code true}内存空间足够
|
||||
*/
|
||||
@Deprecated
|
||||
private static boolean checkSDMemorySpace(String filePath, long fileSize) {
|
||||
List<String> dirs = FileUtil.getSDPathList(AriaConfig.getInstance().getAPP());
|
||||
if (dirs == null || dirs.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
for (String path : dirs) {
|
||||
if (filePath.contains(path)) {
|
||||
if (fileSize > 0 && fileSize > getAvailableExternalMemorySize(path)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* sdcard 可用大小
|
||||
*
|
||||
* @param sdcardPath sdcard 根路径
|
||||
* @return 单位为:byte
|
||||
*/
|
||||
private static long getAvailableExternalMemorySize(String sdcardPath) {
|
||||
StatFs stat = new StatFs(sdcardPath);
|
||||
long blockSize = stat.getBlockSize();
|
||||
long availableBlocks = stat.getAvailableBlocks();
|
||||
return availableBlocks * blockSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* sdcard 总大小
|
||||
*
|
||||
* @param sdcardPath sdcard 根路径
|
||||
* @return 单位为:byte
|
||||
*/
|
||||
private static long getTotalExternalMemorySize(String sdcardPath) {
|
||||
StatFs stat = new StatFs(sdcardPath);
|
||||
long blockSize = stat.getBlockSize();
|
||||
long totalBlocks = stat.getBlockCount();
|
||||
return totalBlocks * blockSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取SD卡目录列表
|
||||
*/
|
||||
private static List<String> getSDPathList(Context context) {
|
||||
List<String> paths = null;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
try {
|
||||
paths = getVolumeList(context);
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
List<String> mounts = readMountsFile();
|
||||
List<String> volds = readVoldFile();
|
||||
paths = compareMountsWithVold(mounts, volds);
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* getSDPathList
|
||||
*/
|
||||
private static List<String> getVolumeList(final Context context)
|
||||
throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException,
|
||||
throws NoSuchMethodException, InvocationTargetException,
|
||||
IllegalAccessException {
|
||||
List<String> pathList = new ArrayList<>();
|
||||
|
||||
@@ -589,7 +610,7 @@ public class FileUtil {
|
||||
*
|
||||
* @return paths to all available SD-Cards in the system (include emulated)
|
||||
*/
|
||||
public static List<String> getStorageDirectories() {
|
||||
private static List<String> getStorageDirectories() {
|
||||
// Final set of paths
|
||||
final List<String> rv = new ArrayList<>();
|
||||
// Primary physical SD-CARD (not emulated)
|
||||
|
||||
Reference in New Issue
Block a user