parent
380b05de43
commit
8cf4c36d71
@ -0,0 +1,65 @@ |
|||||||
|
/* |
||||||
|
* 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.download; |
||||||
|
|
||||||
|
import android.os.Parcel; |
||||||
|
import com.arialyy.aria.core.inf.AbsGroupEntity; |
||||||
|
import java.util.LinkedList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by AriaL on 2017/6/29. |
||||||
|
* 下载任务组实体 |
||||||
|
*/ |
||||||
|
public class DownloadGroupEntity extends AbsGroupEntity { |
||||||
|
|
||||||
|
private List<DownloadEntity> mChild = new LinkedList<>(); |
||||||
|
|
||||||
|
public List<DownloadEntity> getChild() { |
||||||
|
return mChild; |
||||||
|
} |
||||||
|
|
||||||
|
public void setChild(List<DownloadEntity> child) { |
||||||
|
this.mChild = child; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public int describeContents() { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void writeToParcel(Parcel dest, int flags) { |
||||||
|
super.writeToParcel(dest, flags); |
||||||
|
dest.writeTypedList(this.mChild); |
||||||
|
} |
||||||
|
|
||||||
|
public DownloadGroupEntity() { |
||||||
|
} |
||||||
|
|
||||||
|
protected DownloadGroupEntity(Parcel in) { |
||||||
|
super(in); |
||||||
|
this.mChild = in.createTypedArrayList(DownloadEntity.CREATOR); |
||||||
|
} |
||||||
|
|
||||||
|
public static final Creator<DownloadGroupEntity> CREATOR = new Creator<DownloadGroupEntity>() { |
||||||
|
@Override public DownloadGroupEntity createFromParcel(Parcel source) { |
||||||
|
return new DownloadGroupEntity(source); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public DownloadGroupEntity[] newArray(int size) { |
||||||
|
return new DownloadGroupEntity[size]; |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
/* |
||||||
|
* 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.download; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by AriaL on 2017/6/30. |
||||||
|
*/ |
||||||
|
|
||||||
|
public class DownloadGroupListener { |
||||||
|
|
||||||
|
} |
@ -0,0 +1,269 @@ |
|||||||
|
///*
|
||||||
|
// * 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.download;
|
||||||
|
//
|
||||||
|
//import android.content.Context;
|
||||||
|
//import android.os.Handler;
|
||||||
|
//import android.util.SparseArray;
|
||||||
|
//import com.arialyy.aria.core.Aria;
|
||||||
|
//import com.arialyy.aria.core.AriaManager;
|
||||||
|
//import com.arialyy.aria.core.download.downloader.DownloadListener;
|
||||||
|
//import com.arialyy.aria.core.download.downloader.DownloadUtil;
|
||||||
|
//import com.arialyy.aria.core.inf.AbsGroupTask;
|
||||||
|
//import com.arialyy.aria.core.inf.IEntity;
|
||||||
|
//import com.arialyy.aria.core.scheduler.DownloadSchedulers;
|
||||||
|
//import com.arialyy.aria.core.scheduler.ISchedulers;
|
||||||
|
//import com.arialyy.aria.util.CheckUtil;
|
||||||
|
//import com.arialyy.aria.util.CommonUtil;
|
||||||
|
//import java.lang.ref.WeakReference;
|
||||||
|
//import java.util.HashMap;
|
||||||
|
//import java.util.List;
|
||||||
|
//import java.util.Map;
|
||||||
|
//import java.util.concurrent.ExecutorService;
|
||||||
|
//import java.util.concurrent.Executors;
|
||||||
|
//
|
||||||
|
///**
|
||||||
|
// * Created by AriaL on 2017/6/27.
|
||||||
|
// * 任务组任务
|
||||||
|
// */
|
||||||
|
//public class DownloadGroupTask extends AbsGroupTask<DownloadTaskEntity, DownloadGroupEntity> {
|
||||||
|
// DListener mListener;
|
||||||
|
// SparseArray<DownloadUtil> mUtils = new SparseArray<>();
|
||||||
|
// boolean isStop = false;
|
||||||
|
//
|
||||||
|
// private ExecutorService mFixedThreadPool;
|
||||||
|
//
|
||||||
|
// private DownloadGroupTask(DownloadTaskEntity taskEntity, Handler outHandler) {
|
||||||
|
// mTaskEntity = taskEntity;
|
||||||
|
// mEntity = taskEntity.groupEntity;
|
||||||
|
// mOutHandler = outHandler;
|
||||||
|
// mContext = AriaManager.APP;
|
||||||
|
// mListener = new DListener(mContext, this, mOutHandler);
|
||||||
|
// mFixedThreadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override public void start() {
|
||||||
|
// List<DownloadEntity> childs = mEntity.getChild();
|
||||||
|
// int i = 1;
|
||||||
|
// mListener.sendInState2Target(ISchedulers.PRE);
|
||||||
|
// for (DownloadEntity entity : childs) {
|
||||||
|
// DownloadUtil util = createChildDownload(entity);
|
||||||
|
// if (isStop) break;
|
||||||
|
// mUtils.append(i, util);
|
||||||
|
// mFixedThreadPool.execute(util);
|
||||||
|
// i++;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 创建任务组单个任务下载工具
|
||||||
|
// */
|
||||||
|
// private DownloadUtil createChildDownload(DownloadEntity entity) {
|
||||||
|
// DownloadTaskEntity taskEntity = new DownloadTaskEntity(entity);
|
||||||
|
// taskEntity.headers = mTaskEntity.headers;
|
||||||
|
// taskEntity.requestEnum = mTaskEntity.requestEnum;
|
||||||
|
// taskEntity.redirectUrlKey = mTaskEntity.redirectUrlKey;
|
||||||
|
// taskEntity.removeFile = mTaskEntity.removeFile;
|
||||||
|
// return new DownloadUtil(mContext, taskEntity, mListener);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override public void stop() {
|
||||||
|
// isStop = true;
|
||||||
|
// if (mFixedThreadPool != null) {
|
||||||
|
// mFixedThreadPool.shutdown();
|
||||||
|
// }
|
||||||
|
// for (int i = 1, len = mUtils.size(); i <= len; i++) {
|
||||||
|
// DownloadUtil util = mUtils.get(i);
|
||||||
|
// if (util != null && util.isDownloading()) {
|
||||||
|
// util.stopDownload();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override public void cancel() {
|
||||||
|
// isStop = true;
|
||||||
|
// if (mFixedThreadPool != null) {
|
||||||
|
// mFixedThreadPool.shutdown();
|
||||||
|
// }
|
||||||
|
// for (int i = 1, len = mUtils.size(); i <= len; i++) {
|
||||||
|
// DownloadUtil util = mUtils.get(i);
|
||||||
|
// if (util != null) {
|
||||||
|
// util.cancelDownload();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 下载监听类
|
||||||
|
// */
|
||||||
|
// private static class DListener extends DownloadListener {
|
||||||
|
// WeakReference<Handler> outHandler;
|
||||||
|
// WeakReference<DownloadGroupTask> wTask;
|
||||||
|
// Context context;
|
||||||
|
// long lastLen = 0; //上一次发送长度
|
||||||
|
// long lastTime = 0;
|
||||||
|
// long INTERVAL_TIME = 1000; //1m更新周期
|
||||||
|
// boolean isFirst = true;
|
||||||
|
// DownloadGroupEntity groupEntity;
|
||||||
|
// DownloadGroupTask task;
|
||||||
|
// boolean isConvertSpeed = false;
|
||||||
|
// Map<String, DownloadEntity> mEntityMap = new HashMap<>();
|
||||||
|
//
|
||||||
|
// DListener(Context context, DownloadGroupTask task, Handler outHandler) {
|
||||||
|
// this.context = context;
|
||||||
|
// this.outHandler = new WeakReference<>(outHandler);
|
||||||
|
// this.wTask = new WeakReference<>(task);
|
||||||
|
// this.task = wTask.get();
|
||||||
|
// this.groupEntity = this.task.getEntity();
|
||||||
|
// final AriaManager manager = AriaManager.getInstance(context);
|
||||||
|
// isConvertSpeed = manager.getDownloadConfig().isConvertSpeed();
|
||||||
|
// for (DownloadEntity entity : groupEntity.getChild()) {
|
||||||
|
// mEntityMap.put(entity.getDownloadUrl(), entity);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override public void onPre(String url) {
|
||||||
|
// saveData(url, IEntity.STATE_PRE, -1);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override public void onPostPre(String url, long fileSize) {
|
||||||
|
// DownloadEntity entity = mEntityMap.get(url);
|
||||||
|
// if (entity != null){
|
||||||
|
// entity.setFileSize(fileSize);
|
||||||
|
// }
|
||||||
|
// saveData(url, IEntity.STATE_POST_PRE, -1);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override public void onStart(String url, long startLocation) {
|
||||||
|
// downloadEntity.setState(IEntity.STATE_RUNNING);
|
||||||
|
// sendInState2Target(ISchedulers.START);
|
||||||
|
// sendIntent(Aria.ACTION_START, startLocation);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override public void onResume(String url, long resumeLocation) {
|
||||||
|
// downloadEntity.setState(IEntity.STATE_RUNNING);
|
||||||
|
// sendInState2Target(ISchedulers.RESUME);
|
||||||
|
// sendIntent(Aria.ACTION_RESUME, resumeLocation);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override public void onProgress(String url, long currentLocation) {
|
||||||
|
// if (System.currentTimeMillis() - lastTime > INTERVAL_TIME) {
|
||||||
|
// long speed = currentLocation - lastLen;
|
||||||
|
// sendIntent.putExtra(Aria.CURRENT_LOCATION, currentLocation);
|
||||||
|
// sendIntent.putExtra(Aria.CURRENT_SPEED, speed);
|
||||||
|
// lastTime = System.currentTimeMillis();
|
||||||
|
// if (isFirst) {
|
||||||
|
// speed = 0;
|
||||||
|
// isFirst = false;
|
||||||
|
// }
|
||||||
|
// handleSpeed(speed);
|
||||||
|
// downloadEntity.setCurrentProgress(currentLocation);
|
||||||
|
// lastLen = currentLocation;
|
||||||
|
// sendInState2Target(ISchedulers.RUNNING);
|
||||||
|
// context.sendBroadcast(sendIntent);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override public void onStop(String url, long stopLocation) {
|
||||||
|
// downloadEntity.setState(task.isWait ? IEntity.STATE_WAIT : IEntity.STATE_STOP);
|
||||||
|
// handleSpeed(0);
|
||||||
|
// sendInState2Target(ISchedulers.STOP);
|
||||||
|
// sendIntent(Aria.ACTION_STOP, stopLocation);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override public void onCancel(String url) {
|
||||||
|
// downloadEntity.setState(IEntity.STATE_CANCEL);
|
||||||
|
// handleSpeed(0);
|
||||||
|
// sendInState2Target(ISchedulers.CANCEL);
|
||||||
|
// sendIntent(Aria.ACTION_CANCEL, -1);
|
||||||
|
// downloadEntity.deleteData();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override public void onComplete(String url) {
|
||||||
|
// downloadEntity.setState(IEntity.STATE_COMPLETE);
|
||||||
|
// downloadEntity.setDownloadComplete(true);
|
||||||
|
// handleSpeed(0);
|
||||||
|
// sendInState2Target(ISchedulers.COMPLETE);
|
||||||
|
// sendIntent(Aria.ACTION_COMPLETE, downloadEntity.getFileSize());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override public void onFail(String url) {
|
||||||
|
// downloadEntity.setFailNum(downloadEntity.getFailNum() + 1);
|
||||||
|
// downloadEntity.setState(IEntity.STATE_FAIL);
|
||||||
|
// handleSpeed(0);
|
||||||
|
// sendInState2Target(ISchedulers.FAIL);
|
||||||
|
// sendIntent(Aria.ACTION_FAIL, -1);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private void handleSpeed(long speed) {
|
||||||
|
// if (isConvertSpeed) {
|
||||||
|
// downloadEntity.setConvertSpeed(CommonUtil.formatFileSize(speed) + "/s");
|
||||||
|
// } else {
|
||||||
|
// downloadEntity.setSpeed(speed);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 将任务状态发送给下载器
|
||||||
|
// *
|
||||||
|
// * @param state {@link DownloadSchedulers#START}
|
||||||
|
// */
|
||||||
|
// private void sendInState2Target(int state) {
|
||||||
|
// if (outHandler.get() != null) {
|
||||||
|
// outHandler.get().obtainMessage(state, task).sendToTarget();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private void saveData(String url, int state, long location) {
|
||||||
|
// DownloadEntity child = mEntityMap.get(url);
|
||||||
|
// if (child != null) {
|
||||||
|
// child.setState(state);
|
||||||
|
// child.setDownloadComplete(state == IEntity.STATE_COMPLETE);
|
||||||
|
// child.setCurrentProgress(location);
|
||||||
|
// child.update();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public static class Builder {
|
||||||
|
// DownloadTaskEntity taskEntity;
|
||||||
|
// Handler outHandler;
|
||||||
|
// String targetName;
|
||||||
|
//
|
||||||
|
// public Builder(String targetName, DownloadTaskEntity taskEntity) {
|
||||||
|
// CheckUtil.checkTaskEntity(taskEntity);
|
||||||
|
// this.targetName = targetName;
|
||||||
|
// this.taskEntity = taskEntity;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 设置自定义Handler处理下载状态时间
|
||||||
|
// *
|
||||||
|
// * @param schedulers {@link ISchedulers}
|
||||||
|
// */
|
||||||
|
// public DownloadGroupTask.Builder setOutHandler(ISchedulers schedulers) {
|
||||||
|
// this.outHandler = new Handler(schedulers);
|
||||||
|
// return this;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public DownloadGroupTask build() {
|
||||||
|
// DownloadGroupTask task = new DownloadGroupTask(taskEntity, outHandler);
|
||||||
|
// task.setTargetName(targetName);
|
||||||
|
// taskEntity.downloadEntity.save();
|
||||||
|
// return task;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
@ -0,0 +1,28 @@ |
|||||||
|
/* |
||||||
|
* 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.download; |
||||||
|
|
||||||
|
import com.arialyy.aria.core.inf.AbsTaskEntity; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by AriaL on 2017/7/1. |
||||||
|
*/ |
||||||
|
public class DownloadGroupTaskEntity extends AbsTaskEntity<DownloadGroupEntity> { |
||||||
|
|
||||||
|
public DownloadGroupTaskEntity(DownloadGroupEntity entity) { |
||||||
|
this.entity = entity; |
||||||
|
} |
||||||
|
} |
@ -1,8 +0,0 @@ |
|||||||
package com.arialyy.aria.core.download; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by AriaL on 2017/6/27. |
|
||||||
*/ |
|
||||||
|
|
||||||
public class DownloadTaskGroup { |
|
||||||
} |
|
@ -0,0 +1,24 @@ |
|||||||
|
package com.arialyy.aria.core.download.downloader; |
||||||
|
|
||||||
|
import com.arialyy.aria.core.download.DownloadTaskEntity; |
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
/** |
||||||
|
* 子线程下载信息类 |
||||||
|
*/ |
||||||
|
final class ChildThreadConfigEntity { |
||||||
|
//线程Id
|
||||||
|
int THREAD_ID; |
||||||
|
//下载文件大小
|
||||||
|
long FILE_SIZE; |
||||||
|
//子线程启动下载位置
|
||||||
|
long START_LOCATION; |
||||||
|
//子线程结束下载位置
|
||||||
|
long END_LOCATION; |
||||||
|
//下载路径
|
||||||
|
File TEMP_FILE; |
||||||
|
String DOWNLOAD_URL; |
||||||
|
String CONFIG_FILE_PATH; |
||||||
|
DownloadTaskEntity DOWNLOAD_TASK_ENTITY; |
||||||
|
boolean IS_SUPPORT_BREAK_POINT = true; |
||||||
|
} |
@ -0,0 +1,240 @@ |
|||||||
|
/* |
||||||
|
* 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.download.downloader; |
||||||
|
|
||||||
|
import com.arialyy.aria.core.download.DownloadEntity; |
||||||
|
import com.arialyy.aria.core.download.DownloadGroupTaskEntity; |
||||||
|
import com.arialyy.aria.core.download.DownloadTaskEntity; |
||||||
|
import com.arialyy.aria.core.inf.IEntity; |
||||||
|
import com.arialyy.aria.orm.DbEntity; |
||||||
|
import java.io.File; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Set; |
||||||
|
import java.util.concurrent.ExecutorService; |
||||||
|
import java.util.concurrent.Executors; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by AriaL on 2017/6/30. |
||||||
|
* 下载组工具 |
||||||
|
*/ |
||||||
|
public class DownloadGroupUtil implements IDownloadUtil { |
||||||
|
private static final String TAG = "DownloadGroupUtil"; |
||||||
|
/** |
||||||
|
* 任务组所有任务总大小 |
||||||
|
*/ |
||||||
|
private long mTotalSize = 0; |
||||||
|
private long mCurrentProgress = 0; |
||||||
|
private ExecutorService mInfoPool; |
||||||
|
private ExecutorService mExePool; |
||||||
|
private IDownloadListener mListener; |
||||||
|
private DownloadGroupTaskEntity mTaskEntity; |
||||||
|
private boolean isRunning = true; |
||||||
|
/** |
||||||
|
* 保存所有没有下载完成的任务,key为下载地址 |
||||||
|
*/ |
||||||
|
private Map<String, DownloadTaskEntity> mExeMap = new HashMap<>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 下载失败的映射表,key为下载地址 |
||||||
|
*/ |
||||||
|
private Map<String, DownloadTaskEntity> mFailMap = new HashMap<>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 下载器映射表,key为下载地址 |
||||||
|
*/ |
||||||
|
private Map<String, DownloadThreader> mDownloaderMap = new HashMap<>(); |
||||||
|
|
||||||
|
DownloadGroupUtil(IDownloadListener listener, DownloadGroupTaskEntity taskEntity) { |
||||||
|
mListener = listener; |
||||||
|
mTaskEntity = taskEntity; |
||||||
|
mInfoPool = Executors.newCachedThreadPool(); |
||||||
|
|
||||||
|
for (DownloadEntity entity : mTaskEntity.entity.getChild()) { |
||||||
|
File file = new File(entity.getDownloadPath()); |
||||||
|
if (entity.isDownloadComplete() && file.exists()) { |
||||||
|
mTotalSize += entity.getFileSize(); |
||||||
|
} else { |
||||||
|
mExeMap.put(entity.getDownloadUrl(), createDownloadTask(entity)); |
||||||
|
} |
||||||
|
mCurrentProgress += entity.getCurrentProgress(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override public long getFileSize() { |
||||||
|
return mTotalSize; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public long getCurrentLocation() { |
||||||
|
return mCurrentProgress; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public boolean isDownloading() { |
||||||
|
return isRunning; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void cancelDownload() { |
||||||
|
isRunning = false; |
||||||
|
if (!mInfoPool.isShutdown()) { |
||||||
|
mInfoPool.shutdown(); |
||||||
|
} |
||||||
|
|
||||||
|
Set<String> keys = mDownloaderMap.keySet(); |
||||||
|
for (String key : keys) { |
||||||
|
DownloadThreader db = mDownloaderMap.get(key); |
||||||
|
if (db != null) { |
||||||
|
db.cancelDownload(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void stopDownload() { |
||||||
|
isRunning = false; |
||||||
|
if (!mInfoPool.isShutdown()) { |
||||||
|
mInfoPool.shutdown(); |
||||||
|
} |
||||||
|
|
||||||
|
Set<String> keys = mDownloaderMap.keySet(); |
||||||
|
for (String key : keys) { |
||||||
|
DownloadThreader db = mDownloaderMap.get(key); |
||||||
|
if (db != null) { |
||||||
|
db.stopDownload(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void startDownload() { |
||||||
|
isRunning = true; |
||||||
|
Set<String> keys = mExeMap.keySet(); |
||||||
|
for (String key : keys) { |
||||||
|
DownloadTaskEntity taskEntity = mExeMap.get(key); |
||||||
|
mInfoPool.execute(new FileInfoThread(taskEntity, new FileInfoThread.OnFileInfoCallback() { |
||||||
|
|
||||||
|
@Override public void onComplete(String url, int code) { |
||||||
|
DownloadTaskEntity te = mExeMap.get(url); |
||||||
|
if (te != null) { |
||||||
|
mTotalSize += te.getEntity().getFileSize(); |
||||||
|
startChildDownload(te); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onFail(String url, String errorMsg) { |
||||||
|
mFailMap.put(url, mExeMap.get(url)); |
||||||
|
} |
||||||
|
})); |
||||||
|
} |
||||||
|
mListener.onPostPre(mTotalSize); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void resumeDownload() { |
||||||
|
startDownload(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 启动子任务下载器 |
||||||
|
*/ |
||||||
|
private void startChildDownload(DownloadTaskEntity taskEntity) { |
||||||
|
ChildDownloadListener listener = new ChildDownloadListener(taskEntity); |
||||||
|
DownloadThreader dt = new DownloadThreader(listener, taskEntity); |
||||||
|
mDownloaderMap.put(taskEntity.getEntity().getDownloadUrl(), dt); |
||||||
|
mExePool.execute(dt); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建子任务下载信息 |
||||||
|
*/ |
||||||
|
private DownloadTaskEntity createDownloadTask(DownloadEntity entity) { |
||||||
|
DownloadTaskEntity taskEntity = |
||||||
|
DbEntity.findData(DownloadTaskEntity.class, "key=?", entity.getDownloadUrl()); |
||||||
|
if (taskEntity != null) { |
||||||
|
return taskEntity; |
||||||
|
} |
||||||
|
taskEntity = new DownloadTaskEntity(entity); |
||||||
|
taskEntity.headers = mTaskEntity.headers; |
||||||
|
taskEntity.requestEnum = mTaskEntity.requestEnum; |
||||||
|
taskEntity.redirectUrlKey = mTaskEntity.redirectUrlKey; |
||||||
|
taskEntity.removeFile = mTaskEntity.removeFile; |
||||||
|
return taskEntity; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 子任务事件监听 |
||||||
|
*/ |
||||||
|
private class ChildDownloadListener extends DownloadListener { |
||||||
|
|
||||||
|
DownloadTaskEntity taskEntity; |
||||||
|
DownloadEntity entity; |
||||||
|
long lastLen = 0; //上一次发送长度
|
||||||
|
long lastTime = 0; |
||||||
|
long INTERVAL_TIME = 1000; //1m更新周期
|
||||||
|
|
||||||
|
ChildDownloadListener(DownloadTaskEntity entity) { |
||||||
|
this.taskEntity = entity; |
||||||
|
this.entity = taskEntity.getEntity(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onPre() { |
||||||
|
saveData(IEntity.STATE_PRE, -1); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onPostPre(long fileSize) { |
||||||
|
entity.setFileSize(fileSize); |
||||||
|
saveData(IEntity.STATE_POST_PRE, -1); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onResume(long resumeLocation) { |
||||||
|
saveData(IEntity.STATE_POST_PRE, IEntity.STATE_RUNNING); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onStart(long startLocation) { |
||||||
|
saveData(IEntity.STATE_POST_PRE, IEntity.STATE_RUNNING); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onProgress(long currentLocation) { |
||||||
|
if (System.currentTimeMillis() - lastTime > INTERVAL_TIME) { |
||||||
|
long speed = currentLocation - lastLen; |
||||||
|
lastTime = System.currentTimeMillis(); |
||||||
|
handleSpeed(speed); |
||||||
|
entity.setCurrentProgress(currentLocation); |
||||||
|
lastLen = currentLocation; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onStop(long stopLocation) { |
||||||
|
saveData(IEntity.STATE_STOP, stopLocation); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onCancel() { |
||||||
|
saveData(IEntity.STATE_CANCEL, -1); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onComplete() { |
||||||
|
saveData(IEntity.STATE_COMPLETE, entity.getFileSize()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onFail() { |
||||||
|
entity.setFailNum(entity.getFailNum() + 1); |
||||||
|
saveData(IEntity.STATE_FAIL, -1); |
||||||
|
} |
||||||
|
|
||||||
|
private void saveData(int state, long location) { |
||||||
|
entity.setState(state); |
||||||
|
entity.setDownloadComplete(state == IEntity.STATE_COMPLETE); |
||||||
|
entity.setCurrentProgress(location); |
||||||
|
entity.update(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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.download.downloader; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.util.Log; |
||||||
|
import android.util.SparseArray; |
||||||
|
import com.arialyy.aria.core.AriaManager; |
||||||
|
import com.arialyy.aria.core.download.DownloadEntity; |
||||||
|
import com.arialyy.aria.core.download.DownloadTaskEntity; |
||||||
|
import com.arialyy.aria.orm.DbEntity; |
||||||
|
import com.arialyy.aria.util.BufferedRandomAccessFile; |
||||||
|
import com.arialyy.aria.util.CommonUtil; |
||||||
|
import java.io.File; |
||||||
|
import java.io.IOException; |
||||||
|
import java.util.Properties; |
||||||
|
import java.util.Set; |
||||||
|
import java.util.concurrent.ExecutorService; |
||||||
|
import java.util.concurrent.Executors; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by AriaL on 2017/7/1. |
||||||
|
*/ |
||||||
|
class DownloadThreader implements Runnable, IDownloadUtil { |
||||||
|
private final String TAG = "DownloadThreader"; |
||||||
|
private IDownloadListener mListener; |
||||||
|
private DownloadTaskEntity mTaskEntity; |
||||||
|
private DownloadEntity mEntity; |
||||||
|
private ExecutorService mFixedThreadPool; |
||||||
|
private File mConfigFile;//下载信息配置文件
|
||||||
|
private Context mContext; |
||||||
|
private File mTempFile; //下载的文件
|
||||||
|
private boolean isNewTask = true; |
||||||
|
private int mThreadNum, mRealThreadNum; |
||||||
|
private DownloadStateConstance mConstance; |
||||||
|
private SparseArray<Runnable> mTask = new SparseArray<>(); |
||||||
|
|
||||||
|
DownloadThreader(IDownloadListener listener, DownloadTaskEntity taskEntity) { |
||||||
|
mListener = listener; |
||||||
|
mTaskEntity = taskEntity; |
||||||
|
mEntity = mTaskEntity.getEntity(); |
||||||
|
mContext = AriaManager.APP; |
||||||
|
mConstance = new DownloadStateConstance(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void run() { |
||||||
|
checkTask(); |
||||||
|
startDownload(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public long getFileSize() { |
||||||
|
return mEntity.getFileSize(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取当前下载位置 |
||||||
|
*/ |
||||||
|
@Override public long getCurrentLocation() { |
||||||
|
return mConstance.CURRENT_LOCATION; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public boolean isDownloading() { |
||||||
|
return mConstance.isDownloading; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void cancelDownload() { |
||||||
|
mConstance.isCancel = true; |
||||||
|
mConstance.isDownloading = false; |
||||||
|
if (mFixedThreadPool != null) { |
||||||
|
mFixedThreadPool.shutdown(); |
||||||
|
} |
||||||
|
for (int i = 0; i < mThreadNum; i++) { |
||||||
|
SingleThreadTask task = (SingleThreadTask) mTask.get(i); |
||||||
|
if (task != null) { |
||||||
|
task.cancel(); |
||||||
|
} |
||||||
|
} |
||||||
|
CommonUtil.delDownloadTaskConfig(mTaskEntity.removeFile, mEntity); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void stopDownload() { |
||||||
|
mConstance.isStop = true; |
||||||
|
mConstance.isDownloading = false; |
||||||
|
if (mFixedThreadPool != null) { |
||||||
|
mFixedThreadPool.shutdown(); |
||||||
|
} |
||||||
|
for (int i = 0; i < mThreadNum; i++) { |
||||||
|
SingleThreadTask task = (SingleThreadTask) mTask.get(i); |
||||||
|
if (task != null) { |
||||||
|
task.stop(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void startDownload() { |
||||||
|
mConstance.isDownloading = true; |
||||||
|
try { |
||||||
|
if (!mTaskEntity.isSupportBP) { |
||||||
|
mThreadNum = 1; |
||||||
|
handleNoSupportBreakpointDownload(); |
||||||
|
} else { |
||||||
|
mThreadNum = |
||||||
|
isNewTask ? AriaManager.getInstance(mContext).getDownloadConfig().getThreadNum() |
||||||
|
: mRealThreadNum; |
||||||
|
mFixedThreadPool = Executors.newFixedThreadPool(mThreadNum); |
||||||
|
handleBreakpoint(); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
failDownload("下载失败【downloadUrl:" |
||||||
|
+ mEntity.getDownloadUrl() |
||||||
|
+ "】\n【filePath:" |
||||||
|
+ mEntity.getDownloadPath() |
||||||
|
+ "】\n" |
||||||
|
+ CommonUtil.getPrintException(e)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void resumeDownload() { |
||||||
|
startDownload(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 返回该下载器的 |
||||||
|
*/ |
||||||
|
public IDownloadListener getListener() { |
||||||
|
return mListener; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 检查任务是否是新任务,新任务条件: |
||||||
|
* 1、文件不存在 |
||||||
|
* 2、下载记录文件不存在 |
||||||
|
* 3、下载记录文件缺失或不匹配 |
||||||
|
* 4、数据库记录不存在 |
||||||
|
* 5、不支持断点,则是新任务 |
||||||
|
*/ |
||||||
|
private void checkTask() { |
||||||
|
if (!mTaskEntity.isSupportBP) { |
||||||
|
isNewTask = true; |
||||||
|
return; |
||||||
|
} |
||||||
|
mConfigFile = new File(mContext.getFilesDir().getPath() |
||||||
|
+ AriaManager.DOWNLOAD_TEMP_DIR |
||||||
|
+ mEntity.getFileName() |
||||||
|
+ ".properties"); |
||||||
|
mTempFile = new File(mEntity.getDownloadPath()); |
||||||
|
if (!mConfigFile.exists()) { //记录文件被删除,则重新下载
|
||||||
|
isNewTask = true; |
||||||
|
CommonUtil.createFile(mConfigFile.getPath()); |
||||||
|
} else if (!mTempFile.exists()) { |
||||||
|
isNewTask = true; |
||||||
|
} else if (DbEntity.findData(DownloadEntity.class, "downloadUrl=?", mEntity.getDownloadUrl()) |
||||||
|
== null) { |
||||||
|
isNewTask = true; |
||||||
|
} else { |
||||||
|
isNewTask = checkConfigFile(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 检查记录文件,如果是新任务返回{@code true},否则返回{@code false} |
||||||
|
*/ |
||||||
|
private boolean checkConfigFile() { |
||||||
|
Properties pro = CommonUtil.loadConfig(mConfigFile); |
||||||
|
if (pro.isEmpty()) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
Set<Object> keys = pro.keySet(); |
||||||
|
int num = 0; |
||||||
|
for (Object key : keys) { |
||||||
|
if (String.valueOf(key).contains("_record_")) { |
||||||
|
num++; |
||||||
|
} |
||||||
|
} |
||||||
|
if (num == 0) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
mRealThreadNum = num; |
||||||
|
for (int i = 0; i < mRealThreadNum; i++) { |
||||||
|
if (pro.getProperty(mTempFile.getName() + "_record_" + i) == null) { |
||||||
|
Object state = pro.getProperty(mTempFile.getName() + "_state_" + i); |
||||||
|
if (state != null && Integer.parseInt(state + "") == 1) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 恢复记录地址 |
||||||
|
* |
||||||
|
* @return true 表示下载完成 |
||||||
|
*/ |
||||||
|
private boolean resumeRecordLocation(int i, long startL, long endL) { |
||||||
|
mConstance.CURRENT_LOCATION += endL - startL; |
||||||
|
Log.d(TAG, "++++++++++ 线程_" + i + "_已经下载完成 ++++++++++"); |
||||||
|
mConstance.COMPLETE_THREAD_NUM++; |
||||||
|
mConstance.STOP_NUM++; |
||||||
|
mConstance.CANCEL_NUM++; |
||||||
|
if (mConstance.isComplete()) { |
||||||
|
if (mConfigFile.exists()) { |
||||||
|
mConfigFile.delete(); |
||||||
|
} |
||||||
|
mListener.onComplete(); |
||||||
|
mConstance.isDownloading = false; |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建单线程任务 |
||||||
|
*/ |
||||||
|
private void addSingleTask(int i, long startL, long endL, long fileLength) { |
||||||
|
ChildThreadConfigEntity entity = new ChildThreadConfigEntity(); |
||||||
|
entity.FILE_SIZE = fileLength; |
||||||
|
entity.DOWNLOAD_URL = |
||||||
|
mEntity.isRedirect() ? mEntity.getRedirectUrl() : mEntity.getDownloadUrl(); |
||||||
|
entity.TEMP_FILE = mTempFile; |
||||||
|
entity.THREAD_ID = i; |
||||||
|
entity.START_LOCATION = startL; |
||||||
|
entity.END_LOCATION = endL; |
||||||
|
entity.CONFIG_FILE_PATH = mConfigFile.getPath(); |
||||||
|
entity.IS_SUPPORT_BREAK_POINT = mTaskEntity.isSupportBP; |
||||||
|
entity.DOWNLOAD_TASK_ENTITY = mTaskEntity; |
||||||
|
mConstance.THREAD_NUM = mThreadNum; |
||||||
|
SingleThreadTask task = new SingleThreadTask(mConstance, mListener, entity); |
||||||
|
mTask.put(i, task); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 启动单线程下载任务 |
||||||
|
*/ |
||||||
|
private void startSingleTask(int[] recordL) { |
||||||
|
if (mConstance.CURRENT_LOCATION > 0) { |
||||||
|
mListener.onResume(mConstance.CURRENT_LOCATION); |
||||||
|
} else { |
||||||
|
mListener.onStart(mConstance.CURRENT_LOCATION); |
||||||
|
} |
||||||
|
mFixedThreadPool = Executors.newFixedThreadPool(recordL.length); |
||||||
|
for (int l : recordL) { |
||||||
|
if (l == -1) continue; |
||||||
|
Runnable task = mTask.get(l); |
||||||
|
if (task != null) { |
||||||
|
mFixedThreadPool.execute(task); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 处理断点 |
||||||
|
*/ |
||||||
|
private void handleBreakpoint() throws IOException { |
||||||
|
long fileLength = mEntity.getFileSize(); |
||||||
|
Properties pro = CommonUtil.loadConfig(mConfigFile); |
||||||
|
int blockSize = (int) (fileLength / mThreadNum); |
||||||
|
int[] recordL = new int[mThreadNum]; |
||||||
|
for (int i = 0; i < mThreadNum; i++) { |
||||||
|
recordL[i] = -1; |
||||||
|
} |
||||||
|
int rl = 0; |
||||||
|
if (isNewTask) { |
||||||
|
CommonUtil.createFile(mTempFile.getPath()); |
||||||
|
BufferedRandomAccessFile file = |
||||||
|
new BufferedRandomAccessFile(new File(mTempFile.getPath()), "rwd", 8192); |
||||||
|
//设置文件长度
|
||||||
|
file.setLength(fileLength); |
||||||
|
file.close(); |
||||||
|
} |
||||||
|
|
||||||
|
for (int i = 0; i < mThreadNum; i++) { |
||||||
|
long startL = i * blockSize, endL = (i + 1) * blockSize; |
||||||
|
Object state = pro.getProperty(mTempFile.getName() + "_state_" + i); |
||||||
|
if (state != null && Integer.parseInt(state + "") == 1) { //该线程已经完成
|
||||||
|
if (resumeRecordLocation(i, startL, endL)) return; |
||||||
|
continue; |
||||||
|
} |
||||||
|
//分配下载位置
|
||||||
|
Object record = pro.getProperty(mTempFile.getName() + "_record_" + i); |
||||||
|
//如果有记录,则恢复下载
|
||||||
|
if (!isNewTask && record != null && Long.parseLong(record + "") >= 0) { |
||||||
|
Long r = Long.parseLong(record + ""); |
||||||
|
mConstance.CURRENT_LOCATION += r - startL; |
||||||
|
Log.d(TAG, "任务【" + mEntity.getFileName() + "】线程__" + i + "__恢复下载"); |
||||||
|
mListener.onChildResume(r); |
||||||
|
startL = r; |
||||||
|
recordL[rl] = i; |
||||||
|
rl++; |
||||||
|
} else { |
||||||
|
recordL[rl] = i; |
||||||
|
rl++; |
||||||
|
} |
||||||
|
if (i == (mThreadNum - 1)) { |
||||||
|
//最后一个线程的结束位置即为文件的总长度
|
||||||
|
endL = fileLength; |
||||||
|
} |
||||||
|
addSingleTask(i, startL, endL, fileLength); |
||||||
|
} |
||||||
|
startSingleTask(recordL); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 处理不支持断点的下载 |
||||||
|
*/ |
||||||
|
private void handleNoSupportBreakpointDownload() { |
||||||
|
ChildThreadConfigEntity entity = new ChildThreadConfigEntity(); |
||||||
|
long len = mEntity.getFileSize(); |
||||||
|
entity.FILE_SIZE = len; |
||||||
|
entity.DOWNLOAD_URL = |
||||||
|
mEntity.isRedirect() ? mEntity.getRedirectUrl() : mEntity.getDownloadUrl(); |
||||||
|
entity.TEMP_FILE = mTempFile; |
||||||
|
entity.THREAD_ID = 0; |
||||||
|
entity.START_LOCATION = 0; |
||||||
|
entity.END_LOCATION = entity.FILE_SIZE; |
||||||
|
entity.CONFIG_FILE_PATH = mConfigFile.getPath(); |
||||||
|
entity.IS_SUPPORT_BREAK_POINT = mTaskEntity.isSupportBP; |
||||||
|
entity.DOWNLOAD_TASK_ENTITY = mTaskEntity; |
||||||
|
mConstance.THREAD_NUM = mThreadNum; |
||||||
|
SingleThreadTask task = new SingleThreadTask(mConstance, mListener, entity); |
||||||
|
mTask.put(0, task); |
||||||
|
mFixedThreadPool.execute(task); |
||||||
|
mListener.onPostPre(len); |
||||||
|
mListener.onStart(0); |
||||||
|
} |
||||||
|
|
||||||
|
private void failDownload(String errorMsg) { |
||||||
|
Log.e(TAG, errorMsg); |
||||||
|
mConstance.isDownloading = false; |
||||||
|
mListener.onFail(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,161 @@ |
|||||||
|
/* |
||||||
|
* 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.download.downloader; |
||||||
|
|
||||||
|
import android.util.Log; |
||||||
|
import com.arialyy.aria.core.AriaManager; |
||||||
|
import com.arialyy.aria.core.download.DownloadEntity; |
||||||
|
import com.arialyy.aria.core.download.DownloadTaskEntity; |
||||||
|
import com.arialyy.aria.util.CommonUtil; |
||||||
|
import java.io.IOException; |
||||||
|
import java.net.HttpURLConnection; |
||||||
|
import java.net.URL; |
||||||
|
|
||||||
|
/** |
||||||
|
* 下载文件信息获取 |
||||||
|
*/ |
||||||
|
class FileInfoThread implements Runnable { |
||||||
|
private final String TAG = "FileInfoThread"; |
||||||
|
private DownloadEntity mEntity; |
||||||
|
private DownloadTaskEntity mTaskEntity; |
||||||
|
private int mConnectTimeOut; |
||||||
|
private OnFileInfoCallback onFileInfoListener; |
||||||
|
|
||||||
|
interface OnFileInfoCallback { |
||||||
|
/** |
||||||
|
* 处理完成 |
||||||
|
* |
||||||
|
* @param code 状态码 |
||||||
|
*/ |
||||||
|
void onComplete(String url, int code); |
||||||
|
|
||||||
|
/** |
||||||
|
* 请求失败 |
||||||
|
* |
||||||
|
* @param errorMsg 错误信息 |
||||||
|
*/ |
||||||
|
void onFail(String url, String errorMsg); |
||||||
|
} |
||||||
|
|
||||||
|
FileInfoThread(DownloadTaskEntity taskEntity) { |
||||||
|
this(taskEntity, null); |
||||||
|
} |
||||||
|
|
||||||
|
FileInfoThread(DownloadTaskEntity taskEntity, OnFileInfoCallback callback) { |
||||||
|
this.mTaskEntity = taskEntity; |
||||||
|
mEntity = taskEntity.getEntity(); |
||||||
|
mConnectTimeOut = |
||||||
|
AriaManager.getInstance(AriaManager.APP).getDownloadConfig().getConnectTimeOut(); |
||||||
|
onFileInfoListener = callback; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void run() { |
||||||
|
HttpURLConnection conn = null; |
||||||
|
try { |
||||||
|
URL url = new URL(mEntity.getDownloadUrl()); |
||||||
|
conn = ConnectionHelp.handleConnection(url); |
||||||
|
conn = ConnectionHelp.setConnectParam(mTaskEntity, conn); |
||||||
|
conn.setRequestProperty("Range", "bytes=" + 0 + "-"); |
||||||
|
conn.setConnectTimeout(mConnectTimeOut); |
||||||
|
conn.connect(); |
||||||
|
handleConnect(conn); |
||||||
|
} catch (IOException e) { |
||||||
|
failDownload("下载失败【downloadUrl:" |
||||||
|
+ mEntity.getDownloadUrl() |
||||||
|
+ "】\n【filePath:" |
||||||
|
+ mEntity.getDownloadPath() |
||||||
|
+ "】\n" |
||||||
|
+ CommonUtil.getPrintException(e)); |
||||||
|
} finally { |
||||||
|
if (conn != null) { |
||||||
|
conn.disconnect(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void handleConnect(HttpURLConnection conn) throws IOException { |
||||||
|
int len = conn.getContentLength(); |
||||||
|
int code = conn.getResponseCode(); |
||||||
|
boolean isComplete = false; |
||||||
|
mTaskEntity.code = code; |
||||||
|
if (code == HttpURLConnection.HTTP_PARTIAL) { |
||||||
|
if (!checkLen(len)) return; |
||||||
|
mEntity.setFileSize(len); |
||||||
|
mTaskEntity.isSupportBP = true; |
||||||
|
isComplete = true; |
||||||
|
} else if (code == HttpURLConnection.HTTP_OK) { |
||||||
|
if (!checkLen(len)) return; |
||||||
|
mEntity.setFileSize(len); |
||||||
|
mTaskEntity.isSupportBP = false; |
||||||
|
isComplete = true; |
||||||
|
} else if (code == HttpURLConnection.HTTP_NOT_FOUND) { |
||||||
|
failDownload("任务【" + mEntity.getDownloadUrl() + "】下载失败,错误码:404"); |
||||||
|
} else if (code == HttpURLConnection.HTTP_MOVED_TEMP |
||||||
|
|| code == HttpURLConnection.HTTP_MOVED_PERM |
||||||
|
|| code == HttpURLConnection.HTTP_SEE_OTHER) { |
||||||
|
mTaskEntity.redirectUrlKey = conn.getHeaderField(mTaskEntity.redirectUrlKey); |
||||||
|
mEntity.setRedirect(true); |
||||||
|
mEntity.setRedirectUrl(mTaskEntity.redirectUrlKey); |
||||||
|
handle302Turn(conn); |
||||||
|
} else { |
||||||
|
failDownload("任务【" + mEntity.getDownloadUrl() + "】下载失败,错误码:" + code); |
||||||
|
} |
||||||
|
if (isComplete) { |
||||||
|
if (onFileInfoListener != null) { |
||||||
|
onFileInfoListener.onComplete(mEntity.getDownloadUrl(), code); |
||||||
|
} |
||||||
|
mEntity.update(); |
||||||
|
mTaskEntity.update(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 处理30x跳转 |
||||||
|
*/ |
||||||
|
private void handle302Turn(HttpURLConnection conn) throws IOException { |
||||||
|
String newUrl = conn.getHeaderField(mTaskEntity.redirectUrlKey); |
||||||
|
Log.d(TAG, "30x跳转,新url为【" + newUrl + "】"); |
||||||
|
String cookies = conn.getHeaderField("Set-Cookie"); |
||||||
|
conn = (HttpURLConnection) new URL(newUrl).openConnection(); |
||||||
|
conn = ConnectionHelp.setConnectParam(mTaskEntity, conn); |
||||||
|
conn.setRequestProperty("Cookie", cookies); |
||||||
|
conn.setRequestProperty("Range", "bytes=" + 0 + "-"); |
||||||
|
conn.setConnectTimeout(mConnectTimeOut); |
||||||
|
conn.connect(); |
||||||
|
handleConnect(conn); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 检查长度是否合法 |
||||||
|
* |
||||||
|
* @param len 从服务器获取的文件长度 |
||||||
|
* @return true, 合法 |
||||||
|
*/ |
||||||
|
private boolean checkLen(long len) { |
||||||
|
if (len < 0) { |
||||||
|
failDownload("任务【" + mEntity.getDownloadUrl() + "】下载失败,文件长度小于0"); |
||||||
|
return false; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
private void failDownload(String errorMsg) { |
||||||
|
Log.e(TAG, errorMsg); |
||||||
|
if (onFileInfoListener != null) { |
||||||
|
onFileInfoListener.onFail(mEntity.getDownloadUrl(), errorMsg); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,136 @@ |
|||||||
|
/* |
||||||
|
* 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.inf; |
||||||
|
|
||||||
|
import android.os.Parcel; |
||||||
|
import android.os.Parcelable; |
||||||
|
import com.arialyy.aria.orm.DbEntity; |
||||||
|
import com.arialyy.aria.orm.Ignore; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by AriaL on 2017/6/29. |
||||||
|
*/ |
||||||
|
public abstract class AbsEntity extends DbEntity implements IEntity, Parcelable { |
||||||
|
/** |
||||||
|
* 速度 |
||||||
|
*/ |
||||||
|
@Ignore private long speed = 0; |
||||||
|
/** |
||||||
|
* 单位转换后的速度 |
||||||
|
*/ |
||||||
|
@Ignore private String convertSpeed = "0b/s"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 扩展字段 |
||||||
|
*/ |
||||||
|
private String str = ""; |
||||||
|
/** |
||||||
|
* 文件大小 |
||||||
|
*/ |
||||||
|
private long fileSize = 1; |
||||||
|
private int state = STATE_WAIT; |
||||||
|
/** |
||||||
|
* 当前下载进度 |
||||||
|
*/ |
||||||
|
private long currentProgress = 0; |
||||||
|
/** |
||||||
|
* 完成时间 |
||||||
|
*/ |
||||||
|
private long completeTime; |
||||||
|
|
||||||
|
public long getSpeed() { |
||||||
|
return speed; |
||||||
|
} |
||||||
|
|
||||||
|
public void setSpeed(long speed) { |
||||||
|
this.speed = speed; |
||||||
|
} |
||||||
|
|
||||||
|
public String getConvertSpeed() { |
||||||
|
return convertSpeed; |
||||||
|
} |
||||||
|
|
||||||
|
public void setConvertSpeed(String convertSpeed) { |
||||||
|
this.convertSpeed = convertSpeed; |
||||||
|
} |
||||||
|
|
||||||
|
public String getStr() { |
||||||
|
return str; |
||||||
|
} |
||||||
|
|
||||||
|
public void setStr(String str) { |
||||||
|
this.str = str; |
||||||
|
} |
||||||
|
|
||||||
|
public long getFileSize() { |
||||||
|
return fileSize; |
||||||
|
} |
||||||
|
|
||||||
|
public void setFileSize(long fileSize) { |
||||||
|
this.fileSize = fileSize; |
||||||
|
} |
||||||
|
|
||||||
|
public int getState() { |
||||||
|
return state; |
||||||
|
} |
||||||
|
|
||||||
|
public void setState(int state) { |
||||||
|
this.state = state; |
||||||
|
} |
||||||
|
|
||||||
|
public long getCurrentProgress() { |
||||||
|
return currentProgress; |
||||||
|
} |
||||||
|
|
||||||
|
public void setCurrentProgress(long currentProgress) { |
||||||
|
this.currentProgress = currentProgress; |
||||||
|
} |
||||||
|
|
||||||
|
public long getCompleteTime() { |
||||||
|
return completeTime; |
||||||
|
} |
||||||
|
|
||||||
|
public void setCompleteTime(long completeTime) { |
||||||
|
this.completeTime = completeTime; |
||||||
|
} |
||||||
|
|
||||||
|
public AbsEntity() { |
||||||
|
} |
||||||
|
|
||||||
|
@Override public int describeContents() { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void writeToParcel(Parcel dest, int flags) { |
||||||
|
dest.writeLong(this.speed); |
||||||
|
dest.writeString(this.convertSpeed); |
||||||
|
dest.writeString(this.str); |
||||||
|
dest.writeLong(this.fileSize); |
||||||
|
dest.writeInt(this.state); |
||||||
|
dest.writeLong(this.currentProgress); |
||||||
|
dest.writeLong(this.completeTime); |
||||||
|
} |
||||||
|
|
||||||
|
protected AbsEntity(Parcel in) { |
||||||
|
this.speed = in.readLong(); |
||||||
|
this.convertSpeed = in.readString(); |
||||||
|
this.str = in.readString(); |
||||||
|
this.fileSize = in.readLong(); |
||||||
|
this.state = in.readInt(); |
||||||
|
this.currentProgress = in.readLong(); |
||||||
|
this.completeTime = in.readLong(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,71 @@ |
|||||||
|
/* |
||||||
|
* 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.inf; |
||||||
|
|
||||||
|
import com.arialyy.aria.util.CommonUtil; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by AriaL on 2017/6/29. |
||||||
|
*/ |
||||||
|
public abstract class AbsGroupTask<TASK_ENTITY extends AbsTaskEntity, ENTITY extends AbsGroupEntity> |
||||||
|
extends AbsTask<ENTITY> { |
||||||
|
|
||||||
|
protected TASK_ENTITY mTaskEntity; |
||||||
|
|
||||||
|
@Override public int getPercent() { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public long getFileSize() { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public String getConvertFileSize() { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public long getCurrentProgress() { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public String getConvertCurrentProgress() { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public int getState() { |
||||||
|
return mEntity.getState(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public String getExtendField() { |
||||||
|
return mEntity.getStr(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public String getKey() { |
||||||
|
return mEntity.getGroupName(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public boolean isRunning() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public ENTITY getEntity() { |
||||||
|
return mEntity; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void removeRecord() { |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,100 @@ |
|||||||
|
/* |
||||||
|
* 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.inf; |
||||||
|
|
||||||
|
import com.arialyy.aria.util.CommonUtil; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by lyy on 2017/6/3. |
||||||
|
*/ |
||||||
|
public abstract class AbsNormalTask<ENTITY extends AbsEntity> extends AbsTask<ENTITY> { |
||||||
|
|
||||||
|
private boolean isHeighestTask = false; |
||||||
|
|
||||||
|
/** |
||||||
|
* 暂停任务,并让任务处于等待状态 |
||||||
|
*/ |
||||||
|
public void stopAndWait() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 最高优先级命令,最高优先级命令有以下属性 |
||||||
|
* 1、在下载队列中,有且只有一个最高优先级任务 |
||||||
|
* 2、最高优先级任务会一直存在,直到用户手动暂停或任务完成 |
||||||
|
* 3、任务调度器不会暂停最高优先级任务 |
||||||
|
* 4、用户手动暂停或任务完成后,第二次重新执行该任务,该命令将失效 |
||||||
|
* 5、如果下载队列中已经满了,则会停止队尾的任务 |
||||||
|
* 6、把任务设置为最高优先级任务后,将自动执行任务,不需要重新调用start()启动任务 |
||||||
|
*/ |
||||||
|
public void setHighestPriority(boolean isHighestPriority) { |
||||||
|
isHeighestTask = isHighestPriority; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isHighestPriorityTask() { |
||||||
|
return isHeighestTask; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取百分比进度 |
||||||
|
* |
||||||
|
* @return 返回百分比进度,如果文件长度为0,返回0 |
||||||
|
*/ |
||||||
|
@Override public int getPercent() { |
||||||
|
if (mEntity.getFileSize() == 0) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
return (int) (mEntity.getCurrentProgress() * 100 / mEntity.getFileSize()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取文件大小 |
||||||
|
*/ |
||||||
|
@Override public long getFileSize() { |
||||||
|
return mEntity.getFileSize(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 转换单位后的文件长度 |
||||||
|
* |
||||||
|
* @return 如果文件长度为0,则返回0m,否则返回转换后的长度1b、1kb、1mb、1gb、1tb |
||||||
|
*/ |
||||||
|
@Override public String getConvertFileSize() { |
||||||
|
if (mEntity.getFileSize() == 0) { |
||||||
|
return "0mb"; |
||||||
|
} |
||||||
|
return CommonUtil.formatFileSize(mEntity.getFileSize()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取当前下载进度 |
||||||
|
*/ |
||||||
|
@Override public long getCurrentProgress() { |
||||||
|
return mEntity.getCurrentProgress(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取单位转换后的进度 |
||||||
|
* |
||||||
|
* @return 如:已经下载3mb的大小,则返回{@code 3mb} |
||||||
|
*/ |
||||||
|
@Override public String getConvertCurrentProgress() { |
||||||
|
if (mEntity.getCurrentProgress() == 0) { |
||||||
|
return "0b"; |
||||||
|
} |
||||||
|
return CommonUtil.formatFileSize(mEntity.getCurrentProgress()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
/* |
||||||
|
* 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.queue.pool; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by AriaL on 2017/6/29. |
||||||
|
* 任务组缓冲池 |
||||||
|
*/ |
||||||
|
public class GroupCachePool { |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
/* |
||||||
|
* 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.queue.pool; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by AriaL on 2017/6/29. |
||||||
|
*/ |
||||||
|
|
||||||
|
public class GroupExecutePool { |
||||||
|
} |
Loading…
Reference in new issue