parent
9bba3cd899
commit
235dfef19d
@ -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; |
||||||
|
} |
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue