parent
c9eb57132b
commit
1e237d795d
@ -0,0 +1,167 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package com.arialyy.aria.http; |
||||||
|
|
||||||
|
import com.arialyy.aria.core.common.AbsEntity; |
||||||
|
import com.arialyy.aria.core.common.CompleteInfo; |
||||||
|
import com.arialyy.aria.core.download.DGTaskWrapper; |
||||||
|
import com.arialyy.aria.core.download.DTaskWrapper; |
||||||
|
import com.arialyy.aria.core.download.DownloadEntity; |
||||||
|
import com.arialyy.aria.core.listener.DownloadGroupListener; |
||||||
|
import com.arialyy.aria.core.loader.IInfoTask; |
||||||
|
import com.arialyy.aria.core.loader.ILoaderVisitor; |
||||||
|
import com.arialyy.aria.exception.AriaIOException; |
||||||
|
import com.arialyy.aria.exception.BaseException; |
||||||
|
import com.arialyy.aria.util.ALog; |
||||||
|
import com.arialyy.aria.util.CommonUtil; |
||||||
|
import java.util.concurrent.ExecutorService; |
||||||
|
import java.util.concurrent.Executors; |
||||||
|
|
||||||
|
/** |
||||||
|
* 组合任务文件信息,用于获取长度未知时,组合任务的长度 |
||||||
|
*/ |
||||||
|
public class HttpGroupInfoTask implements IInfoTask { |
||||||
|
private String TAG = CommonUtil.getClassName(this); |
||||||
|
private Callback callback; |
||||||
|
private DGTaskWrapper wrapper; |
||||||
|
private final Object LOCK = new Object(); |
||||||
|
private ExecutorService mPool = null; |
||||||
|
private boolean getLenComplete = false; |
||||||
|
private int count; |
||||||
|
private int failCount; |
||||||
|
private DownloadGroupListener listener; |
||||||
|
|
||||||
|
/** |
||||||
|
* 子任务回调 |
||||||
|
*/ |
||||||
|
private Callback subCallback = new Callback() { |
||||||
|
@Override public void onSucceed(String url, CompleteInfo info) { |
||||||
|
count++; |
||||||
|
checkGetSizeComplete(count, failCount); |
||||||
|
ALog.d(TAG, "获取子任务信息完成"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { |
||||||
|
ALog.e(TAG, String.format("获取文件信息失败,url:%s", ((DownloadEntity) entity).getUrl())); |
||||||
|
count++; |
||||||
|
failCount++; |
||||||
|
listener.onSubFail((DownloadEntity) entity, new AriaIOException(TAG, |
||||||
|
String.format("子任务获取文件长度失败,url:%s", ((DownloadEntity) entity).getUrl()))); |
||||||
|
checkGetSizeComplete(count, failCount); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
public HttpGroupInfoTask(DGTaskWrapper wrapper, DownloadGroupListener listener) { |
||||||
|
this.wrapper = wrapper; |
||||||
|
this.listener = listener; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void run() { |
||||||
|
// 如果是isUnknownSize()标志,并且获取大小没有完成,则直接回调onStop
|
||||||
|
if (mPool != null && !getLenComplete) { |
||||||
|
ALog.d(TAG, "获取长度未完成的情况下,停止组合任务"); |
||||||
|
mPool.shutdown(); |
||||||
|
//mListener.onStop(0);
|
||||||
|
return; |
||||||
|
} |
||||||
|
// 处理组合任务大小未知的情况
|
||||||
|
if (wrapper.isUnknownSize() && wrapper.getEntity().getFileSize() < 1) { |
||||||
|
mPool = Executors.newCachedThreadPool(); |
||||||
|
getGroupSize(); |
||||||
|
try { |
||||||
|
synchronized (LOCK) { |
||||||
|
LOCK.wait(); |
||||||
|
} |
||||||
|
} catch (InterruptedException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} else { |
||||||
|
for (DTaskWrapper wrapper : wrapper.getSubTaskWrapper()) { |
||||||
|
cloneHeader(wrapper); |
||||||
|
} |
||||||
|
callback.onSucceed(wrapper.getKey(), new CompleteInfo()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取组合任务大小,使用该方式获取到的组合任务大小,子任务不需要再重新获取文件大小 |
||||||
|
*/ |
||||||
|
private void getGroupSize() { |
||||||
|
new Thread(new Runnable() { |
||||||
|
@Override public void run() { |
||||||
|
for (DTaskWrapper dTaskWrapper : wrapper.getSubTaskWrapper()) { |
||||||
|
cloneHeader(dTaskWrapper); |
||||||
|
HttpFileInfoTask infoTask = new HttpFileInfoTask(dTaskWrapper); |
||||||
|
infoTask.setCallback(subCallback); |
||||||
|
} |
||||||
|
} |
||||||
|
}).start(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 检查组合任务大小是否获取完成,获取完成后取消阻塞,并设置组合任务大小 |
||||||
|
*/ |
||||||
|
private void checkGetSizeComplete(int count, int failCount) { |
||||||
|
if (failCount == wrapper.getSubTaskWrapper().size()) { |
||||||
|
callback.onFail(wrapper.getEntity(), new AriaIOException(TAG, "获取子任务长度失败"), false); |
||||||
|
notifyLock(); |
||||||
|
return; |
||||||
|
} |
||||||
|
if (count == wrapper.getSubTaskWrapper().size()) { |
||||||
|
long size = 0; |
||||||
|
for (DTaskWrapper wrapper : wrapper.getSubTaskWrapper()) { |
||||||
|
size += wrapper.getEntity().getFileSize(); |
||||||
|
} |
||||||
|
wrapper.getEntity().setConvertFileSize(CommonUtil.formatFileSize(size)); |
||||||
|
wrapper.getEntity().setFileSize(size); |
||||||
|
wrapper.getEntity().update(); |
||||||
|
getLenComplete = true; |
||||||
|
ALog.d(TAG, String.format("获取组合任务长度完成,组合任务总长度:%s,失败的只任务数:%s", size, failCount)); |
||||||
|
callback.onSucceed(wrapper.getKey(), new CompleteInfo()); |
||||||
|
notifyLock(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void notifyLock() { |
||||||
|
synchronized (LOCK) { |
||||||
|
LOCK.notifyAll(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 子任务使用父包裹器的属性 |
||||||
|
*/ |
||||||
|
private void cloneHeader(DTaskWrapper taskWrapper) { |
||||||
|
HttpTaskOption groupOption = (HttpTaskOption) wrapper.getTaskOption(); |
||||||
|
HttpTaskOption subOption = new HttpTaskOption(); |
||||||
|
|
||||||
|
// 设置属性
|
||||||
|
subOption.setFileLenAdapter(groupOption.getFileLenAdapter()); |
||||||
|
subOption.setRequestEnum(groupOption.getRequestEnum()); |
||||||
|
subOption.setHeaders(groupOption.getHeaders()); |
||||||
|
subOption.setProxy(groupOption.getProxy()); |
||||||
|
subOption.setParams(groupOption.getParams()); |
||||||
|
taskWrapper.setTaskOption(subOption); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void setCallback(Callback callback) { |
||||||
|
this.callback = callback; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void accept(ILoaderVisitor visitor) { |
||||||
|
visitor.addComponent(this); |
||||||
|
} |
||||||
|
} |
@ -1,100 +1,100 @@ |
|||||||
/* |
///*
|
||||||
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
// * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
||||||
* |
// *
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
// * Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License. |
// * you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at |
// * You may obtain a copy of the License at
|
||||||
* |
// *
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
// * http://www.apache.org/licenses/LICENSE-2.0
|
||||||
* |
// *
|
||||||
* Unless required by applicable law or agreed to in writing, software |
// * Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
// * distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and |
// * See the License for the specific language governing permissions and
|
||||||
* limitations under the License. |
// * limitations under the License.
|
||||||
*/ |
// */
|
||||||
package com.arialyy.aria.http.download; |
//package com.arialyy.aria.http.download;
|
||||||
|
//
|
||||||
import com.arialyy.aria.core.TaskRecord; |
//import com.arialyy.aria.core.TaskRecord;
|
||||||
import com.arialyy.aria.core.common.RecordHandler; |
//import com.arialyy.aria.core.common.RecordHandler;
|
||||||
import com.arialyy.aria.core.common.SubThreadConfig; |
//import com.arialyy.aria.core.common.SubThreadConfig;
|
||||||
import com.arialyy.aria.core.download.DownloadEntity; |
//import com.arialyy.aria.core.download.DownloadEntity;
|
||||||
import com.arialyy.aria.core.inf.IRecordHandler; |
//import com.arialyy.aria.core.loader.IRecordHandler;
|
||||||
import com.arialyy.aria.core.task.AbsNormalLoaderAdapter; |
//import com.arialyy.aria.core.task.AbsNormalLoaderAdapter;
|
||||||
import com.arialyy.aria.core.task.IThreadTask; |
//import com.arialyy.aria.core.task.IThreadTask;
|
||||||
import com.arialyy.aria.core.task.ThreadTask; |
//import com.arialyy.aria.core.task.ThreadTask;
|
||||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper; |
//import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||||
import com.arialyy.aria.core.wrapper.ITaskWrapper; |
//import com.arialyy.aria.core.wrapper.ITaskWrapper;
|
||||||
import com.arialyy.aria.http.HttpRecordAdapter; |
//import com.arialyy.aria.http.HttpRecordHandler;
|
||||||
import com.arialyy.aria.util.ALog; |
//import com.arialyy.aria.util.ALog;
|
||||||
import com.arialyy.aria.util.BufferedRandomAccessFile; |
//import com.arialyy.aria.util.BufferedRandomAccessFile;
|
||||||
import com.arialyy.aria.util.FileUtil; |
//import com.arialyy.aria.util.FileUtil;
|
||||||
import java.io.File; |
//import java.io.File;
|
||||||
import java.io.IOException; |
//import java.io.IOException;
|
||||||
|
//
|
||||||
/** |
///**
|
||||||
* @Author lyy |
// * @Author lyy
|
||||||
* @Date 2019-09-21 |
// * @Date 2019-09-21
|
||||||
*/ |
// */
|
||||||
final class HttpDLoaderAdapter extends AbsNormalLoaderAdapter { |
//final class HttpDLoaderAdapter extends AbsNormalLoaderAdapter {
|
||||||
HttpDLoaderAdapter(ITaskWrapper wrapper) { |
// HttpDLoaderAdapter(ITaskWrapper wrapper) {
|
||||||
super(wrapper); |
// super(wrapper);
|
||||||
} |
// }
|
||||||
|
//
|
||||||
@Override public boolean handleNewTask(TaskRecord record, int totalThreadNum) { |
// @Override public boolean handleNewTask(TaskRecord record, int totalThreadNum) {
|
||||||
if (!record.isBlock) { |
// if (!record.isBlock) {
|
||||||
if (getTempFile().exists()) { |
// if (getTempFile().exists()) {
|
||||||
FileUtil.deleteFile(getTempFile()); |
// FileUtil.deleteFile(getTempFile());
|
||||||
} |
// }
|
||||||
} else { |
// } else {
|
||||||
for (int i = 0; i < totalThreadNum; i++) { |
// for (int i = 0; i < totalThreadNum; i++) {
|
||||||
File blockFile = |
// File blockFile =
|
||||||
new File(String.format(IRecordHandler.SUB_PATH, getTempFile().getPath(), i)); |
// new File(String.format(IRecordHandler.SUB_PATH, getTempFile().getPath(), i));
|
||||||
if (blockFile.exists()) { |
// if (blockFile.exists()) {
|
||||||
ALog.d(TAG, String.format("分块【%s】已经存在,将删除该分块", i)); |
// ALog.d(TAG, String.format("分块【%s】已经存在,将删除该分块", i));
|
||||||
FileUtil.deleteFile(blockFile); |
// FileUtil.deleteFile(blockFile);
|
||||||
} |
// }
|
||||||
} |
// }
|
||||||
} |
// }
|
||||||
BufferedRandomAccessFile file = null; |
// BufferedRandomAccessFile file = null;
|
||||||
try { |
// try {
|
||||||
if (totalThreadNum > 1 && !record.isBlock) { |
// if (totalThreadNum > 1 && !record.isBlock) {
|
||||||
file = new BufferedRandomAccessFile(new File(getTempFile().getPath()), "rwd", 8192); |
// file = new BufferedRandomAccessFile(new File(getTempFile().getPath()), "rwd", 8192);
|
||||||
//设置文件长度
|
// //设置文件长度
|
||||||
file.setLength(getEntity().getFileSize()); |
// file.setLength(getEntity().getFileSize());
|
||||||
} |
// }
|
||||||
return true; |
// return true;
|
||||||
} catch (IOException e) { |
// } catch (IOException e) {
|
||||||
e.printStackTrace(); |
// e.printStackTrace();
|
||||||
ALog.e(TAG, String.format("下载失败,filePath: %s, url: %s", getEntity().getFilePath(), |
// ALog.e(TAG, String.format("下载失败,filePath: %s, url: %s", getEntity().getFilePath(),
|
||||||
getEntity().getUrl())); |
// getEntity().getUrl()));
|
||||||
} finally { |
// } finally {
|
||||||
if (file != null) { |
// if (file != null) {
|
||||||
try { |
// try {
|
||||||
file.close(); |
// file.close();
|
||||||
} catch (IOException e) { |
// } catch (IOException e) {
|
||||||
e.printStackTrace(); |
// e.printStackTrace();
|
||||||
} |
// }
|
||||||
} |
// }
|
||||||
} |
// }
|
||||||
return false; |
// return false;
|
||||||
} |
// }
|
||||||
|
//
|
||||||
@Override public IThreadTask createThreadTask(SubThreadConfig config) { |
// @Override public IThreadTask createThreadTask(SubThreadConfig config) {
|
||||||
ThreadTask task = new ThreadTask(config); |
// ThreadTask task = new ThreadTask(config);
|
||||||
HttpDThreadTaskAdapter adapter = new HttpDThreadTaskAdapter(config); |
// HttpDThreadTaskAdapter adapter = new HttpDThreadTaskAdapter(config);
|
||||||
task.setAdapter(adapter); |
// task.setAdapter(adapter);
|
||||||
return task; |
// return task;
|
||||||
} |
// }
|
||||||
|
//
|
||||||
@Override public IRecordHandler recordHandler(AbsTaskWrapper wrapper) { |
// @Override public IRecordHandler recordHandler(AbsTaskWrapper wrapper) {
|
||||||
RecordHandler recordHandler = new RecordHandler(wrapper); |
// RecordHandler recordHandler = new RecordHandler(wrapper);
|
||||||
HttpRecordAdapter adapter = new HttpRecordAdapter(wrapper); |
// HttpRecordHandler adapter = new HttpRecordHandler(wrapper);
|
||||||
recordHandler.setAdapter(adapter); |
// recordHandler.setAdapter(adapter);
|
||||||
return recordHandler; |
// return recordHandler;
|
||||||
} |
// }
|
||||||
|
//
|
||||||
private DownloadEntity getEntity() { |
// private DownloadEntity getEntity() {
|
||||||
return (DownloadEntity) getWrapper().getEntity(); |
// return (DownloadEntity) getWrapper().getEntity();
|
||||||
} |
// }
|
||||||
} |
//}
|
||||||
|
@ -0,0 +1,62 @@ |
|||||||
|
package com.arialyy.aria.http.download; |
||||||
|
|
||||||
|
import com.arialyy.aria.core.TaskRecord; |
||||||
|
import com.arialyy.aria.core.common.SubThreadConfig; |
||||||
|
import com.arialyy.aria.core.loader.AbsNormalTTBuilder; |
||||||
|
import com.arialyy.aria.core.loader.IRecordHandler; |
||||||
|
import com.arialyy.aria.core.task.IThreadTaskAdapter; |
||||||
|
import com.arialyy.aria.core.wrapper.AbsTaskWrapper; |
||||||
|
import com.arialyy.aria.util.ALog; |
||||||
|
import com.arialyy.aria.util.BufferedRandomAccessFile; |
||||||
|
import com.arialyy.aria.util.FileUtil; |
||||||
|
import java.io.File; |
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
final class HttpDTTBuilder extends AbsNormalTTBuilder { |
||||||
|
HttpDTTBuilder(AbsTaskWrapper wrapper) { |
||||||
|
super(wrapper); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public IThreadTaskAdapter getAdapter(SubThreadConfig config) { |
||||||
|
return new HttpDThreadTaskAdapter(config); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public boolean handleNewTask(TaskRecord record, int totalThreadNum) { |
||||||
|
if (!record.isBlock) { |
||||||
|
if (getTempFile().exists()) { |
||||||
|
FileUtil.deleteFile(getTempFile()); |
||||||
|
} |
||||||
|
} else { |
||||||
|
for (int i = 0; i < totalThreadNum; i++) { |
||||||
|
File blockFile = |
||||||
|
new File(String.format(IRecordHandler.SUB_PATH, getTempFile().getPath(), i)); |
||||||
|
if (blockFile.exists()) { |
||||||
|
ALog.d(TAG, String.format("分块【%s】已经存在,将删除该分块", i)); |
||||||
|
FileUtil.deleteFile(blockFile); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
BufferedRandomAccessFile file = null; |
||||||
|
try { |
||||||
|
if (totalThreadNum > 1 && !record.isBlock) { |
||||||
|
file = new BufferedRandomAccessFile(new File(getTempFile().getPath()), "rwd", 8192); |
||||||
|
//设置文件长度
|
||||||
|
file.setLength(getEntity().getFileSize()); |
||||||
|
} |
||||||
|
return true; |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
ALog.e(TAG, String.format("下载失败,filePath: %s, url: %s", getEntity().getFilePath(), |
||||||
|
getEntity().getUrl())); |
||||||
|
} finally { |
||||||
|
if (file != null) { |
||||||
|
try { |
||||||
|
file.close(); |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
@ -1,44 +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.common; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.inf.IRecordHandlerAdapter; |
|
||||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper; |
|
||||||
import com.arialyy.aria.util.CommonUtil; |
|
||||||
|
|
||||||
/** |
|
||||||
* @Author lyy |
|
||||||
* @Date 2019-09-19 |
|
||||||
*/ |
|
||||||
public abstract class AbsRecordHandlerAdapter implements IRecordHandlerAdapter { |
|
||||||
private AbsTaskWrapper mWrapper; |
|
||||||
protected String TAG = CommonUtil.getClassName(getClass()); |
|
||||||
|
|
||||||
@Override public void onPre() { |
|
||||||
} |
|
||||||
|
|
||||||
public AbsRecordHandlerAdapter(AbsTaskWrapper wrapper) { |
|
||||||
mWrapper = wrapper; |
|
||||||
} |
|
||||||
|
|
||||||
public AbsTaskWrapper getWrapper() { |
|
||||||
return mWrapper; |
|
||||||
} |
|
||||||
|
|
||||||
protected AbsNormalEntity getEntity() { |
|
||||||
return (AbsNormalEntity) getWrapper().getEntity(); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,185 @@ |
|||||||
|
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.task.IThreadTask; |
||||||
|
import com.arialyy.aria.core.task.IThreadTaskAdapter; |
||||||
|
import com.arialyy.aria.core.task.ThreadTask; |
||||||
|
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.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
public abstract class AbsNormalTTBuilder implements IThreadTaskBuilder { |
||||||
|
protected String TAG = CommonUtil.getClassName(this); |
||||||
|
|
||||||
|
private Handler mStateHandler; |
||||||
|
private AbsTaskWrapper mWrapper; |
||||||
|
private TaskRecord mRecord; |
||||||
|
private int mTotalThreadNum; |
||||||
|
private File mTempFile; |
||||||
|
private int mStartThreadNum; |
||||||
|
|
||||||
|
public AbsNormalTTBuilder(AbsTaskWrapper wrapper) { |
||||||
|
if (wrapper instanceof DGTaskWrapper) { |
||||||
|
throw new AssertionError("NormalTTBuilder 不适用于组合任务"); |
||||||
|
} |
||||||
|
mWrapper = wrapper; |
||||||
|
mTempFile = new File(((AbsNormalEntity) wrapper.getEntity()).getFilePath()); |
||||||
|
} |
||||||
|
|
||||||
|
protected File getTempFile(){ |
||||||
|
return mTempFile; |
||||||
|
} |
||||||
|
|
||||||
|
protected AbsNormalEntity getEntity() { |
||||||
|
return (AbsNormalEntity) mWrapper.getEntity(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建线程任务适配器 |
||||||
|
*/ |
||||||
|
public abstract IThreadTaskAdapter getAdapter(SubThreadConfig config); |
||||||
|
|
||||||
|
/** |
||||||
|
* 处理新任务 |
||||||
|
* |
||||||
|
* @param record 任务记录 |
||||||
|
* @param totalThreadNum 任务的线程总数 |
||||||
|
* @return {@code true}创建新任务成功 |
||||||
|
*/ |
||||||
|
public abstract boolean handleNewTask(TaskRecord record, int totalThreadNum); |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建线程任务 |
||||||
|
*/ |
||||||
|
private IThreadTask createThreadTask(SubThreadConfig config) { |
||||||
|
ThreadTask task = new ThreadTask(config); |
||||||
|
task.setAdapter(getAdapter(config)); |
||||||
|
return task; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 启动断点任务时,创建单线程任务 |
||||||
|
* |
||||||
|
* @param record 线程记录 |
||||||
|
* @param startNum 启动的线程数 |
||||||
|
*/ |
||||||
|
private IThreadTask createSingThreadTask(ThreadRecord record, int startNum) { |
||||||
|
SubThreadConfig config = new SubThreadConfig(); |
||||||
|
config.url = getEntity().isRedirect() ? getEntity().getRedirectUrl() : getEntity().getUrl(); |
||||||
|
config.tempFile = |
||||||
|
mRecord.isBlock ? new File( |
||||||
|
String.format(IRecordHandler.SUB_PATH, mTempFile.getPath(), record.threadId)) |
||||||
|
: mTempFile; |
||||||
|
config.isBlock = mRecord.isBlock; |
||||||
|
config.startThreadNum = startNum; |
||||||
|
config.taskWrapper = mWrapper; |
||||||
|
config.record = record; |
||||||
|
config.stateHandler = mStateHandler; |
||||||
|
return createThreadTask(config); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 处理不支持断点的任务 |
||||||
|
*/ |
||||||
|
private List<IThreadTask> handleNoSupportBP() { |
||||||
|
List<IThreadTask> list = new ArrayList<>(); |
||||||
|
mStartThreadNum = 1; |
||||||
|
|
||||||
|
IThreadTask task = createSingThreadTask(mRecord.threadRecords.get(0), 1); |
||||||
|
if (task == null) { |
||||||
|
ALog.e(TAG, "创建线程任务失败"); |
||||||
|
return null; |
||||||
|
} |
||||||
|
list.add(task); |
||||||
|
return list; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 处理支持断点的任务 |
||||||
|
*/ |
||||||
|
private List<IThreadTask> handleBreakpoint() { |
||||||
|
long fileLength = getEntity().getFileSize(); |
||||||
|
long blockSize = fileLength / mTotalThreadNum; |
||||||
|
long currentProgress = 0; |
||||||
|
List<IThreadTask> threadTasks = new ArrayList<>(); |
||||||
|
|
||||||
|
mRecord.fileLength = fileLength; |
||||||
|
if (mWrapper.isNewTask() && !handleNewTask(mRecord, mTotalThreadNum)) { |
||||||
|
ALog.e(TAG, "初始化线程任务失败"); |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
for (ThreadRecord tr : mRecord.threadRecords) { |
||||||
|
if (!tr.isComplete) { |
||||||
|
mStartThreadNum++; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
for (int i = 0; i < mTotalThreadNum; i++) { |
||||||
|
long startL = i * blockSize, endL = (i + 1) * blockSize; |
||||||
|
ThreadRecord tr = mRecord.threadRecords.get(i); |
||||||
|
|
||||||
|
if (tr.isComplete) {//该线程已经完成
|
||||||
|
currentProgress += endL - startL; |
||||||
|
ALog.d(TAG, String.format("任务【%s】线程__%s__已完成", mWrapper.getKey(), i)); |
||||||
|
mStateHandler.obtainMessage(IThreadState.STATE_COMPLETE).sendToTarget(); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
//如果有记录,则恢复任务
|
||||||
|
long r = tr.startLocation; |
||||||
|
//记录的位置需要在线程区间中
|
||||||
|
if (startL < r && r <= (i == (mTotalThreadNum - 1) ? fileLength : endL)) { |
||||||
|
currentProgress += r - startL; |
||||||
|
} |
||||||
|
ALog.d(TAG, String.format("任务【%s】线程__%s__恢复任务", getEntity().getFileName(), i)); |
||||||
|
|
||||||
|
IThreadTask task = createSingThreadTask(tr, mStartThreadNum); |
||||||
|
if (task == null) { |
||||||
|
ALog.e(TAG, "创建线程任务失败"); |
||||||
|
return null; |
||||||
|
} |
||||||
|
threadTasks.add(task); |
||||||
|
} |
||||||
|
if (currentProgress != 0 && currentProgress != getEntity().getCurrentProgress()) { |
||||||
|
ALog.d(TAG, String.format("进度修正,当前进度:%s", currentProgress)); |
||||||
|
getEntity().setCurrentProgress(currentProgress); |
||||||
|
} |
||||||
|
//mStateManager.updateProgress(currentProgress);
|
||||||
|
return threadTasks; |
||||||
|
} |
||||||
|
|
||||||
|
private List<IThreadTask> handleTask() { |
||||||
|
if (mWrapper.isSupportBP()) { |
||||||
|
return handleBreakpoint(); |
||||||
|
}else { |
||||||
|
return handleNoSupportBP(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override public List<IThreadTask> buildThreadTask(TaskRecord record, Looper looper, |
||||||
|
IThreadState stateManager) { |
||||||
|
mRecord = record; |
||||||
|
mStateHandler = new Handler(looper, stateManager.getHandlerCallback()); |
||||||
|
mTotalThreadNum = mRecord.threadNum; |
||||||
|
return handleTask(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public int getCreatedThreadNum() { |
||||||
|
return mStartThreadNum; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void accept(ILoaderVisitor visitor) { |
||||||
|
visitor.addComponent(this); |
||||||
|
} |
||||||
|
} |
@ -1,75 +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.loader; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.TaskRecord; |
|
||||||
import com.arialyy.aria.core.listener.IEventListener; |
|
||||||
import com.arialyy.aria.core.wrapper.ITaskWrapper; |
|
||||||
import java.util.List; |
|
||||||
|
|
||||||
/** |
|
||||||
* 责任链 |
|
||||||
*/ |
|
||||||
public final class LoaderChain implements ILoaderInterceptor.Chain { |
|
||||||
private ITaskWrapper wrapper; |
|
||||||
private IEventListener listener; |
|
||||||
private TaskRecord taskRecord; |
|
||||||
private int index; |
|
||||||
private List<ILoaderInterceptor> interceptors; |
|
||||||
|
|
||||||
public LoaderChain(List<ILoaderInterceptor> interceptors, ITaskWrapper wrapper, |
|
||||||
IEventListener listener, TaskRecord taskRecord, |
|
||||||
int index) { |
|
||||||
this.interceptors = interceptors; |
|
||||||
this.wrapper = wrapper; |
|
||||||
this.listener = listener; |
|
||||||
this.taskRecord = taskRecord; |
|
||||||
this.index = index; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void updateRecord(TaskRecord record) { |
|
||||||
this.taskRecord = record; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public TaskRecord getRecord() { |
|
||||||
return taskRecord; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public IEventListener getListener() { |
|
||||||
return listener; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public ITaskWrapper getWrapper() { |
|
||||||
return wrapper; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public ILoader proceed() { |
|
||||||
int index = this.index + 1; |
|
||||||
if (index >= interceptors.size()) { |
|
||||||
throw new AssertionError(); |
|
||||||
} |
|
||||||
|
|
||||||
LoaderChain next = new LoaderChain(interceptors, wrapper, listener, taskRecord, index); |
|
||||||
ILoaderInterceptor interceptor = interceptors.get(index); |
|
||||||
ILoader loader = interceptor.intercept(next); |
|
||||||
|
|
||||||
if (loader == null) { |
|
||||||
throw new NullPointerException("Loader为空"); |
|
||||||
} |
|
||||||
|
|
||||||
return loader; |
|
||||||
} |
|
||||||
} |
|
Loading…
Reference in new issue