loader 重构

This commit is contained in:
laoyuyu
2020-01-01 14:43:52 +08:00
170 changed files with 2862 additions and 21661 deletions

View File

@@ -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;
}
/**
* 获取任务路径
*

View File

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

View File

@@ -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(("文件信息组件为空"));
}
}
}

View File

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

View File

@@ -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 {

View File

@@ -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> {

View File

@@ -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()

View File

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

View File

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

View File

@@ -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("任务记录组件为空");
}

View File

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

View File

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

View File

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

View File

@@ -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);
/**
* 构造线程任务

View File

@@ -44,7 +44,7 @@ public interface IRecordHandler extends ILoaderComponent {
/**
* 获取任务记录
*/
TaskRecord getRecord();
TaskRecord getRecord(long fileSize);
/**
* 记录处理前的操作,可用来删除任务记录

View File

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

View File

@@ -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 待添加的组件

View File

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

View File

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

View File

@@ -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("线程任务组件为空");
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 增加任务类型
*/

View File

@@ -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编码字符串
*

View File

@@ -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) {

View File

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