Merge branch 'v_2.0'

pull/330/head
AriaLyy 8 years ago
commit aad3bd22c0
  1. 4
      .idea/modules.xml
  2. 6
      .idea/vcs.xml
  3. 2
      Aria/src/main/java/com/arialyy/aria/core/AMReceiver.java
  4. 8
      Aria/src/main/java/com/arialyy/aria/core/AMUplodReceiver.java
  5. 27
      Aria/src/main/java/com/arialyy/aria/core/Aria.java
  6. 4
      Aria/src/main/java/com/arialyy/aria/core/AriaManager.java
  7. 16
      Aria/src/main/java/com/arialyy/aria/core/RequestEnum.java
  8. 10
      Aria/src/main/java/com/arialyy/aria/core/TaskEntity.java
  9. 75
      Aria/src/main/java/com/arialyy/aria/core/task/ConnectionHelp.java
  10. 76
      Aria/src/main/java/com/arialyy/aria/core/task/DownloadStateConstance.java
  11. 395
      Aria/src/main/java/com/arialyy/aria/core/task/DownloadUtil.java
  12. 283
      Aria/src/main/java/com/arialyy/aria/core/task/SingleThreadTask.java
  13. 12
      Aria/src/main/java/com/arialyy/aria/core/task/Task.java
  14. 13
      Aria/src/main/java/com/arialyy/aria/exception/FileException.java
  15. 328
      Aria/src/main/java/com/arialyy/aria/util/BufferedRandomAccessFile.java
  16. 9
      Aria/src/main/java/com/arialyy/aria/util/CheckUtil.java
  17. 3
      README.md
  18. 3
      app/src/main/java/com/arialyy/simple/multi_task/FileListAdapter.java
  19. 66
      app/src/main/java/com/arialyy/simple/single_task/SingleTaskActivity.java

@ -2,8 +2,8 @@
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/Aria.iml" filepath="$PROJECT_DIR$/Aria.iml" />
<module fileurl="file://$PROJECT_DIR$/Aria/Aria-Aria.iml" filepath="$PROJECT_DIR$/Aria/Aria-Aria.iml" />
<module fileurl="file://$PROJECT_DIR$/Aria/Aria.iml" filepath="$PROJECT_DIR$/Aria/Aria.iml" />
<module fileurl="file://$PROJECT_DIR$/AriaPrj.iml" filepath="$PROJECT_DIR$/AriaPrj.iml" />
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
</modules>
</component>

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

@ -15,9 +15,7 @@
*/
package com.arialyy.aria.core;
import android.os.Build;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import com.arialyy.aria.core.scheduler.DownloadSchedulers;
import com.arialyy.aria.core.scheduler.OnSchedulerListener;
import com.arialyy.aria.util.CheckUtil;

@ -0,0 +1,8 @@
package com.arialyy.aria.core;
/**
* Created by Aria.Lao on 2017/1/18.
* AM 上传文件接收器
*/
public class AMUplodReceiver {
}

