DownloadUtil 优化

pull/330/head
AriaLyy 8 years ago
parent 9bba3cd899
commit 235dfef19d
  1. 74
      Aria/src/main/java/com/arialyy/aria/core/task/ConnectionHelp.java
  2. 76
      Aria/src/main/java/com/arialyy/aria/core/task/DownloadStateConstance.java
  3. 367
      Aria/src/main/java/com/arialyy/aria/core/task/DownloadUtil.java
  4. 251
      Aria/src/main/java/com/arialyy/aria/core/task/SingleThreadTask.java
  5. 13
      Aria/src/main/java/com/arialyy/aria/exception/FileException.java
  6. 5
      Aria/src/main/java/com/arialyy/aria/util/CheckUtil.java
  7. 3
      app/src/main/java/com/arialyy/simple/multi_task/FileListAdapter.java

@ -0,0 +1,74 @@
/*
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.arialyy.aria.core.task;
import com.arialyy.aria.util.CAConfiguration;
import com.arialyy.aria.util.SSLContextUtil;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.URL;
import java.net.URLConnection;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
/**
* Created by lyy on 2017/1/18.
* 链接帮助类
*/
class ConnectionHelp {
/**
* 处理链接
*
* @throws IOException
*/
static HttpURLConnection handleConnection(URL url) throws IOException {
HttpURLConnection conn;
URLConnection urlConn = url.openConnection();
if (urlConn instanceof HttpsURLConnection) {
conn = (HttpsURLConnection) urlConn;
SSLContext sslContext =
SSLContextUtil.getSSLContext(CAConfiguration.CA_ALIAS, CAConfiguration.CA_ALIAS);
if (sslContext == null) {
sslContext = SSLContextUtil.getDefaultSLLContext();
}
SSLSocketFactory ssf = sslContext.getSocketFactory();
((HttpsURLConnection) conn).setSSLSocketFactory(ssf);
((HttpsURLConnection) conn).setHostnameVerifier(SSLContextUtil.HOSTNAME_VERIFIER);
} else {
conn = (HttpURLConnection) urlConn;
}
return conn;
}
/**
* 设置头部参数
*
* @throws ProtocolException
*/
static HttpURLConnection setConnectParam(HttpURLConnection conn) throws ProtocolException {
conn.setRequestMethod("GET");
conn.setRequestProperty("Charset", "UTF-8");
conn.setRequestProperty("User-Agent",
"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
conn.setRequestProperty("Accept",
"image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
////用于处理Disconnect 不起作用问题
//conn.setRequestProperty("Connection", "close");
return conn;
}
}

@ -0,0 +1,76 @@
/*
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.arialyy.aria.core.task;
/**
* Created by lyy on 2017/1/18.
* 下载状态常量
*/
final class DownloadStateConstance {
int CANCEL_NUM = 0;
int STOP_NUM = 0;
int FAIL_NUM = 0;
int CONNECT_TIME_OUT = 5000 * 4; //连接超时时间
int READ_TIME_OUT = 1000 * 20; //流读取的超时时间
int COMPLETE_THREAD_NUM = 0;
int THREAD_NUM = 3;
long CURRENT_LOCATION = 0;
boolean isDownloading = false;
boolean isCancel = false;
boolean isStop = false;
DownloadStateConstance(int num) {
THREAD_NUM = num;
}
void cleanState() {
isCancel = false;
isStop = false;
isDownloading = true;
CURRENT_LOCATION = 0;
CANCEL_NUM = 0;
STOP_NUM = 0;
FAIL_NUM = 0;
}
/**
* 所有子线程是否都已经停止下载
*/
boolean isStop() {
return STOP_NUM == THREAD_NUM;
}
/**
* 所有子线程是否都已经下载失败
*/
boolean isFail() {
return FAIL_NUM == THREAD_NUM;
}
/**
* 所有子线程是否都已经完成下载
*/
boolean isComplete() {
return COMPLETE_THREAD_NUM == THREAD_NUM;
}
/**
* 所有子线程是否都已经取消下载
*/
boolean isCancel() {
return CANCEL_NUM == THREAD_NUM;
}
}

@ -20,24 +20,15 @@ import android.content.Context;
import android.util.Log; import android.util.Log;
import android.util.SparseArray; import android.util.SparseArray;
import com.arialyy.aria.core.DownloadEntity; import com.arialyy.aria.core.DownloadEntity;
import com.arialyy.aria.util.CAConfiguration;
import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.SSLContextUtil;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL; import java.net.URL;
import java.net.URLConnection;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
/** /**
* Created by lyy on 2015/8/25. * Created by lyy on 2015/8/25.
@ -45,7 +36,6 @@ import javax.net.ssl.SSLSocketFactory;
*/ */
final class DownloadUtil implements IDownloadUtil, Runnable { final class DownloadUtil implements IDownloadUtil, Runnable {
private static final String TAG = "DownloadUtil"; private static final String TAG = "DownloadUtil";
private static final Object LOCK = new Object();
/** /**
* 线程数 * 线程数
*/ */
@ -54,25 +44,15 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
private IDownloadListener mListener; private IDownloadListener mListener;
private int mConnectTimeOut = 5000 * 4; //连接超时时间 private int mConnectTimeOut = 5000 * 4; //连接超时时间
private int mReadTimeOut = 5000 * 20; //流读取的超时时间 private int mReadTimeOut = 5000 * 20; //流读取的超时时间
/**
* 已经完成下载任务的线程数量
*/
private boolean isDownloading = false;
private boolean isStop = false;
private boolean isCancel = false;
private boolean isNewTask = true; private boolean isNewTask = true;
private boolean isSupportBreakpoint = true; private boolean isSupportBreakpoint = true;
private int mCompleteThreadNum = 0;
private int mCancelNum = 0;
private long mCurrentLocation = 0;
private int mStopNum = 0;
private int mFailNum = 0;
private Context mContext; private Context mContext;
private DownloadEntity mDownloadEntity; private DownloadEntity mDownloadEntity;
private ExecutorService mFixedThreadPool; private ExecutorService mFixedThreadPool;
private File mDownloadFile; //下载的文件 private File mDownloadFile; //下载的文件
private File mConfigFile;//下载信息配置文件 private File mConfigFile;//下载信息配置文件
private SparseArray<Runnable> mTask = new SparseArray<>(); private SparseArray<Runnable> mTask = new SparseArray<>();
private DownloadStateConstance mConstance;
DownloadUtil(Context context, DownloadEntity entity, IDownloadListener downloadListener) { DownloadUtil(Context context, DownloadEntity entity, IDownloadListener downloadListener) {
this(context, entity, downloadListener, 3); this(context, entity, downloadListener, 3);
@ -85,6 +65,7 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
mListener = downloadListener; mListener = downloadListener;
THREAD_NUM = threadNum; THREAD_NUM = threadNum;
mFixedThreadPool = Executors.newFixedThreadPool(Integer.MAX_VALUE); mFixedThreadPool = Executors.newFixedThreadPool(Integer.MAX_VALUE);
mConstance = new DownloadStateConstance(THREAD_NUM);
init(); init();
} }
@ -128,19 +109,19 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
* 获取当前下载位置 * 获取当前下载位置
*/ */
@Override public long getCurrentLocation() { @Override public long getCurrentLocation() {
return mCurrentLocation; return mConstance.CURRENT_LOCATION;
} }
@Override public boolean isDownloading() { @Override public boolean isDownloading() {
return isDownloading; return mConstance.isDownloading;
} }
/** /**
* 取消下载 * 取消下载
*/ */
@Override public void cancelDownload() { @Override public void cancelDownload() {
isCancel = true; mConstance.isCancel = true;
isDownloading = false; mConstance.isDownloading = false;
mFixedThreadPool.shutdown(); mFixedThreadPool.shutdown();
for (int i = 0; i < THREAD_NUM; i++) { for (int i = 0; i < THREAD_NUM; i++) {
SingleThreadTask task = (SingleThreadTask) mTask.get(i); SingleThreadTask task = (SingleThreadTask) mTask.get(i);
@ -154,8 +135,8 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
* 停止下载 * 停止下载
*/ */
@Override public void stopDownload() { @Override public void stopDownload() {
isStop = true; mConstance.isStop = true;
isDownloading = false; mConstance.isDownloading = false;
mFixedThreadPool.shutdown(); mFixedThreadPool.shutdown();
for (int i = 0; i < THREAD_NUM; i++) { for (int i = 0; i < THREAD_NUM; i++) {
SingleThreadTask task = (SingleThreadTask) mTask.get(i); SingleThreadTask task = (SingleThreadTask) mTask.get(i);
@ -195,13 +176,7 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
* 多线程断点续传下载文件开始下载 * 多线程断点续传下载文件开始下载
*/ */
@Override public void startDownload() { @Override public void startDownload() {
isDownloading = true; mConstance.cleanState();
mCurrentLocation = 0;
isStop = false;
isCancel = false;
mCancelNum = 0;
mStopNum = 0;
mFailNum = 0;
mListener.onPre(); mListener.onPre();
new Thread(this).start(); new Thread(this).start();
} }
@ -212,25 +187,16 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
private void failDownload(String msg) { private void failDownload(String msg) {
Log.e(TAG, msg); Log.e(TAG, msg);
isDownloading = false; mConstance.isDownloading = false;
stopDownload(); stopDownload();
mListener.onFail(); mListener.onFail();
} }
private void setConnectParam(HttpURLConnection conn) throws ProtocolException {
conn.setRequestMethod("GET");
conn.setRequestProperty("Charset", "UTF-8");
conn.setRequestProperty("User-Agent",
"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
conn.setRequestProperty("Accept",
"image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
}
@Override public void run() { @Override public void run() {
try { try {
URL url = new URL(mDownloadEntity.getDownloadUrl()); URL url = new URL(mDownloadEntity.getDownloadUrl());
HttpURLConnection conn = handleConnection(url); HttpURLConnection conn = ConnectionHelp.handleConnection(url);
setConnectParam(conn); conn = ConnectionHelp.setConnectParam(conn);
conn.setRequestProperty("Range", "bytes=" + 0 + "-"); conn.setRequestProperty("Range", "bytes=" + 0 + "-");
conn.setConnectTimeout(mConnectTimeOut * 4); conn.setConnectTimeout(mConnectTimeOut * 4);
conn.connect(); conn.connect();
@ -269,44 +235,23 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
} }
} }
/**
* 处理链接
*
* @throws IOException
*/
private HttpURLConnection handleConnection(URL url) throws IOException {
HttpURLConnection conn;
URLConnection urlConn = url.openConnection();
if (urlConn instanceof HttpsURLConnection) {
conn = (HttpsURLConnection) urlConn;
SSLContext sslContext =
SSLContextUtil.getSSLContext(CAConfiguration.CA_ALIAS, CAConfiguration.CA_ALIAS);
if (sslContext == null) {
sslContext = SSLContextUtil.getDefaultSLLContext();
}
SSLSocketFactory ssf = sslContext.getSocketFactory();
((HttpsURLConnection) conn).setSSLSocketFactory(ssf);
((HttpsURLConnection) conn).setHostnameVerifier(SSLContextUtil.HOSTNAME_VERIFIER);
} else {
conn = (HttpURLConnection) urlConn;
}
return conn;
}
/** /**
* 处理断点 * 处理断点
*/ */
private void handleBreakpoint(HttpURLConnection conn) throws IOException { private void handleBreakpoint(HttpURLConnection conn) throws IOException {
//不支持断点只能单线程下载 //不支持断点只能单线程下载
if (!isSupportBreakpoint) { if (!isSupportBreakpoint) {
ConfigEntity entity = new ConfigEntity(); ConfigEntity entity = new ConfigEntity();
entity.fileSize = conn.getContentLength(); entity.FILE_SIZE = conn.getContentLength();
entity.downloadUrl = mDownloadEntity.getDownloadUrl(); entity.DOWNLOAD_URL = mDownloadEntity.getDownloadUrl();
entity.tempFile = mDownloadFile; entity.TEMP_FILE = mDownloadFile;
entity.threadId = 0; entity.THREAD_ID = 0;
entity.startLocation = 0; entity.START_LOCATION = 0;
entity.endLocation = entity.fileSize; entity.END_LOCATION = entity.FILE_SIZE;
SingleThreadTask task = new SingleThreadTask(entity); entity.CONFIG_FILE_PATH = mConfigFile.getPath();
entity.isSupportBreakpoint = isSupportBreakpoint;
SingleThreadTask task = new SingleThreadTask(mConstance, mListener, entity);
mFixedThreadPool.execute(task); mFixedThreadPool.execute(task);
mListener.onStart(0); mListener.onStart(0);
return; return;
@ -345,17 +290,17 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
long startL = i * blockSize, endL = (i + 1) * blockSize; long startL = i * blockSize, endL = (i + 1) * blockSize;
Object state = pro.getProperty(mDownloadFile.getName() + "_state_" + i); Object state = pro.getProperty(mDownloadFile.getName() + "_state_" + i);
if (state != null && Integer.parseInt(state + "") == 1) { //该线程已经完成 if (state != null && Integer.parseInt(state + "") == 1) { //该线程已经完成
mCurrentLocation += endL - startL; mConstance.CURRENT_LOCATION += endL - startL;
Log.d(TAG, "++++++++++ 线程_" + i + "_已经下载完成 ++++++++++"); Log.d(TAG, "++++++++++ 线程_" + i + "_已经下载完成 ++++++++++");
mCompleteThreadNum++; mConstance.COMPLETE_THREAD_NUM++;
mStopNum++; mConstance.STOP_NUM++;
mCancelNum++; mConstance.CANCEL_NUM++;
if (mCompleteThreadNum == THREAD_NUM) { if (mConstance.isComplete()) {
if (mConfigFile.exists()) { if (mConfigFile.exists()) {
mConfigFile.delete(); mConfigFile.delete();
} }
mListener.onComplete(); mListener.onComplete();
isDownloading = false; mConstance.isDownloading = false;
return; return;
} }
continue; continue;
@ -365,7 +310,7 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
//如果有记录,则恢复下载 //如果有记录,则恢复下载
if (!isNewTask && record != null && Long.parseLong(record + "") > 0) { if (!isNewTask && record != null && Long.parseLong(record + "") > 0) {
Long r = Long.parseLong(record + ""); Long r = Long.parseLong(record + "");
mCurrentLocation += r - startL; mConstance.CURRENT_LOCATION += r - startL;
Log.d(TAG, "++++++++++ 线程_" + i + "_恢复下载 ++++++++++"); Log.d(TAG, "++++++++++ 线程_" + i + "_恢复下载 ++++++++++");
mListener.onChildResume(r); mListener.onChildResume(r);
startL = r; startL = r;
@ -383,19 +328,21 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
endL = fileLength; endL = fileLength;
} }
ConfigEntity entity = new ConfigEntity(); ConfigEntity entity = new ConfigEntity();
entity.fileSize = fileLength; entity.FILE_SIZE = fileLength;
entity.downloadUrl = mDownloadEntity.getDownloadUrl(); entity.DOWNLOAD_URL = mDownloadEntity.getDownloadUrl();
entity.tempFile = mDownloadFile; entity.TEMP_FILE = mDownloadFile;
entity.threadId = i; entity.THREAD_ID = i;
entity.startLocation = startL; entity.START_LOCATION = startL;
entity.endLocation = endL; entity.END_LOCATION = endL;
SingleThreadTask task = new SingleThreadTask(entity); entity.CONFIG_FILE_PATH = mConfigFile.getPath();
entity.isSupportBreakpoint = isSupportBreakpoint;
SingleThreadTask task = new SingleThreadTask(mConstance, mListener, entity);
mTask.put(i, task); mTask.put(i, task);
} }
if (mCurrentLocation > 0) { if (mConstance.CURRENT_LOCATION > 0) {
mListener.onResume(mCurrentLocation); mListener.onResume(mConstance.CURRENT_LOCATION);
} else { } else {
mListener.onStart(mCurrentLocation); mListener.onStart(mConstance.CURRENT_LOCATION);
} }
for (int l : recordL) { for (int l : recordL) {
if (l == -1) continue; if (l == -1) continue;
@ -409,229 +356,15 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
/** /**
* 子线程下载信息类 * 子线程下载信息类
*/ */
private static class ConfigEntity { final static class ConfigEntity {
//文件大小 //文件大小
long fileSize; long FILE_SIZE;
String downloadUrl; String DOWNLOAD_URL;
int threadId; int THREAD_ID;
long startLocation; long START_LOCATION;
long endLocation; long END_LOCATION;
File tempFile; File TEMP_FILE;
} boolean isSupportBreakpoint = true;
String CONFIG_FILE_PATH;
/**
* 单个线程的下载任务
*/
private class SingleThreadTask implements Runnable {
private static final String TAG = "SingleThreadTask";
private ConfigEntity configEntity;
private String configFPath;
private long currentLocation = 0;
private SingleThreadTask(ConfigEntity downloadInfo) {
this.configEntity = downloadInfo;
if (isSupportBreakpoint) {
configFPath = mContext.getFilesDir().getPath()
+ "/temp/"
+ configEntity.tempFile.getName()
+ ".properties";
}
}
@Override public void run() {
HttpURLConnection conn = null;
InputStream is = null;
try {
URL url = new URL(configEntity.downloadUrl);
//conn = (HttpURLConnection) url.openConnection();
conn = handleConnection(url);
if (isSupportBreakpoint) {
Log.d(TAG, "线程_"
+ configEntity.threadId
+ "_正在下载【开始位置 : "
+ configEntity.startLocation
+ ",结束位置:"
+ configEntity.endLocation
+ "】");
//在头里面请求下载开始位置和结束位置
conn.setRequestProperty("Range",
"bytes=" + configEntity.startLocation + "-" + configEntity.endLocation);
} else {
Log.w(TAG, "该下载不支持断点,即将重新下载");
}
setConnectParam(conn);
conn.setConnectTimeout(mConnectTimeOut);
conn.setReadTimeout(mReadTimeOut); //设置读取流的等待时间,必须设置该参数
is = conn.getInputStream();
//创建可设置位置的文件
RandomAccessFile file = new RandomAccessFile(configEntity.tempFile, "rwd");
//设置每条线程写入文件的位置
file.seek(configEntity.startLocation);
byte[] buffer = new byte[1024];
int len;
//当前子线程的下载位置
currentLocation = configEntity.startLocation;
while ((len = is.read(buffer)) != -1) {
if (isCancel) {
Log.d(TAG, "++++++++++ thread_" + configEntity.threadId + "_cancel ++++++++++");
break;
}
if (isStop) {
break;
}
//把下载数据数据写入文件
file.write(buffer, 0, len);
progress(len);
}
file.close();
//close 为阻塞的,需要使用线程池来处理
is.close();
conn.disconnect();
if (isCancel) {
return;
}
//停止状态不需要删除记录文件
if (isStop) {
return;
}
//支持断点的处理
if (isSupportBreakpoint) {
Log.i(TAG, "线程【" + configEntity.threadId + "】下载完毕");
writeConfig(configEntity.tempFile.getName() + "_state_" + configEntity.threadId, 1 + "");
mListener.onChildComplete(configEntity.endLocation);
mCompleteThreadNum++;
if (mCompleteThreadNum == THREAD_NUM) {
File configFile = new File(configFPath);
if (configFile.exists()) {
configFile.delete();
}
isDownloading = false;
mListener.onComplete();
}
} else {
Log.i(TAG, "下载任务完成");
isDownloading = false;
mListener.onComplete();
}
} catch (MalformedURLException e) {
mFailNum++;
failDownload(configEntity, currentLocation, "下载链接异常", e);
} catch (IOException e) {
mFailNum++;
failDownload(configEntity, currentLocation, "下载失败【" + configEntity.downloadUrl + "】", e);
} catch (Exception e) {
mFailNum++;
failDownload(configEntity, currentLocation, "获取流失败", e);
}
}
/**
* 停止下载
*/
protected void stop() {
synchronized (LOCK) {
try {
if (isSupportBreakpoint) {
mStopNum++;
String location = String.valueOf(currentLocation);
Log.i(TAG,
"thread_" + configEntity.threadId + "_stop, stop location ==> " + currentLocation);
writeConfig(configEntity.tempFile.getName() + "_record_" + configEntity.threadId,
location);
if (mStopNum == THREAD_NUM) {
Log.d(TAG, "++++++++++++++++ onStop +++++++++++++++++");
isDownloading = false;
mListener.onStop(mCurrentLocation);
}
} else {
Log.d(TAG, "++++++++++++++++ onStop +++++++++++++++++");
isDownloading = false;
mListener.onStop(mCurrentLocation);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 下载中
*/
private void progress(long len) {
synchronized (LOCK) {
currentLocation += len;
mCurrentLocation += len;
mListener.onProgress(mCurrentLocation);
}
}
/**
* 取消下载
*/
private void cancel() {
synchronized (LOCK) {
if (isSupportBreakpoint) {
mCancelNum++;
if (mCancelNum == THREAD_NUM) {
File configFile = new File(configFPath);
if (configFile.exists()) {
configFile.delete();
}
if (configEntity.tempFile.exists()) {
configEntity.tempFile.delete();
}
Log.d(TAG, "++++++++++++++++ onCancel +++++++++++++++++");
isDownloading = false;
mListener.onCancel();
}
} else {
Log.d(TAG, "++++++++++++++++ onCancel +++++++++++++++++");
isDownloading = false;
mListener.onCancel();
}
}
}
/**
* 下载失败
*/
private void failDownload(ConfigEntity dEntity, long currentLocation, String msg,
Exception ex) {
synchronized (LOCK) {
try {
isDownloading = false;
isStop = true;
if (ex != null) {
Log.e(TAG, CommonUtil.getPrintException(ex));
}
if (isSupportBreakpoint) {
if (currentLocation != -1) {
String location = String.valueOf(currentLocation);
writeConfig(dEntity.tempFile.getName() + "_record_" + dEntity.threadId, location);
}
if (mFailNum == THREAD_NUM) {
Log.d(TAG, "++++++++++++++++ onFail +++++++++++++++++");
mListener.onFail();
}
} else {
Log.d(TAG, "++++++++++++++++ onFail +++++++++++++++++");
mListener.onFail();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 将记录写入到配置文件
*/
private void writeConfig(String key, String record) throws IOException {
File configFile = new File(configFPath);
Properties pro = CommonUtil.loadConfig(configFile);
pro.setProperty(key, record);
CommonUtil.saveConfig(configFile, pro);
}
} }
} }

@ -0,0 +1,251 @@
/*
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.arialyy.aria.core.task;
import android.util.Log;
import com.arialyy.aria.util.CommonUtil;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Properties;
/**
* Created by lyy on 2017/1/18.
* 下载线程
*/
final class SingleThreadTask implements Runnable {
private static final String TAG = "SingleThreadTask";
private DownloadUtil.ConfigEntity mConfigEntity;
private String mConfigFPath;
private long mChildCurrentLocation = 0;
private static final Object LOCK = new Object();
private IDownloadListener mListener;
private DownloadStateConstance mConstance;
SingleThreadTask(DownloadStateConstance constance, IDownloadListener listener,
DownloadUtil.ConfigEntity downloadInfo) {
mConstance = constance;
mListener = listener;
this.mConfigEntity = downloadInfo;
if (mConfigEntity.isSupportBreakpoint) {
mConfigFPath = downloadInfo.CONFIG_FILE_PATH;
}
}
@Override public void run() {
HttpURLConnection conn = null;
InputStream is = null;
try {
URL url = new URL(mConfigEntity.DOWNLOAD_URL);
//conn = (HttpURLConnection) url.openConnection();
conn = ConnectionHelp.handleConnection(url);
if (mConfigEntity.isSupportBreakpoint) {
Log.d(TAG, "线程_"
+ mConfigEntity.THREAD_ID
+ "_正在下载【开始位置 : "
+ mConfigEntity.START_LOCATION
+ ",结束位置:"
+ mConfigEntity.END_LOCATION
+ "】");
//在头里面请求下载开始位置和结束位置
conn.setRequestProperty("Range",
"bytes=" + mConfigEntity.START_LOCATION + "-" + mConfigEntity.END_LOCATION);
} else {
Log.w(TAG, "该下载不支持断点,即将重新下载");
}
conn = ConnectionHelp.setConnectParam(conn);
conn.setConnectTimeout(mConstance.CONNECT_TIME_OUT);
conn.setReadTimeout(mConstance.READ_TIME_OUT); //设置读取流的等待时间,必须设置该参数
is = conn.getInputStream();
//创建可设置位置的文件
RandomAccessFile file = new RandomAccessFile(mConfigEntity.TEMP_FILE, "rwd");
//设置每条线程写入文件的位置
file.seek(mConfigEntity.START_LOCATION);
byte[] buffer = new byte[1024];
int len;
//当前子线程的下载位置
mChildCurrentLocation = mConfigEntity.START_LOCATION;
while ((len = is.read(buffer)) != -1) {
if (mConstance.isCancel) {
break;
}
if (mConstance.isStop) {
Log.i(TAG, "stop");
break;
}
//把下载数据数据写入文件
file.write(buffer, 0, len);
progress(len);
}
file.close();
//close 为阻塞的,需要使用线程池来处理
is.close();
conn.disconnect();
if (mConstance.isCancel) {
return;
}
//停止状态不需要删除记录文件
if (mConstance.isStop) {
return;
}
//支持断点的处理
if (mConfigEntity.isSupportBreakpoint) {
Log.i(TAG, "线程【" + mConfigEntity.THREAD_ID + "】下载完毕");
writeConfig(mConfigEntity.TEMP_FILE.getName() + "_state_" + mConfigEntity.THREAD_ID,
1 + "");
mListener.onChildComplete(mConfigEntity.END_LOCATION);
mConstance.COMPLETE_THREAD_NUM++;
if (mConstance.isComplete()) {
File configFile = new File(mConfigFPath);
if (configFile.exists()) {
configFile.delete();
}
mConstance.isDownloading = false;
mListener.onComplete();
}
} else {
Log.i(TAG, "下载任务完成");
mConstance.isDownloading = false;
mListener.onComplete();
}
} catch (MalformedURLException e) {
mConstance.FAIL_NUM++;
failDownload(mConfigEntity, mChildCurrentLocation, "下载链接异常", e);
} catch (IOException e) {
mConstance.FAIL_NUM++;
failDownload(mConfigEntity, mChildCurrentLocation, "下载失败【" + mConfigEntity.DOWNLOAD_URL + "】",
e);
} catch (Exception e) {
mConstance.FAIL_NUM++;
failDownload(mConfigEntity, mChildCurrentLocation, "获取流失败", e);
}
}
/**
* 停止下载
*/
protected void stop() {
synchronized (LOCK) {
try {
if (mConfigEntity.isSupportBreakpoint) {
mConstance.STOP_NUM++;
String location = String.valueOf(mChildCurrentLocation);
Log.d(TAG, "thread_"
+ mConfigEntity.THREAD_ID
+ "_stop, stop location ==> "
+ mChildCurrentLocation);
writeConfig(mConfigEntity.TEMP_FILE.getName() + "_record_" + mConfigEntity.THREAD_ID,
location);
if (mConstance.isStop()) {
Log.d(TAG, "++++++++++++++++ onStop +++++++++++++++++");
mConstance.isDownloading = false;
mListener.onStop(mConstance.CURRENT_LOCATION);
}
} else {
Log.d(TAG, "++++++++++++++++ onStop +++++++++++++++++");
mConstance.isDownloading = false;
mListener.onStop(mConstance.CURRENT_LOCATION);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 下载中
*/
private void progress(long len) {
synchronized (LOCK) {
mChildCurrentLocation += len;
mConstance.CURRENT_LOCATION += len;
mListener.onProgress(mConstance.CURRENT_LOCATION);
}
}
/**
* 取消下载
*/
protected void cancel() {
synchronized (LOCK) {
if (mConfigEntity.isSupportBreakpoint) {
mConstance.CANCEL_NUM++;
Log.d(TAG, "++++++++++ thread_" + mConfigEntity.THREAD_ID + "_cancel ++++++++++");
if (mConstance.isCancel()) {
File configFile = new File(mConfigFPath);
if (configFile.exists()) {
configFile.delete();
}
if (mConfigEntity.TEMP_FILE.exists()) {
mConfigEntity.TEMP_FILE.delete();
}
Log.d(TAG, "++++++++++++++++ onCancel +++++++++++++++++");
mConstance.isDownloading = false;
mListener.onCancel();
}
} else {
Log.d(TAG, "++++++++++++++++ onCancel +++++++++++++++++");
mConstance.isDownloading = false;
mListener.onCancel();
}
}
}
/**
* 下载失败
*/
private void failDownload(DownloadUtil.ConfigEntity dEntity, long currentLocation, String msg,
Exception ex) {
synchronized (LOCK) {
try {
mConstance.isDownloading = false;
mConstance.isStop = true;
if (ex != null) {
Log.e(TAG, CommonUtil.getPrintException(ex));
}
if (mConfigEntity.isSupportBreakpoint) {
if (currentLocation != -1) {
String location = String.valueOf(currentLocation);
writeConfig(dEntity.TEMP_FILE.getName() + "_record_" + dEntity.THREAD_ID, location);
}
if (mConstance.isFail()) {
Log.d(TAG, "++++++++++++++++ onFail +++++++++++++++++");
mListener.onFail();
}
} else {
Log.d(TAG, "++++++++++++++++ onFail +++++++++++++++++");
mListener.onFail();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 将记录写入到配置文件
*/
private void writeConfig(String key, String record) throws IOException {
File configFile = new File(mConfigFPath);
Properties pro = CommonUtil.loadConfig(configFile);
pro.setProperty(key, record);
CommonUtil.saveConfig(configFile, pro);
}
}

@ -0,0 +1,13 @@
package com.arialyy.aria.exception;
/**
* Created by Aria.Lao on 2017/1/18.
* Aria 文件异常
*/
public class FileException extends NullPointerException {
private static final String ARIA_FILE_EXCEPTION = "Aria Exception:";
public FileException(String detailMessage) {
super(ARIA_FILE_EXCEPTION + detailMessage);
}
}

@ -19,6 +19,7 @@ package com.arialyy.aria.util;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import com.arialyy.aria.core.DownloadEntity; import com.arialyy.aria.core.DownloadEntity;
import com.arialyy.aria.exception.FileException;
import java.io.File; import java.io.File;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -80,9 +81,9 @@ public class CheckUtil {
return false; return false;
} else if (TextUtils.isEmpty(entity.getFileName())) { } else if (TextUtils.isEmpty(entity.getFileName())) {
//Log.w(TAG, "文件名不能为空"); //Log.w(TAG, "文件名不能为空");
throw new IllegalArgumentException("文件名不能为null"); throw new FileException("文件名不能为null");
} else if (TextUtils.isEmpty(entity.getDownloadPath())) { } else if (TextUtils.isEmpty(entity.getDownloadPath())) {
throw new IllegalArgumentException("文件保存路径不能为null"); throw new FileException("文件保存路径不能为null");
} }
String fileName = entity.getFileName(); String fileName = entity.getFileName();
if (fileName.contains(" ")) { if (fileName.contains(" ")) {

@ -67,7 +67,6 @@ final class FileListAdapter extends AbsRVAdapter<FileListEntity, FileListAdapter
Set<String> keys = mBtStates.keySet(); Set<String> keys = mBtStates.keySet();
for (String key : keys) { for (String key : keys) {
if (key.equals(downloadUrl)) { if (key.equals(downloadUrl)) {
Log.d(TAG, "able ==> " + able);
mBtStates.put(downloadUrl, able); mBtStates.put(downloadUrl, able);
notifyItemChanged(indexItem(downloadUrl)); notifyItemChanged(indexItem(downloadUrl));
return; return;
@ -80,7 +79,7 @@ final class FileListAdapter extends AbsRVAdapter<FileListEntity, FileListAdapter
for (String key : keys) { for (String key : keys) {
if (key.equals(url)) { if (key.equals(url)) {
int index = mPositions.get(key); int index = mPositions.get(key);
Log.d(TAG, "index ==> " + index); //Log.d(TAG, "index ==> " + index);
return index; return index;
} }
} }

Loading…
Cancel
Save