分块下载实现
This commit is contained in:
@@ -28,10 +28,10 @@ import org.xml.sax.helpers.DefaultHandler;
|
||||
class ConfigHelper extends DefaultHandler {
|
||||
private final String TAG = "ConfigHelper";
|
||||
|
||||
private boolean isDownloadConfig = false, isUploadConfig = false, isAppConfig = false;
|
||||
private Configuration.DownloadConfig mDownloadConfig = Configuration.DownloadConfig.getInstance();
|
||||
private Configuration.UploadConfig mUploadConfig = Configuration.UploadConfig.getInstance();
|
||||
private Configuration.AppConfig mAppConfig = Configuration.AppConfig.getInstance();
|
||||
private @ConfigType int mType;
|
||||
|
||||
@Override public void startDocument() throws SAXException {
|
||||
super.startDocument();
|
||||
@@ -41,21 +41,19 @@ class ConfigHelper extends DefaultHandler {
|
||||
public void startElement(String uri, String localName, String qName, Attributes attributes)
|
||||
throws SAXException {
|
||||
super.startElement(uri, localName, qName, attributes);
|
||||
if (qName.equals("download")) {
|
||||
isDownloadConfig = true;
|
||||
isUploadConfig = false;
|
||||
isAppConfig = false;
|
||||
} else if (qName.equals("upload")) {
|
||||
isUploadConfig = true;
|
||||
isDownloadConfig = false;
|
||||
isAppConfig = false;
|
||||
} else if (qName.equals("app")) {
|
||||
isUploadConfig = false;
|
||||
isDownloadConfig = false;
|
||||
isAppConfig = true;
|
||||
switch (qName) {
|
||||
case "download":
|
||||
mType = ConfigType.DOWNLOAD;
|
||||
break;
|
||||
case "upload":
|
||||
mType = ConfigType.UPLOAD;
|
||||
break;
|
||||
case "app":
|
||||
mType = ConfigType.APP;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isDownloadConfig || isUploadConfig) {
|
||||
if (mType == ConfigType.DOWNLOAD || mType == ConfigType.UPLOAD) {
|
||||
|
||||
String value = attributes.getValue("value");
|
||||
switch (qName) {
|
||||
@@ -100,8 +98,11 @@ class ConfigHelper extends DefaultHandler {
|
||||
case "notNetRetry":
|
||||
loadNotNetRetry(value);
|
||||
break;
|
||||
case "useBlock":
|
||||
loadUseBlock(value);
|
||||
break;
|
||||
}
|
||||
} else if (isAppConfig) {
|
||||
} else if (mType == ConfigType.APP) {
|
||||
String value = attributes.getValue("value");
|
||||
switch (qName) {
|
||||
case "useAriaCrashHandler":
|
||||
@@ -114,11 +115,17 @@ class ConfigHelper extends DefaultHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private void loadUseBlock(String value) {
|
||||
if (mType == ConfigType.DOWNLOAD) {
|
||||
mDownloadConfig.useBlock = checkBoolean(value) ? Boolean.valueOf(value) : false;
|
||||
}
|
||||
}
|
||||
|
||||
private void loadNotNetRetry(String value) {
|
||||
if (isDownloadConfig) {
|
||||
if (mType == ConfigType.DOWNLOAD) {
|
||||
mDownloadConfig.notNetRetry = checkBoolean(value) ? Boolean.valueOf(value) : false;
|
||||
}
|
||||
if (isUploadConfig) {
|
||||
if (mType == ConfigType.UPLOAD) {
|
||||
mUploadConfig.notNetRetry = checkBoolean(value) ? Boolean.valueOf(value) : false;
|
||||
}
|
||||
}
|
||||
@@ -150,10 +157,10 @@ class ConfigHelper extends DefaultHandler {
|
||||
|
||||
private void loadUpdateInterval(String value) {
|
||||
long temp = checkLong(value) ? Long.parseLong(value) : 1000;
|
||||
if (isDownloadConfig) {
|
||||
if (mType == ConfigType.DOWNLOAD) {
|
||||
mDownloadConfig.updateInterval = temp;
|
||||
}
|
||||
if (isUploadConfig) {
|
||||
if (mType == ConfigType.UPLOAD) {
|
||||
mUploadConfig.updateInterval = temp;
|
||||
}
|
||||
}
|
||||
@@ -164,17 +171,17 @@ class ConfigHelper extends DefaultHandler {
|
||||
"wait"))) {
|
||||
mod = value;
|
||||
}
|
||||
if (isDownloadConfig) {
|
||||
if (mType == ConfigType.DOWNLOAD) {
|
||||
mDownloadConfig.queueMod = mod;
|
||||
}
|
||||
if (isUploadConfig) {
|
||||
if (mType == ConfigType.UPLOAD) {
|
||||
mUploadConfig.queueMod = mod;
|
||||
}
|
||||
}
|
||||
|
||||
private void loadMaxSpeed(String value) {
|
||||
int maxSpeed = checkInt(value) ? Integer.parseInt(value) : 0;
|
||||
if (isDownloadConfig) {
|
||||
if (mType == ConfigType.DOWNLOAD) {
|
||||
mDownloadConfig.maxSpeed = maxSpeed;
|
||||
}
|
||||
}
|
||||
@@ -185,10 +192,10 @@ class ConfigHelper extends DefaultHandler {
|
||||
open = Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
if (isDownloadConfig) {
|
||||
if (mType == ConfigType.DOWNLOAD) {
|
||||
mDownloadConfig.isConvertSpeed = open;
|
||||
}
|
||||
if (isUploadConfig) {
|
||||
if (mType == ConfigType.UPLOAD) {
|
||||
mUploadConfig.isConvertSpeed = open;
|
||||
}
|
||||
}
|
||||
@@ -200,13 +207,13 @@ class ConfigHelper extends DefaultHandler {
|
||||
time = 2 * 1000;
|
||||
}
|
||||
|
||||
if (isDownloadConfig) {
|
||||
if (mType == ConfigType.DOWNLOAD) {
|
||||
mDownloadConfig.reTryInterval = time;
|
||||
}
|
||||
}
|
||||
|
||||
private void loadCA(String name, String path) {
|
||||
if (isDownloadConfig) {
|
||||
if (mType == ConfigType.DOWNLOAD) {
|
||||
mDownloadConfig.caName = name;
|
||||
mDownloadConfig.caPath = path;
|
||||
}
|
||||
@@ -219,11 +226,11 @@ class ConfigHelper extends DefaultHandler {
|
||||
buffSize = 2048;
|
||||
}
|
||||
|
||||
if (isDownloadConfig) {
|
||||
if (mType == ConfigType.DOWNLOAD) {
|
||||
mDownloadConfig.buffSize = buffSize;
|
||||
}
|
||||
|
||||
if (isUploadConfig) {
|
||||
if (mType == ConfigType.UPLOAD) {
|
||||
mUploadConfig.buffSize = buffSize;
|
||||
}
|
||||
}
|
||||
@@ -235,11 +242,11 @@ class ConfigHelper extends DefaultHandler {
|
||||
time = 10 * 1000;
|
||||
}
|
||||
|
||||
if (isDownloadConfig) {
|
||||
if (mType == ConfigType.DOWNLOAD) {
|
||||
mDownloadConfig.iOTimeOut = time;
|
||||
}
|
||||
|
||||
if (isUploadConfig) {
|
||||
if (mType == ConfigType.UPLOAD) {
|
||||
mUploadConfig.iOTimeOut = time;
|
||||
}
|
||||
}
|
||||
@@ -247,10 +254,10 @@ class ConfigHelper extends DefaultHandler {
|
||||
private void loadConnectTime(String value) {
|
||||
int time = checkInt(value) ? Integer.parseInt(value) : 5 * 1000;
|
||||
|
||||
if (isDownloadConfig) {
|
||||
if (mType == ConfigType.DOWNLOAD) {
|
||||
mDownloadConfig.connectTimeOut = time;
|
||||
}
|
||||
if (isUploadConfig) {
|
||||
if (mType == ConfigType.UPLOAD) {
|
||||
mUploadConfig.connectTimeOut = time;
|
||||
}
|
||||
}
|
||||
@@ -258,10 +265,10 @@ class ConfigHelper extends DefaultHandler {
|
||||
private void loadReTry(String value) {
|
||||
int num = checkInt(value) ? Integer.parseInt(value) : 0;
|
||||
|
||||
if (isDownloadConfig) {
|
||||
if (mType == ConfigType.DOWNLOAD) {
|
||||
mDownloadConfig.reTryNum = num;
|
||||
}
|
||||
if (isUploadConfig) {
|
||||
if (mType == ConfigType.UPLOAD) {
|
||||
mUploadConfig.reTryNum = num;
|
||||
}
|
||||
}
|
||||
@@ -272,10 +279,10 @@ class ConfigHelper extends DefaultHandler {
|
||||
ALog.w(TAG, "任务队列数不能小于 1");
|
||||
num = 2;
|
||||
}
|
||||
if (isDownloadConfig) {
|
||||
if (mType == ConfigType.DOWNLOAD) {
|
||||
mDownloadConfig.maxTaskNum = num;
|
||||
}
|
||||
if (isUploadConfig) {
|
||||
if (mType == ConfigType.UPLOAD) {
|
||||
mUploadConfig.maxTaskNum = num;
|
||||
}
|
||||
}
|
||||
@@ -286,7 +293,7 @@ class ConfigHelper extends DefaultHandler {
|
||||
ALog.e(TAG, "下载线程数不能小于 1");
|
||||
num = 1;
|
||||
}
|
||||
if (isDownloadConfig) {
|
||||
if (mType == ConfigType.DOWNLOAD) {
|
||||
mDownloadConfig.threadNum = num;
|
||||
}
|
||||
}
|
||||
|
||||
31
Aria/src/main/java/com/arialyy/aria/core/ConfigType.java
Normal file
31
Aria/src/main/java/com/arialyy/aria/core/ConfigType.java
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import android.support.annotation.IntDef;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@IntDef({
|
||||
ConfigType.DOWNLOAD,
|
||||
ConfigType.UPLOAD,
|
||||
ConfigType.APP
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE) @interface ConfigType {
|
||||
int DOWNLOAD = 1;
|
||||
int UPLOAD = 2;
|
||||
int APP = 3;
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package com.arialyy.aria.core.common;
|
||||
import android.content.Context;
|
||||
import com.arialyy.aria.core.AriaManager;
|
||||
import com.arialyy.aria.core.download.DownloadEntity;
|
||||
import com.arialyy.aria.core.download.DownloadTaskEntity;
|
||||
import com.arialyy.aria.core.inf.AbsNormalEntity;
|
||||
import com.arialyy.aria.core.inf.AbsTaskEntity;
|
||||
import com.arialyy.aria.core.inf.IDownloadListener;
|
||||
@@ -47,6 +48,10 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte
|
||||
implements Runnable, IUtil {
|
||||
private static final String STATE = "_state_";
|
||||
private static final String RECORD = "_record_";
|
||||
/**
|
||||
* 分块文件路径
|
||||
*/
|
||||
public static final String SUB_PATH = "%s.%s.part";
|
||||
/**
|
||||
* 小于1m的文件不启用多线程
|
||||
*/
|
||||
@@ -74,7 +79,7 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte
|
||||
* 进度刷新间隔
|
||||
*/
|
||||
private long mUpdateInterval = 1000;
|
||||
private TaskRecord mRecord;
|
||||
protected TaskRecord mRecord;
|
||||
|
||||
protected AbsFileer(IEventListener listener, TASK_ENTITY taskEntity) {
|
||||
mListener = listener;
|
||||
@@ -117,23 +122,36 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte
|
||||
if (!mTaskEntity.isSupportBP()) {
|
||||
mTotalThreadNum = 1;
|
||||
mStartThreadNum = 1;
|
||||
handleNoSupportBP();
|
||||
} else {
|
||||
mTotalThreadNum =
|
||||
mTaskEntity.isNewTask() ? (mStartThreadNum = setNewTaskThreadNum()) : mTotalThreadNum;
|
||||
handleBreakpoint();
|
||||
}
|
||||
mConstance.START_THREAD_NUM = mTotalThreadNum;
|
||||
|
||||
// 处理分块和动态文件参数
|
||||
if (mTaskEntity.isNewTask() && mTaskEntity instanceof DownloadTaskEntity) {
|
||||
AriaManager manager = AriaManager.getInstance(AriaManager.APP);
|
||||
mRecord.isBlock = mTotalThreadNum > 1 && manager.getDownloadConfig().isUseBlock();
|
||||
// 线程数不等1并且没有使用块下载,则认为没有使用动态文件
|
||||
mRecord.isOpenDynamicFile = mTotalThreadNum == 1 || mRecord.isBlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* mTaskEntity.getEntity().getFileSize() != mTempFile.length()为兼容以前老版本代码
|
||||
* 动态长度条件:
|
||||
* 1、总线程数为1,并且是新任务
|
||||
* 2、总线程数为1,不是新任务,但是长度不是文件全长度
|
||||
*/
|
||||
if (mTotalThreadNum == 1 && (mTaskEntity.isNewTask()
|
||||
|| mTaskEntity.getEntity().getFileSize() != mTempFile.length())) {
|
||||
mConstance.isOpenDynamicFile = true;
|
||||
if (mTotalThreadNum == 1 && (mTaskEntity.getEntity().getFileSize() != mTempFile.length())) {
|
||||
mRecord.isOpenDynamicFile = true;
|
||||
}
|
||||
|
||||
if (!mTaskEntity.isSupportBP()) {
|
||||
handleNoSupportBP();
|
||||
} else {
|
||||
handleBreakpoint();
|
||||
}
|
||||
|
||||
startTimer();
|
||||
}
|
||||
|
||||
@@ -254,17 +272,16 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte
|
||||
} else {
|
||||
mRecord = DbHelper.getTaskRecord(mTaskEntity.getKey());
|
||||
if (mRecord == null) {
|
||||
initRecord();
|
||||
mTaskEntity.setNewTask(true);
|
||||
initRecord(true);
|
||||
} else {
|
||||
if (mRecord.threadRecords == null || mRecord.threadRecords.isEmpty()) {
|
||||
initRecord();
|
||||
mTaskEntity.setNewTask(true);
|
||||
} else if (mTempFile.length() == 0) {
|
||||
mRecord.deleteData();
|
||||
initRecord();
|
||||
mTaskEntity.setNewTask(true);
|
||||
} else {
|
||||
initRecord(true);
|
||||
}
|
||||
//else if (mTempFile.length() == 0) {
|
||||
// mRecord.deleteData();
|
||||
// initRecord(true);
|
||||
//}
|
||||
else {
|
||||
for (ThreadRecord tr : mRecord.threadRecords) {
|
||||
if (tr.isComplete) {
|
||||
mCompleteThreadNum++;
|
||||
@@ -294,7 +311,7 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte
|
||||
mTaskEntity.setNewTask(true);
|
||||
return;
|
||||
}
|
||||
initRecord();
|
||||
initRecord(false);
|
||||
Set<Object> keys = pro.keySet();
|
||||
// 老版本记录是5s存一次,但是5s中内,如果线程执行完成,record记录是没有的,只有state记录...
|
||||
// 第一步应该是record 和 state去重取正确的线程数
|
||||
@@ -338,7 +355,7 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte
|
||||
/**
|
||||
* 初始化记录
|
||||
*/
|
||||
private void initRecord() {
|
||||
private void initRecord(boolean isNewTask) {
|
||||
mRecord = new TaskRecord();
|
||||
mRecord.fileName = mEntity.getFileName();
|
||||
mRecord.filePath = mTaskEntity.getKey();
|
||||
@@ -349,6 +366,7 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte
|
||||
mRecord.dGroupName = ((DownloadEntity) mTaskEntity.getEntity()).getGroupName();
|
||||
}
|
||||
}
|
||||
mTaskEntity.setNewTask(isNewTask);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -399,7 +417,8 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY exte
|
||||
SubThreadConfig<TASK_ENTITY> config = new SubThreadConfig<>();
|
||||
config.FILE_SIZE = fileLength;
|
||||
config.URL = mEntity.isRedirect() ? mEntity.getRedirectUrl() : mEntity.getUrl();
|
||||
config.TEMP_FILE = mTempFile;
|
||||
config.TEMP_FILE =
|
||||
mRecord.isBlock ? new File(String.format(SUB_PATH, mTempFile.getPath(), i)) : mTempFile;
|
||||
config.THREAD_ID = i;
|
||||
config.START_LOCATION = startL;
|
||||
config.END_LOCATION = endL;
|
||||
|
||||
@@ -23,8 +23,12 @@ import com.arialyy.aria.core.inf.IEventListener;
|
||||
import com.arialyy.aria.core.upload.UploadEntity;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.ErrorHelp;
|
||||
import com.arialyy.aria.util.FileUtil;
|
||||
import com.arialyy.aria.util.NetUtils;
|
||||
import java.io.File;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
@@ -69,10 +73,10 @@ public abstract class AbsThreadTask<ENTITY extends AbsNormalEntity, TASK_ENTITY
|
||||
});
|
||||
|
||||
protected AbsThreadTask(StateConstance constance, IEventListener listener,
|
||||
SubThreadConfig<TASK_ENTITY> info) {
|
||||
SubThreadConfig<TASK_ENTITY> config) {
|
||||
STATE = constance;
|
||||
mListener = listener;
|
||||
mConfig = info;
|
||||
mConfig = config;
|
||||
mTaskEntity = mConfig.TASK_ENTITY;
|
||||
mEntity = mTaskEntity.getEntity();
|
||||
mLastSaveTime = System.currentTimeMillis();
|
||||
@@ -134,6 +138,30 @@ public abstract class AbsThreadTask<ENTITY extends AbsNormalEntity, TASK_ENTITY
|
||||
return STATE.isCancel || STATE.isStop || taskBreak;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并文件
|
||||
*
|
||||
* @return {@code true} 合并成功,{@code false}合并失败
|
||||
*/
|
||||
protected boolean mergeFile() {
|
||||
List<String> partPath = new ArrayList<>();
|
||||
for (int i = 0, len = STATE.TASK_RECORD.threadNum; i < len; i++) {
|
||||
partPath.add(String.format(AbsFileer.SUB_PATH, STATE.TASK_RECORD.filePath, i));
|
||||
}
|
||||
boolean isSuccess = FileUtil.mergeFile(STATE.TASK_RECORD.filePath, partPath);
|
||||
if (isSuccess) {
|
||||
for (String pp : partPath) {
|
||||
File f = new File(pp);
|
||||
if (f.exists()) {
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止任务
|
||||
*/
|
||||
|
||||
@@ -17,7 +17,7 @@ package com.arialyy.aria.core.common;
|
||||
|
||||
/**
|
||||
* Created by lyy on 2017/1/18.
|
||||
* 下载状态常量
|
||||
* 状态常量
|
||||
*/
|
||||
public class StateConstance {
|
||||
public int CANCEL_NUM = 0;
|
||||
@@ -30,13 +30,8 @@ public class StateConstance {
|
||||
public boolean isCancel = false;
|
||||
public boolean isStop = false;
|
||||
public TaskRecord TASK_RECORD;
|
||||
/**
|
||||
* 是否是使用虚拟文件下载的
|
||||
* {@code true}是,{@code false}不是
|
||||
*/
|
||||
public boolean isOpenDynamicFile = false;
|
||||
|
||||
public StateConstance() {
|
||||
StateConstance() {
|
||||
}
|
||||
|
||||
public void resetState() {
|
||||
@@ -50,14 +45,14 @@ public class StateConstance {
|
||||
}
|
||||
|
||||
/**
|
||||
* 所有子线程是否都已经停止下载
|
||||
* 所有子线程是否都已经停止
|
||||
*/
|
||||
public boolean isStop() {
|
||||
return STOP_NUM == START_THREAD_NUM;
|
||||
}
|
||||
|
||||
/**
|
||||
* 所有子线程是否都已经下载失败
|
||||
* 所有子线程是否都已经失败
|
||||
*/
|
||||
public boolean isFail() {
|
||||
return COMPLETE_THREAD_NUM != START_THREAD_NUM
|
||||
@@ -65,14 +60,14 @@ public class StateConstance {
|
||||
}
|
||||
|
||||
/**
|
||||
* 所有子线程是否都已经完成下载
|
||||
* 所有子线程是否都已经完成
|
||||
*/
|
||||
public boolean isComplete() {
|
||||
return COMPLETE_THREAD_NUM >= START_THREAD_NUM;
|
||||
}
|
||||
|
||||
/**
|
||||
* 所有子线程是否都已经取消下载
|
||||
* 所有子线程是否都已经取消
|
||||
*/
|
||||
public boolean isCancel() {
|
||||
return CANCEL_NUM == START_THREAD_NUM;
|
||||
|
||||
@@ -73,4 +73,15 @@ public class TaskRecord extends DbEntity {
|
||||
@Ignore
|
||||
@Deprecated
|
||||
public String uGroupName;
|
||||
|
||||
/**
|
||||
* 是否是分块{@code true}是,{@code false} 不是
|
||||
*/
|
||||
public boolean isBlock = false;
|
||||
|
||||
/**
|
||||
* 是否是使用虚拟文件下载的
|
||||
* {@code true}是,{@code false}不是
|
||||
*/
|
||||
public boolean isOpenDynamicFile = false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 线程任务管理器
|
||||
*/
|
||||
class ThreadTaskManager {
|
||||
private static volatile ThreadTaskManager INSTANCE = null;
|
||||
private Map<String, List<AbsThreadTask>> mThreadTasks = new ConcurrentHashMap<>();
|
||||
|
||||
private ThreadTaskManager() {
|
||||
|
||||
}
|
||||
|
||||
public static ThreadTaskManager getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
synchronized (ThreadTaskManager.class) {
|
||||
INSTANCE = new ThreadTaskManager();
|
||||
}
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加单条线程记录
|
||||
*
|
||||
* @param key 任务对应的key
|
||||
* @param threadTask 线程任务
|
||||
*/
|
||||
public void addTask(String key, AbsThreadTask threadTask) {
|
||||
if (mThreadTasks.get(key) == null) {
|
||||
mThreadTasks.put(key, new ArrayList<AbsThreadTask>());
|
||||
}
|
||||
mThreadTasks.get(key).add(threadTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除对应的任务的线程记录
|
||||
*
|
||||
* @param key 任务对应的key
|
||||
*/
|
||||
public void removeTask(String key) {
|
||||
for (Iterator<Map.Entry<String, List<AbsThreadTask>>> iter = mThreadTasks.entrySet().iterator();
|
||||
iter.hasNext(); ) {
|
||||
Map.Entry<String, List<AbsThreadTask>> entry = iter.next();
|
||||
if (key.equals(entry.getKey())) {
|
||||
List<AbsThreadTask> list = mThreadTasks.get(key);
|
||||
if (list != null && !list.isEmpty()) {
|
||||
list.clear();
|
||||
}
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -57,22 +57,20 @@ class Downloader extends AbsFileer<DownloadEntity, DownloadTaskEntity> {
|
||||
}
|
||||
|
||||
@Override protected boolean handleNewTask() {
|
||||
CommonUtil.createFile(mTempFile.getPath());
|
||||
if (!mRecord.isBlock) {
|
||||
CommonUtil.createFile(mTempFile.getPath());
|
||||
}
|
||||
BufferedRandomAccessFile file = null;
|
||||
try {
|
||||
if (mTotalThreadNum > 1) {
|
||||
if (mTotalThreadNum > 1 && !mRecord.isBlock) {
|
||||
file = new BufferedRandomAccessFile(new File(mTempFile.getPath()), "rwd", 8192);
|
||||
//设置文件长度
|
||||
file.setLength(mEntity.getFileSize());
|
||||
}
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
failDownload("下载失败【downloadUrl:"
|
||||
+ mEntity.getUrl()
|
||||
+ "】\n【filePath:"
|
||||
+ mEntity.getDownloadPath()
|
||||
+ "】\n"
|
||||
+ ALog.getExceptionString(e));
|
||||
failDownload(String.format("下载失败【downloadUrl:%s】;【filePath:%s】\n %S", mEntity.getUrl(),
|
||||
mEntity.getDownloadPath(), ALog.getExceptionString(e)));
|
||||
} finally {
|
||||
if (file != null) {
|
||||
try {
|
||||
|
||||
@@ -41,6 +41,7 @@ import org.apache.commons.net.ftp.FTPReply;
|
||||
class FtpThreadTask extends AbsFtpThreadTask<DownloadEntity, DownloadTaskEntity> {
|
||||
private final String TAG = "FtpThreadTask";
|
||||
private boolean isOpenDynamicFile;
|
||||
private boolean isBlock;
|
||||
|
||||
FtpThreadTask(StateConstance constance, IDownloadListener listener,
|
||||
SubThreadConfig<DownloadTaskEntity> downloadInfo) {
|
||||
@@ -50,11 +51,16 @@ class FtpThreadTask extends AbsFtpThreadTask<DownloadEntity, DownloadTaskEntity>
|
||||
mReadTimeOut = manager.getDownloadConfig().getIOTimeOut();
|
||||
mBufSize = manager.getDownloadConfig().getBuffSize();
|
||||
isNotNetRetry = manager.getDownloadConfig().isNotNetRetry();
|
||||
isOpenDynamicFile = STATE.isOpenDynamicFile;
|
||||
isOpenDynamicFile = STATE.TASK_RECORD.isOpenDynamicFile;
|
||||
isBlock = STATE.TASK_RECORD.isBlock;
|
||||
setMaxSpeed(manager.getDownloadConfig().getMaxSpeed());
|
||||
}
|
||||
|
||||
@Override public void run() {
|
||||
if (mConfig.THREAD_RECORD.isComplete) {
|
||||
handleComplete();
|
||||
return;
|
||||
}
|
||||
//当前子线程的下载位置
|
||||
mChildCurrentLocation = mConfig.START_LOCATION;
|
||||
FTPClient client = null;
|
||||
@@ -100,19 +106,7 @@ class FtpThreadTask extends AbsFtpThreadTask<DownloadEntity, DownloadTaskEntity>
|
||||
if (isBreak()) {
|
||||
return;
|
||||
}
|
||||
ALog.i(TAG,
|
||||
String.format("任务【%s】线程__%s__下载完毕", mConfig.TEMP_FILE.getName(), mConfig.THREAD_ID));
|
||||
writeConfig(true, mConfig.END_LOCATION);
|
||||
STATE.COMPLETE_THREAD_NUM++;
|
||||
if (STATE.isComplete()) {
|
||||
STATE.TASK_RECORD.deleteData();
|
||||
STATE.isRunning = false;
|
||||
mListener.onComplete();
|
||||
}
|
||||
if (STATE.isFail()) {
|
||||
STATE.isRunning = false;
|
||||
mListener.onFail(false);
|
||||
}
|
||||
handleComplete();
|
||||
} catch (IOException e) {
|
||||
fail(mChildCurrentLocation, String.format("下载失败【%s】", mConfig.URL), e);
|
||||
} catch (Exception e) {
|
||||
@@ -131,6 +125,34 @@ class FtpThreadTask extends AbsFtpThreadTask<DownloadEntity, DownloadTaskEntity>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理线程完成的情况
|
||||
*/
|
||||
private void handleComplete() {
|
||||
ALog.i(TAG,
|
||||
String.format("任务【%s】线程__%s__下载完毕", mConfig.TEMP_FILE.getName(), mConfig.THREAD_ID));
|
||||
writeConfig(true, mConfig.END_LOCATION);
|
||||
STATE.COMPLETE_THREAD_NUM++;
|
||||
if (STATE.isComplete()) {
|
||||
if (isBlock) {
|
||||
boolean success = mergeFile();
|
||||
if (!success) {
|
||||
ALog.e(TAG, String.format("任务【%s】分块文件合并失败", mConfig.TEMP_FILE.getName()));
|
||||
STATE.isRunning = false;
|
||||
mListener.onFail(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
STATE.TASK_RECORD.deleteData();
|
||||
STATE.isRunning = false;
|
||||
mListener.onComplete();
|
||||
}
|
||||
if (STATE.isFail()) {
|
||||
STATE.isRunning = false;
|
||||
mListener.onFail(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态长度文件读取方式
|
||||
*/
|
||||
|
||||
@@ -44,6 +44,7 @@ import java.nio.channels.ReadableByteChannel;
|
||||
final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DownloadTaskEntity> {
|
||||
private final String TAG = "HttpThreadTask";
|
||||
private boolean isOpenDynamicFile;
|
||||
private boolean isBlock;
|
||||
|
||||
HttpThreadTask(StateConstance constance, IDownloadListener listener,
|
||||
SubThreadConfig<DownloadTaskEntity> downloadInfo) {
|
||||
@@ -53,11 +54,16 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DownloadTaskEnt
|
||||
mReadTimeOut = manager.getDownloadConfig().getIOTimeOut();
|
||||
mBufSize = manager.getDownloadConfig().getBuffSize();
|
||||
isNotNetRetry = manager.getDownloadConfig().isNotNetRetry();
|
||||
isOpenDynamicFile = STATE.isOpenDynamicFile;
|
||||
isOpenDynamicFile = STATE.TASK_RECORD.isOpenDynamicFile;
|
||||
isBlock = STATE.TASK_RECORD.isBlock;
|
||||
setMaxSpeed(manager.getDownloadConfig().getMaxSpeed());
|
||||
}
|
||||
|
||||
@Override public void run() {
|
||||
if (mConfig.THREAD_RECORD.isComplete) {
|
||||
handleComplete();
|
||||
return;
|
||||
}
|
||||
HttpURLConnection conn = null;
|
||||
BufferedInputStream is = null;
|
||||
BufferedRandomAccessFile file = null;
|
||||
@@ -209,6 +215,15 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DownloadTaskEnt
|
||||
writeConfig(true, mConfig.END_LOCATION);
|
||||
STATE.COMPLETE_THREAD_NUM++;
|
||||
if (STATE.isComplete()) {
|
||||
if (isBlock) {
|
||||
boolean success = mergeFile();
|
||||
if (!success) {
|
||||
ALog.e(TAG, String.format("任务【%s】分块文件合并失败", mConfig.TEMP_FILE.getName()));
|
||||
STATE.isRunning = false;
|
||||
mListener.onFail(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
STATE.TASK_RECORD.deleteData();
|
||||
STATE.isRunning = false;
|
||||
mListener.onComplete();
|
||||
|
||||
@@ -35,7 +35,7 @@ class DBConfig {
|
||||
/*adb pull /mnt/sdcard/Android/data/com.arialyy.simple/files/DB/AriaLyyDb d:/db*/
|
||||
static Map<String, Class> mapping = new HashMap<>();
|
||||
static String DB_NAME;
|
||||
static int VERSION = 34;
|
||||
static int VERSION = 35;
|
||||
|
||||
/**
|
||||
* 是否将数据库保存在Sd卡,{@code true} 是
|
||||
|
||||
@@ -30,6 +30,7 @@ import com.arialyy.aria.core.command.group.AbsGroupCmd;
|
||||
import com.arialyy.aria.core.command.group.GroupCmdFactory;
|
||||
import com.arialyy.aria.core.command.normal.AbsNormalCmd;
|
||||
import com.arialyy.aria.core.command.normal.NormalCmdFactory;
|
||||
import com.arialyy.aria.core.common.AbsFileer;
|
||||
import com.arialyy.aria.core.common.TaskRecord;
|
||||
import com.arialyy.aria.core.download.DownloadEntity;
|
||||
import com.arialyy.aria.core.download.DownloadGroupEntity;
|
||||
@@ -411,8 +412,17 @@ public class CommonUtil {
|
||||
if (records == null || records.isEmpty()) {
|
||||
ALog.w(TAG, "组任务记录删除失败,记录为null");
|
||||
} else {
|
||||
for (TaskRecord tr : records) {
|
||||
tr.deleteData();
|
||||
for (TaskRecord record : records) {
|
||||
// 删除分块文件
|
||||
if (record.isBlock) {
|
||||
for (int i = 0, len = record.threadNum; i < len; i++) {
|
||||
File partFile = new File(String.format(AbsFileer.SUB_PATH, record.filePath, i));
|
||||
if (partFile.exists()) {
|
||||
partFile.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
record.deleteData();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -449,8 +459,19 @@ public class CommonUtil {
|
||||
ALog.w(TAG, "删除记录失败,未知类型");
|
||||
return;
|
||||
}
|
||||
if (file.exists() && (removeFile || !dEntity.isComplete())) {
|
||||
file.delete();
|
||||
if (removeFile || !dEntity.isComplete()) {
|
||||
// 删除分块文件
|
||||
if (record.isBlock) {
|
||||
for (int i = 0, len = record.threadNum; i < len; i++) {
|
||||
File partFile = new File(String.format(AbsFileer.SUB_PATH, record.filePath, i));
|
||||
if (partFile.exists()) {
|
||||
partFile.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
if (record != null) {
|
||||
|
||||
@@ -43,42 +43,44 @@ public class FileUtil {
|
||||
*
|
||||
* @param targetPath 目标文件
|
||||
* @param subPaths 碎片文件路径
|
||||
* @return {@code true} 合并成功,{@code false}合并失败
|
||||
*/
|
||||
public static void mergeFile(String targetPath, List<String> subPaths) {
|
||||
public static boolean mergeFile(String targetPath, List<String> subPaths) {
|
||||
File file = new File(targetPath);
|
||||
FileOutputStream fos = null;
|
||||
FileChannel foc = null;
|
||||
try {
|
||||
if (!file.exists()) {
|
||||
file.createNewFile();
|
||||
fos = new FileOutputStream(targetPath);
|
||||
foc = fos.getChannel();
|
||||
List<FileInputStream> streams = new LinkedList<>();
|
||||
for (String subPath : subPaths) {
|
||||
File f = new File(subPath);
|
||||
if (!f.exists()) {
|
||||
ALog.d(TAG, String.format("合并文件失败,文件【%s】不存在", subPath));
|
||||
for (FileInputStream fis : streams) {
|
||||
fis.close();
|
||||
}
|
||||
streams.clear();
|
||||
|
||||
return;
|
||||
}
|
||||
streams.add(new FileInputStream(subPath));
|
||||
}
|
||||
Enumeration<FileInputStream> en = Collections.enumeration(streams);
|
||||
SequenceInputStream sis = new SequenceInputStream(en);
|
||||
ReadableByteChannel fic = Channels.newChannel(sis);
|
||||
ByteBuffer bf = ByteBuffer.allocate(8196);
|
||||
while (fic.read(bf) != -1) {
|
||||
bf.flip();
|
||||
foc.write(bf);
|
||||
bf.compact();
|
||||
}
|
||||
fic.close();
|
||||
sis.close();
|
||||
}
|
||||
fos = new FileOutputStream(targetPath);
|
||||
foc = fos.getChannel();
|
||||
List<FileInputStream> streams = new LinkedList<>();
|
||||
for (String subPath : subPaths) {
|
||||
File f = new File(subPath);
|
||||
if (!f.exists()) {
|
||||
ALog.d(TAG, String.format("合并文件失败,文件【%s】不存在", subPath));
|
||||
for (FileInputStream fis : streams) {
|
||||
fis.close();
|
||||
}
|
||||
streams.clear();
|
||||
|
||||
return false;
|
||||
}
|
||||
streams.add(new FileInputStream(subPath));
|
||||
}
|
||||
Enumeration<FileInputStream> en = Collections.enumeration(streams);
|
||||
SequenceInputStream sis = new SequenceInputStream(en);
|
||||
ReadableByteChannel fic = Channels.newChannel(sis);
|
||||
ByteBuffer bf = ByteBuffer.allocate(8196);
|
||||
while (fic.read(bf) != -1) {
|
||||
bf.flip();
|
||||
foc.write(bf);
|
||||
bf.compact();
|
||||
}
|
||||
fic.close();
|
||||
sis.close();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
@@ -93,6 +95,7 @@ public class FileUtil {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,6 +11,15 @@
|
||||
|
||||
<!--注意,修改该配置文件中的属性会覆盖代码中所设置的属性-->
|
||||
<download>
|
||||
<!--
|
||||
多线程下载是否使用块下载模式,{@code true}使用,{@code false}不使用
|
||||
注意:
|
||||
1、使用分块模式,在读写性能底下的手机上,合并文件需要的时间会更加长;
|
||||
2、优点是使用多线程的块下载,初始化时,文件初始化时将不会预占用对应长度的空间;
|
||||
3、只对新的多线程下载任务有效
|
||||
4、只对多线程的任务有效
|
||||
-->
|
||||
<useBlock value="true"/>
|
||||
|
||||
<!--断网的时候是否重试,true:断网也重试;false:断网不重试,直接走失败的回调-->
|
||||
<notNetRetry value="true"/>
|
||||
@@ -22,7 +31,7 @@
|
||||
3、从3.4.1开始,如果线程数为1,文件初始化时将不再预占用对应长度的空间,下载多少byte,则占多大的空间;
|
||||
对于采用多线程的任务或旧任务,依然采用原来的文件空间占用方式;
|
||||
-->
|
||||
<threadNum value="1"/>
|
||||
<threadNum value="3"/>
|
||||
|
||||
<!--设置下载队列最大任务数, 默认为2-->
|
||||
<maxTaskNum value="1"/>
|
||||
|
||||
@@ -18,10 +18,10 @@ public class AnyRunActivity extends BaseActivity<ActivityTestBinding> {
|
||||
AnyRunnModule module;
|
||||
String[] urls;
|
||||
int index = 0;
|
||||
//String URL = "http://static.gaoshouyou.com/d/12/0d/7f120f50c80d2e7b8c4ba24ece4f9cdd.apk";
|
||||
String URL = "http://static.gaoshouyou.com/d/12/0d/7f120f50c80d2e7b8c4ba24ece4f9cdd.apk";
|
||||
//String URL = "http://58.213.157.242:8081/sims_file/rest/v1/file/mshd_touchscreen_ms/guideFile/41c33556-dc4a-4d78-bb76-b9f627f94448.mp4/%E5%85%AB%E5%8D%A6%E6%B4%B2%E5%8D%97%E4%BA%AC%E5%86%9C%E4%B8%9A%E5%98%89%E5%B9%B4%E5%8D%8E0511.mp4";
|
||||
//String URL = "http://d1.showself.com/download/showself_android-s236279_release.apk";
|
||||
String URL = "http://static.gaoshouyou.com/d/22/94/822260b849944492caadd2983f9bb624.apk";
|
||||
//String URL = "http://static.gaoshouyou.com/d/22/94/822260b849944492caadd2983f9bb624.apk";
|
||||
//private final String URL = "ftp://192.168.29.140:21/download/AriaPrj.rar";
|
||||
//String URL = "https://dl.genymotion.com/releases/genymotion-2.12.1/genymotion-2.12.1-vbox.exe";
|
||||
//String URL = "ftp://192.168.29.140:21/download/SDK_Demo-release.apk";
|
||||
|
||||
@@ -65,11 +65,11 @@ public class AnyRunnModule {
|
||||
|
||||
void start(String url) {
|
||||
mUrl = url;
|
||||
String path = Environment.getExternalStorageDirectory().getPath() + "/abcds.exe";
|
||||
File file = new File(path);
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
String path = Environment.getExternalStorageDirectory().getPath() + "/abcds.apk";
|
||||
//File file = new File(path);
|
||||
//if (file.exists()) {
|
||||
// file.delete();
|
||||
//}
|
||||
Aria.download(this)
|
||||
.load(url)
|
||||
//.addHeader("Accept-Encoding", "gzip")
|
||||
|
||||
@@ -379,6 +379,26 @@ class Configuration {
|
||||
*/
|
||||
int maxSpeed = 0;
|
||||
|
||||
/**
|
||||
* 多线程下载是否使用块下载模式,{@code true}使用,{@code false}不使用
|
||||
* 注意:
|
||||
* 1、使用分块模式,在读写性能底下的手机上,合并文件需要的时间会更加长;
|
||||
* 2、优点是使用多线程的块下载,初始化时,文件初始化时将不会预占用对应长度的空间;
|
||||
* 3、只对新的多线程下载任务有效
|
||||
* 4、只对多线程的任务有效
|
||||
*/
|
||||
boolean useBlock = false;
|
||||
|
||||
public boolean isUseBlock() {
|
||||
return useBlock;
|
||||
}
|
||||
|
||||
public DownloadConfig setUseBlock(boolean useBlock) {
|
||||
this.useBlock = useBlock;
|
||||
saveKey("useBlock", String.valueOf(useBlock));
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownloadConfig setMaxTaskNum(int maxTaskNum) {
|
||||
oldMaxTaskNum = this.maxTaskNum;
|
||||
this.maxTaskNum = maxTaskNum;
|
||||
@@ -398,9 +418,10 @@ class Configuration {
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setThreadNum(int threadNum) {
|
||||
public DownloadConfig setThreadNum(int threadNum) {
|
||||
this.threadNum = threadNum;
|
||||
saveKey("threadNum", String.valueOf(threadNum));
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getCaPath() {
|
||||
|
||||
Reference in New Issue
Block a user