@ -28,6 +28,7 @@ import android.os.Build;
import android.widget.PopupWindow;
import com.arialyy.aria.core.scheduler.OnSchedulerListener;
import com.arialyy.aria.core.task.Task;
import com.arialyy.aria.util.CheckUtil;
/**
* Created by lyy on 2016/12/1.
@ -98,12 +99,12 @@ import com.arialyy.aria.core.task.Task;
private Aria() {
}
/**
* 接受ActivityServiceApplication
*/
public static AMReceiver whit(Context context) {
//if (context == null) throw new IllegalArgumentException("context 不能为 null");
checkNull(context);
CheckUtil.checkNull(context);
if (context instanceof Activity
|| context instanceof Service
|| context instanceof Application) {
@ -117,7 +118,7 @@ import com.arialyy.aria.core.task.Task;
* 处理Fragment
*/
public static AMReceiver whit(Fragment fragment) {
checkNull(fragment);
CheckUtil.checkNull(fragment);
return AriaManager.getInstance(
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? fragment.getContext()
: fragment.getActivity()).get(fragment);
@ -127,7 +128,7 @@ import com.arialyy.aria.core.task.Task;
* 处理Fragment
*/
public static AMReceiver whit(android.support.v4.app.Fragment fragment) {
checkNull(fragment);
CheckUtil.checkNull(fragment);
return AriaManager.getInstance(
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? fragment.getContext()
: fragment.getActivity()).get(fragment);
@ -137,7 +138,7 @@ import com.arialyy.aria.core.task.Task;
* 处理Fragment或者DialogFragment
*/
public static AMReceiver whit(DialogFragment dialog) {
checkNull(dialog);
CheckUtil.checkNull(dialog);
return AriaManager.getInstance(
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? dialog.getContext() : dialog.getActivity())
.get(dialog);
@ -147,7 +148,7 @@ import com.arialyy.aria.core.task.Task;
* 处理popupwindow
*/
public static AMReceiver whit(PopupWindow popupWindow) {
checkNull(popupWindow);
CheckUtil.checkNull(popupWindow);
return AriaManager.getInstance(popupWindow.getContentView().getContext()).get(popupWindow);
}
@ -155,7 +156,7 @@ import com.arialyy.aria.core.task.Task;
* 处理Dialog
*/
public static AMReceiver whit(Dialog dialog) {
checkNull(dialog);
CheckUtil.checkNull(dialog);
return AriaManager.getInstance(dialog.getContext()).get(dialog);
}
@ -177,7 +178,7 @@ import com.arialyy.aria.core.task.Task;
* 处理Dialog的通用任务
*/
public static AriaManager get(Dialog dialog) {
checkNull(dialog);
CheckUtil.checkNull(dialog);
return AriaManager.getInstance(dialog.getContext());
}
@ -185,7 +186,7 @@ import com.arialyy.aria.core.task.Task;
* 处理Dialog的通用任务
*/
public static AriaManager get(PopupWindow popupWindow) {
checkNull(popupWindow);
CheckUtil.checkNull(popupWindow);
return AriaManager.getInstance(popupWindow.getContentView().getContext());
}
@ -193,7 +194,7 @@ import com.arialyy.aria.core.task.Task;
* 处理Fragment的通用任务
*/
public static AriaManager get(Fragment fragment) {
checkNull(fragment);
CheckUtil.checkNull(fragment);
return AriaManager.getInstance(
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? fragment.getContext()
: fragment.getActivity());
@ -203,16 +204,12 @@ import com.arialyy.aria.core.task.Task;
* 处理Fragment的通用任务
*/
public static AriaManager get(android.support.v4.app.Fragment fragment) {
checkNull(fragment);
CheckUtil.checkNull(fragment);
return AriaManager.getInstance(
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? fragment.getContext()
: fragment.getActivity());
}
private static void checkNull(Object obj) {
if (obj == null) throw new IllegalArgumentException("不能传入空对象");
}
public static class SimpleSchedulerListener implements OnSchedulerListener {
@Override public void onTaskPre(Task task) {

@ -217,6 +217,10 @@ import java.util.Set;
key = clsName;
}
handlePopupWindowLift((PopupWindow) obj);
} else if (obj instanceof Service) {
key = clsName;
} else if (obj instanceof Application) {
key = clsName;
}
} else {
key = clsName;

@ -0,0 +1,16 @@
package com.arialyy.aria.core;
/**
* Created by Aria.Lao on 2017/1/23.
*/
public enum RequestEnum {
GET("GET"), POST("POST");
String name;
RequestEnum(String name) {
this.name = name;
}
}

@ -0,0 +1,10 @@
package com.arialyy.aria.core;
/**
* Created by Aria.Lao on 2017/1/23.
* 任务实体
*/
public class TaskEntity {
public DownloadEntity downloadEntity;
public RequestEnum requestEnum = RequestEnum.GET;
}

@ -0,0 +1,75 @@
/*
* 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");
conn.setRequestProperty("Connection", "Keep-Alive");
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,61 +20,42 @@ import android.content.Context;
import android.util.Log;
import android.util.SparseArray;
import com.arialyy.aria.core.DownloadEntity;
import com.arialyy.aria.util.CAConfiguration;
import com.arialyy.aria.util.BufferedRandomAccessFile;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.SSLContextUtil;
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.ProtocolException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
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.
* 下载工具类
*/
final class DownloadUtil implements IDownloadUtil, Runnable {
public class DownloadUtil implements IDownloadUtil, Runnable {
private static final String TAG = "DownloadUtil";
private static final Object LOCK = new Object();
/**
* 线程数
*/
private final int THREAD_NUM;
private final int THREAD_NUM;
//下载监听
private IDownloadListener mListener;
private int mConnectTimeOut = 5000 * 4; //连接超时时间
private int mReadTimeOut = 5000 * 20; //流读取的超时时间
/**
* 已经完成下载任务的线程数量
*/
private boolean isDownloading = false;
private boolean isStop = false;
private boolean isCancel = false;
private boolean isNewTask = true;
private IDownloadListener mListener;
private int mConnectTimeOut = 5000 * 4; //连接超时时间
private int mReadTimeOut = 5000 * 20; //流读取的超时时间
private boolean isNewTask = 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 DownloadEntity mDownloadEntity;
private Context mContext;
private DownloadEntity mDownloadEntity;
private ExecutorService mFixedThreadPool;
private File mDownloadFile; //下载的文件
private File mConfigFile;//下载信息配置文件
private File mDownloadFile; //下载的文件
private File mConfigFile;//下载信息配置文件
private SparseArray<Runnable> mTask = new SparseArray<>();
private DownloadStateConstance mConstance;
DownloadUtil(Context context, DownloadEntity entity, IDownloadListener downloadListener) {
public DownloadUtil(Context context, DownloadEntity entity, IDownloadListener downloadListener) {
this(context, entity, downloadListener, 3);
}
@ -85,6 +66,7 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
mListener = downloadListener;
THREAD_NUM = threadNum;
mFixedThreadPool = Executors.newFixedThreadPool(Integer.MAX_VALUE);
mConstance = new DownloadStateConstance(THREAD_NUM);
init();
}
@ -128,19 +110,19 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
* 获取当前下载位置
*/
@Override public long getCurrentLocation() {
return mCurrentLocation;
return mConstance.CURRENT_LOCATION;
}
@Override public boolean isDownloading() {
return isDownloading;
return mConstance.isDownloading;
}
/**
* 取消下载
*/
@Override public void cancelDownload() {
isCancel = true;
isDownloading = false;
mConstance.isCancel = true;
mConstance.isDownloading = false;
mFixedThreadPool.shutdown();
for (int i = 0; i < THREAD_NUM; i++) {
SingleThreadTask task = (SingleThreadTask) mTask.get(i);
@ -154,8 +136,8 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
* 停止下载
*/
@Override public void stopDownload() {
isStop = true;
isDownloading = false;
mConstance.isStop = true;
mConstance.isDownloading = false;
mFixedThreadPool.shutdown();
for (int i = 0; i < THREAD_NUM; i++) {
SingleThreadTask task = (SingleThreadTask) mTask.get(i);
@ -195,13 +177,7 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
* 多线程断点续传下载文件开始下载
*/
@Override public void startDownload() {
isDownloading = true;
mCurrentLocation = 0;
isStop = false;
isCancel = false;
mCancelNum = 0;
mStopNum = 0;
mFailNum = 0;
mConstance.cleanState();
mListener.onPre();
new Thread(this).start();
}
@ -212,25 +188,16 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
private void failDownload(String msg) {
Log.e(TAG, msg);
isDownloading = false;
mConstance.isDownloading = false;
stopDownload();
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() {
try {
URL url = new URL(mDownloadEntity.getDownloadUrl());
HttpURLConnection conn = handleConnection(url);
setConnectParam(conn);
HttpURLConnection conn = ConnectionHelp.handleConnection(url);
conn = ConnectionHelp.setConnectParam(conn);
conn.setRequestProperty("Range", "bytes=" + 0 + "-");
conn.setConnectTimeout(mConnectTimeOut * 4);
conn.connect();
@ -269,44 +236,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 {
//不支持断点只能单线程下载
if (!isSupportBreakpoint) {
ConfigEntity entity = new ConfigEntity();
entity.fileSize = conn.getContentLength();
entity.downloadUrl = mDownloadEntity.getDownloadUrl();
entity.tempFile = mDownloadFile;
entity.threadId = 0;
entity.startLocation = 0;
entity.endLocation = entity.fileSize;
SingleThreadTask task = new SingleThreadTask(entity);
entity.FILE_SIZE = conn.getContentLength();
entity.DOWNLOAD_URL = mDownloadEntity.getDownloadUrl();
entity.TEMP_FILE = mDownloadFile;
entity.THREAD_ID = 0;
entity.START_LOCATION = 0;
entity.END_LOCATION = entity.FILE_SIZE;
entity.CONFIG_FILE_PATH = mConfigFile.getPath();
entity.isSupportBreakpoint = isSupportBreakpoint;
SingleThreadTask task = new SingleThreadTask(mConstance, mListener, entity);
mFixedThreadPool.execute(task);
mListener.onStart(0);
return;
@ -314,7 +260,10 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
int fileLength = conn.getContentLength();
//必须建一个文件
CommonUtil.createFile(mDownloadFile.getPath());
RandomAccessFile file = new RandomAccessFile(mDownloadFile.getPath(), "rwd");
//RandomAccessFile file = new RandomAccessFile(mDownloadFile.getPath(), "rwd");
////设置文件长度
//file.setLength(fileLength);
BufferedRandomAccessFile file = new BufferedRandomAccessFile(mDownloadFile.getPath(), "rwd", 8192);
//设置文件长度
file.setLength(fileLength);
mListener.onPostPre(fileLength);
@ -345,17 +294,17 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
long startL = i * blockSize, endL = (i + 1) * blockSize;
Object state = pro.getProperty(mDownloadFile.getName() + "_state_" + i);
if (state != null && Integer.parseInt(state + "") == 1) { //该线程已经完成
mCurrentLocation += endL - startL;
mConstance.CURRENT_LOCATION += endL - startL;
Log.d(TAG, "++++++++++ 线程_" + i + "_已经下载完成 ++++++++++");
mCompleteThreadNum++;
mStopNum++;
mCancelNum++;
if (mCompleteThreadNum == THREAD_NUM) {
mConstance.COMPLETE_THREAD_NUM++;
mConstance.STOP_NUM++;
mConstance.CANCEL_NUM++;
if (mConstance.isComplete()) {
if (mConfigFile.exists()) {
mConfigFile.delete();
}
mListener.onComplete();
isDownloading = false;
mConstance.isDownloading = false;
return;
}
continue;
@ -365,7 +314,7 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
//如果有记录,则恢复下载
if (!isNewTask && record != null && Long.parseLong(record + "") > 0) {
Long r = Long.parseLong(record + "");
mCurrentLocation += r - startL;
mConstance.CURRENT_LOCATION += r - startL;
Log.d(TAG, "++++++++++ 线程_" + i + "_恢复下载 ++++++++++");
mListener.onChildResume(r);
startL = r;
@ -383,19 +332,21 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
endL = fileLength;
}
ConfigEntity entity = new ConfigEntity();
entity.fileSize = fileLength;
entity.downloadUrl = mDownloadEntity.getDownloadUrl();
entity.tempFile = mDownloadFile;
entity.threadId = i;
entity.startLocation = startL;
entity.endLocation = endL;
SingleThreadTask task = new SingleThreadTask(entity);
entity.FILE_SIZE = fileLength;
entity.DOWNLOAD_URL = mDownloadEntity.getDownloadUrl();
entity.TEMP_FILE = mDownloadFile;
entity.THREAD_ID = i;
entity.START_LOCATION = startL;
entity.END_LOCATION = endL;
entity.CONFIG_FILE_PATH = mConfigFile.getPath();
entity.isSupportBreakpoint = isSupportBreakpoint;
SingleThreadTask task = new SingleThreadTask(mConstance, mListener, entity);
mTask.put(i, task);
}
if (mCurrentLocation > 0) {
mListener.onResume(mCurrentLocation);
if (mConstance.CURRENT_LOCATION > 0) {
mListener.onResume(mConstance.CURRENT_LOCATION);
} else {
mListener.onStart(mCurrentLocation);
mListener.onStart(mConstance.CURRENT_LOCATION);
}
for (int l : recordL) {
if (l == -1) continue;
@ -409,229 +360,15 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
/**
* 子线程下载信息类
*/
private static class ConfigEntity {
final static class ConfigEntity {
//文件大小
long fileSize;
String downloadUrl;
int threadId;
long startLocation;
long endLocation;
File tempFile;
}
/**
* 单个线程的下载任务
*/
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);
}
long FILE_SIZE;
String DOWNLOAD_URL;
int THREAD_ID;
long START_LOCATION;
long END_LOCATION;
File TEMP_FILE;
boolean isSupportBreakpoint = true;
String CONFIG_FILE_PATH;
}
}

@ -0,0 +1,283 @@
/*
* 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.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import com.arialyy.aria.util.BufferedRandomAccessFile;
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";
// TODO: 2017/2/22 不能使用1024 否则最大速度不能超过3m
private static final int BUF_SIZE = 8192;
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();
//创建可设置位置的文件
BufferedRandomAccessFile file =
new BufferedRandomAccessFile(mConfigEntity.TEMP_FILE, "rwd", 8192);
//设置文件长度
file.seek(mConfigEntity.START_LOCATION);
byte[] buffer = new byte[BUF_SIZE];
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);
//mHandler.sendEmptyMessage(1);
//mHandler.post(t);
}
}
Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override public void handleMessage(Message msg) {
super.handleMessage(msg);
mListener.onProgress(mConstance.CURRENT_LOCATION);
}
};
Thread t = new Thread(new Runnable() {
@Override public void run() {
mListener.onProgress(mConstance.CURRENT_LOCATION);
}
});
//Handler handler = new Handler(){
// @Override public void handleMessage(Message msg) {
// super.handleMessage(msg);
// mListener.onProgress(mConstance.CURRENT_LOCATION);
// }
//};
Thread thread = new Thread();
/**
* 取消下载
*/
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);
}
}

@ -22,6 +22,7 @@ import android.os.Handler;
import android.util.Log;
import com.arialyy.aria.core.Aria;
import com.arialyy.aria.core.DownloadManager;
import com.arialyy.aria.core.TaskEntity;
import com.arialyy.aria.core.scheduler.DownloadSchedulers;
import com.arialyy.aria.core.scheduler.IDownloadSchedulers;
import com.arialyy.aria.core.DownloadEntity;
@ -196,20 +197,9 @@ public class Task {
return this;
}
///**
// * 设置自定义下载工具
// *
// * @param downloadUtil {@link IDownloadUtil}
// */
//public Builder setDownloadUtil(IDownloadUtil downloadUtil) {
// this.downloadUtil = downloadUtil;
// return this;
//}
public Task build() {
Task task = new Task(context, downloadEntity, outHandler);
task.setTargetName(targetName);
//Log.w(TAG, "downloadEntity hashcode ==> " + downloadEntity.hashCode());
downloadEntity.save();
return task;
}

@ -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);
}
}

@ -0,0 +1,328 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.util;
import java.io.*;
import java.util.Arrays;
//import org.apache.log4j.Logger;
/**
* A <code>BufferedRandomAccessFile</code> is like a
* <code>RandomAccessFile</code>, but it uses a private buffer so that most
* operations do not require a disk access.
* <P>
*
* Note: The operations on this class are unmonitored. Also, the correct
* functioning of the <code>RandomAccessFile</code> methods that are not
* overridden here relies on the implementation of those methods in the
* superclass.
* Author : Avinash Lakshman ( alakshman@facebook.com) Prashant Malik ( pmalik@facebook.com )
*/
public final class BufferedRandomAccessFile extends RandomAccessFile {
//private static final Logger logger_ = Logger.getLogger(BufferedRandomAccessFile.class);
static final int LogBuffSz_ = 16; // 64K buffer
public static final int BuffSz_ = (1 << LogBuffSz_);
static final long BuffMask_ = ~(((long) BuffSz_) - 1L);
/*
* This implementation is based on the buffer implementation in Modula-3's
* "Rd", "Wr", "RdClass", and "WrClass" interfaces.
*/
private boolean dirty_; // true iff unflushed bytes exist
private boolean closed_; // true iff the file is closed
private long curr_; // current position in file
private long lo_, hi_; // bounds on characters in "buff"
private byte[] buff_; // local buffer
private long maxHi_; // this.lo + this.buff.length
private boolean hitEOF_; // buffer contains last file block?
private long diskPos_; // disk position
/*
* To describe the above fields, we introduce the following abstractions for
* the file "f":
*
* len(f) the length of the file curr(f) the current position in the file
* c(f) the abstract contents of the file disk(f) the contents of f's
* backing disk file closed(f) true iff the file is closed
*
* "curr(f)" is an index in the closed interval [0, len(f)]. "c(f)" is a
* character sequence of length "len(f)". "c(f)" and "disk(f)" may differ if
* "c(f)" contains unflushed writes not reflected in "disk(f)". The flush
* operation has the effect of making "disk(f)" identical to "c(f)".
*
* A file is said to be *valid* if the following conditions hold:
*
* V1. The "closed" and "curr" fields are correct:
*
* f.closed == closed(f) f.curr == curr(f)
*
* V2. The current position is either contained in the buffer, or just past
* the buffer:
*
* f.lo <= f.curr <= f.hi
*
* V3. Any (possibly) unflushed characters are stored in "f.buff":
*
* (forall i in [f.lo, f.curr): c(f)[i] == f.buff[i - f.lo])
*
* V4. For all characters not covered by V3, c(f) and disk(f) agree:
*
* (forall i in [f.lo, len(f)): i not in [f.lo, f.curr) => c(f)[i] ==
* disk(f)[i])
*
* V5. "f.dirty" is true iff the buffer contains bytes that should be
* flushed to the file; by V3 and V4, only part of the buffer can be dirty.
*
* f.dirty == (exists i in [f.lo, f.curr): c(f)[i] != f.buff[i - f.lo])
*
* V6. this.maxHi == this.lo + this.buff.length
*
* Note that "f.buff" can be "null" in a valid file, since the range of
* characters in V3 is empty when "f.lo == f.curr".
*
* A file is said to be *ready* if the buffer contains the current position,
* i.e., when:
*
* R1. !f.closed && f.buff != null && f.lo <= f.curr && f.curr < f.hi
*
* When a file is ready, reading or writing a single byte can be performed
* by reading or writing the in-memory buffer without performing a disk
* operation.
*/
/**
* Open a new <code>BufferedRandomAccessFile</code> on <code>file</code>
* in mode <code>mode</code>, which should be "r" for reading only, or
* "rw" for reading and writing.
*/
public BufferedRandomAccessFile(File file, String mode) throws IOException {
super(file, mode);
this.init(0);
}
public BufferedRandomAccessFile(File file, String mode, int size) throws IOException {
super(file, mode);
this.init(size);
}
/**
* Open a new <code>BufferedRandomAccessFile</code> on the file named
* <code>name</code> in mode <code>mode</code>, which should be "r" for
* reading only, or "rw" for reading and writing.
*/
public BufferedRandomAccessFile(String name, String mode) throws IOException {
super(name, mode);
this.init(0);
}
public BufferedRandomAccessFile(String name, String mode, int size) throws FileNotFoundException {
super(name, mode);
this.init(size);
}
private void init(int size) {
this.dirty_ = this.closed_ = false;
this.lo_ = this.curr_ = this.hi_ = 0;
this.buff_ = (size > BuffSz_) ? new byte[size] : new byte[BuffSz_];
this.maxHi_ = (long) BuffSz_;
this.hitEOF_ = false;
this.diskPos_ = 0L;
}
public void close() throws IOException {
this.flush();
this.closed_ = true;
super.close();
}
/**
* Flush any bytes in the file's buffer that have not yet been written to
* disk. If the file was created read-only, this method is a no-op.
*/
public void flush() throws IOException {
this.flushBuffer();
}
/* Flush any dirty bytes in the buffer to disk. */
private void flushBuffer() throws IOException {
if (this.dirty_) {
if (this.diskPos_ != this.lo_) super.seek(this.lo_);
int len = (int) (this.curr_ - this.lo_);
super.write(this.buff_, 0, len);
this.diskPos_ = this.curr_;
this.dirty_ = false;
}
}
/*
* Read at most "this.buff.length" bytes into "this.buff", returning the
* number of bytes read. If the return result is less than
* "this.buff.length", then EOF was read.
*/
private int fillBuffer() throws IOException {
int cnt = 0;
int rem = this.buff_.length;
while (rem > 0) {
int n = super.read(this.buff_, cnt, rem);
if (n < 0) break;
cnt += n;
rem -= n;
}
if ((cnt < 0) && (this.hitEOF_ = (cnt < this.buff_.length))) {
// make sure buffer that wasn't read is initialized with -1
Arrays.fill(this.buff_, cnt, this.buff_.length, (byte) 0xff);
}
this.diskPos_ += cnt;
return cnt;
}
/*
* This method positions <code>this.curr</code> at position <code>pos</code>.
* If <code>pos</code> does not fall in the current buffer, it flushes the
* current buffer and loads the correct one.<p>
*
* On exit from this routine <code>this.curr == this.hi</code> iff <code>pos</code>
* is at or past the end-of-file, which can only happen if the file was
* opened in read-only mode.
*/
public void seek(long pos) throws IOException {
if (pos >= this.hi_ || pos < this.lo_) {
// seeking outside of current buffer -- flush and read
this.flushBuffer();
this.lo_ = pos & BuffMask_; // start at BuffSz boundary
this.maxHi_ = this.lo_ + (long) this.buff_.length;
if (this.diskPos_ != this.lo_) {
super.seek(this.lo_);
this.diskPos_ = this.lo_;
}
int n = this.fillBuffer();
this.hi_ = this.lo_ + (long) n;
} else {
// seeking inside current buffer -- no read required
if (pos < this.curr_) {
// if seeking backwards, we must flush to maintain V4
this.flushBuffer();
}
}
this.curr_ = pos;
}
public long getFilePointer() {
return this.curr_;
}
public long length() throws IOException {
return Math.max(this.curr_, super.length());
}
public int read() throws IOException {
if (this.curr_ >= this.hi_) {
// test for EOF
// if (this.hi < this.maxHi) return -1;
if (this.hitEOF_) return -1;
// slow path -- read another buffer
this.seek(this.curr_);
if (this.curr_ == this.hi_) return -1;
}
byte res = this.buff_[(int) (this.curr_ - this.lo_)];
this.curr_++;
return ((int) res) & 0xFF; // convert byte -> int
}
public int read(byte[] b) throws IOException {
return this.read(b, 0, b.length);
}
public int read(byte[] b, int off, int len) throws IOException {
if (this.curr_ >= this.hi_) {
// test for EOF
// if (this.hi < this.maxHi) return -1;
if (this.hitEOF_) return -1;
// slow path -- read another buffer
this.seek(this.curr_);
if (this.curr_ == this.hi_) return -1;
}
len = Math.min(len, (int) (this.hi_ - this.curr_));
int buffOff = (int) (this.curr_ - this.lo_);
System.arraycopy(this.buff_, buffOff, b, off, len);
this.curr_ += len;
return len;
}
public void write(int b) throws IOException {
if (this.curr_ >= this.hi_) {
if (this.hitEOF_ && this.hi_ < this.maxHi_) {
// at EOF -- bump "hi"
this.hi_++;
} else {
// slow path -- write current buffer; read next one
this.seek(this.curr_);
if (this.curr_ == this.hi_) {
// appending to EOF -- bump "hi"
this.hi_++;
}
}
}
this.buff_[(int) (this.curr_ - this.lo_)] = (byte) b;
this.curr_++;
this.dirty_ = true;
}
public void write(byte[] b) throws IOException {
this.write(b, 0, b.length);
}
public void write(byte[] b, int off, int len) throws IOException {
while (len > 0) {
int n = this.writeAtMost(b, off, len);
off += n;
len -= n;
this.dirty_ = true;
}
}
/*
* Write at most "len" bytes to "b" starting at position "off", and return
* the number of bytes written.
*/
private int writeAtMost(byte[] b, int off, int len) throws IOException {
if (this.curr_ >= this.hi_) {
if (this.hitEOF_ && this.hi_ < this.maxHi_) {
// at EOF -- bump "hi"
this.hi_ = this.maxHi_;
} else {
// slow path -- write current buffer; read next one
this.seek(this.curr_);
if (this.curr_ == this.hi_) {
// appending to EOF -- bump "hi"
this.hi_ = this.maxHi_;
}
}
}
len = Math.min(len, (int) (this.hi_ - this.curr_));
int buffOff = (int) (this.curr_ - this.lo_);
System.arraycopy(b, off, this.buff_, buffOff, len);
this.curr_ += len;
return len;
}
}

@ -19,6 +19,7 @@ package com.arialyy.aria.util;
import android.text.TextUtils;
import android.util.Log;
import com.arialyy.aria.core.DownloadEntity;
import com.arialyy.aria.exception.FileException;
import java.io.File;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -30,6 +31,10 @@ import java.util.regex.Pattern;
public class CheckUtil {
private static final String TAG = "CheckUtil";
public static void checkNull(Object obj) {
if (obj == null) throw new IllegalArgumentException("不能传入空对象");
}
/**
* 检查sql的expression是否合法
*/
@ -80,9 +85,9 @@ public class CheckUtil {
return false;
} else if (TextUtils.isEmpty(entity.getFileName())) {
//Log.w(TAG, "文件名不能为空");
throw new IllegalArgumentException("文件名不能为null");
throw new FileException("文件名不能为null");
} else if (TextUtils.isEmpty(entity.getDownloadPath())) {
throw new IllegalArgumentException("文件保存路径不能为null");
throw new FileException("文件保存路径不能为null");
}
String fileName = entity.getFileName();
if (fileName.contains(" ")) {

@ -17,7 +17,7 @@
## 下载
[![Download](https://api.bintray.com/packages/arialyy/maven/Aria/images/download.svg)](https://bintray.com/arialyy/maven/Aria/_latestVersion)</br>
```java
compile 'com.arialyy.aria:Aria:2.4.0'
compile 'com.arialyy.aria:Aria:2.4.1'
```
## 示例
@ -134,6 +134,7 @@ Aria支持https下载,如果你希望使用自己的ca证书,那么你需要
***
## 开发日志
+ v_2.4.1 修复下载慢的问题,修复application、service 不能使用的问题
+ v_2.4.0 支持https链接下载
+ v_2.3.8 修复数据错乱的bug、添加fragment支持
+ v_2.3.6 添加dialog、popupWindow支持

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

@ -33,6 +33,8 @@ import butterknife.Bind;
import com.arialyy.aria.core.AMTarget;
import com.arialyy.aria.core.Aria;
import com.arialyy.aria.core.DownloadEntity;
import com.arialyy.aria.core.task.DownloadUtil;
import com.arialyy.aria.core.task.IDownloadListener;
import com.arialyy.aria.core.task.Task;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.frame.util.show.L;
@ -52,7 +54,8 @@ public class SingleTaskActivity extends BaseActivity<ActivitySingleBinding> {
private static final String DOWNLOAD_URL =
//"http://kotlinlang.org/docs/kotlin-docs.pdf";
"https://atom-installer.github.com/v1.13.0/AtomSetup.exe?s=1484074138&ext=.exe";
//"https://atom-installer.github.com/v1.13.0/AtomSetup.exe?s=1484074138&ext=.exe";
"http://static.gaoshouyou.com/d/21/e8/61218d78d0e8b79df68dbc18dd484c97.apk";
@Bind(R.id.progressBar) HorizontalProgressBarWithNumber mPb;
@Bind(R.id.start) Button mStart;
@Bind(R.id.stop) Button mStop;
@ -197,6 +200,67 @@ public class SingleTaskActivity extends BaseActivity<ActivitySingleBinding> {
.setDownloadPath(Environment.getExternalStorageDirectory().getPath() + "/test.apk")
.setDownloadName("test.apk")
.start();
//DownloadEntity entity = new DownloadEntity();
//entity.setDownloadUrl(DOWNLOAD_URL);
//entity.setDownloadPath(Environment.getExternalStorageDirectory().getPath() + "/test.apk");
//entity.setFileName("test.apk");
//DownloadUtil util = new DownloadUtil(this, entity, new IDownloadListener() {
// long fileSize = 1;
// @Override public void supportBreakpoint(boolean support) {
//
// }
//
// @Override public void onCancel() {
//
// }
//
// @Override public void onFail() {
//
// }
//
// @Override public void onPre() {
//
// }
//
// @Override public void onPostPre(long fileSize) {
// this.fileSize = fileSize;
// }
//
// @Override public void onProgress(long currentLocation) {
// long current = currentLocation;
// long len = fileSize;
// if (len == 0) {
// mPb.setProgress(0);
// } else {
// mPb.setProgress((int) ((current * 100) / len));
// }
// }
//
// @Override public void onChildComplete(long finishLocation) {
//
// }
//
// @Override public void onStart(long startLocation) {
//
// }
//
// @Override public void onChildResume(long resumeLocation) {
//
// }
//
// @Override public void onResume(long resumeLocation) {
//
// }
//
// @Override public void onStop(long stopLocation) {
//
// }
//
// @Override public void onComplete() {
//
// }
//});
//util.startDownload();
}
private void stop() {

Loading…
Cancel
Save